Indonesian (Bahasa Indonesia) translation by Aditia Dwiperdana (you can also view the original English article)



Pada tutorial ini kamu akan membuat sebuah game blackjack dalam SpriteKit menggunakan Swift 3. Kamu akan belajar tentang mengimplementasi fitur sentuh, membuat animasi visual, dan banyak konsep lain yang akan berguna saat membuat game dengan SpriteKit.
1. Membuat project dan mengimpor resource
Buka XCode dan pilih Create a new Xcode project atau pilih New > Project... dari menu File. Pastikan iOS terpilih dan pilih template Game.



Lalu, tentukan nama yang kamu inginkan untuk Product Name, Organization Name, dan Organization Identifier. Pastikan Language diset menjadi Swift, Game Technology diset sebagai SpriteKit, dan Devices diset dengan iPad.



Tentukan lokasi untuk menyimpan file project dan klik Create.
Mengimpor Helper Class
Download Github repo untuk project ini. Di dalamnya kamu akan temukan folder classes. Buka folder ini dan geser semua file ke folder yang sesuai dengan nama project-mu, misalnya blackjack. Pastikan Copy items if needed dan target utama dari daftar target keduanya dicentang.



Mengimpor gambar-gambar
Dalam repo GitHub tutorial ini juga ada folder bernama tutorial images. Di dalam project navigator, buka Assets.xcassets dan geser semua gambar ke siderbar. Xcode akan membuat atlas tekstur dari gambar-gambar tersebut secara otomatis.



2. Pengaturan project
Dalam project navigator ada dua file yang bisa kamu hapus (Gamescene.sks dan Actions.sks). Hapus kedua file tersebut dan pilih Move To Trash. File-file ini digunakan oleh editor scene bawaan Xcode, yang bisa digunakan untuk menyusun project secara visual. Kita akan membuat semua melalui kode, jadi file-file tersebut tidak dibutuhkan.
Buka GameViewController.swift, hapus isinya, dan ganti dengan kode berikut ini:
1 |
import UIKit |
2 |
import SpriteKit |
3 |
|
4 |
class GameViewController: UIViewController { |
5 |
|
6 |
override func viewDidLoad() { |
7 |
super.viewDidLoad() |
8 |
|
9 |
let scene = GameScene(size:CGSize(width: 768, height: 1024)) |
10 |
let skView = self.view as! SKView |
11 |
skView.showsFPS = false |
12 |
skView.showsNodeCount = false |
13 |
skView.ignoresSiblingOrder = false |
14 |
scene.scaleMode = .aspectFill |
15 |
skView.presentScene(scene) |
16 |
}
|
17 |
|
18 |
override var prefersStatusBarHidden: Bool { |
19 |
return true |
20 |
}
|
21 |
}
|
Kelas GameViewController
diturunkan dari UIViewController
dan akan memiliki SKView
sebagai view. Di dalam fungsi viewDidLoad
, kita melakukan downcast properti view
ke instance SKView
, menggunakan operator type cast as!
, dan mengatur view tersebut.
Jika kamu menjalankan project ini saat pertama kali kamu buat dari awal, kamu mungkin melihat teks di kanan bawah layar. Itu adalah kegunaan properti showsFPS
dan showsNodeCount
, menampilkan jumlah frame per detik dalam game dan jumlah SKNodes
yang terlihat di dalam scene. Kita tidak perlu informasi ini, jadi ubah nilainya menjadi false
.
Properti ignoreSiblingOrder
digunakan untuk menentukan urutan gambar dari SKNode
di dalam game. Kita mengisi properti ini false
karena kita perlu SKNodes
untuk digambarkan sesuai dengan urutan saat ditambahkan ke scene.
Terakhir, kita atur scale mode menjadi .aspectFill
, yang akan membuat konten di dalam scene untuk mengisi seluruh layar. Lalu kita perlu memanggil fungsi presentScene(_:)
pada skView
yang berfungsi menampilkan scene.
Berikutnya, hapus semua yang ada di dalam GameScene.swift dan ganti dengan kode berikut.
1 |
import SpriteKit |
2 |
import GameplayKit |
3 |
|
4 |
class GameScene: SKScene { |
5 |
|
6 |
override func didMove(to view: SKView) { |
7 |
}
|
8 |
|
9 |
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { |
10 |
}
|
11 |
}
|
Kamu sekarang bisa menjalankan project ini, akan muncul layar hitam kosong. Di langkah berikutnya kita akan tambahkan konten ke scene.
3. Variabel dan Konstanta
Masukkan kode berikut di awal kelas GameScene
, tepat di bawah baris GameScene
mewariskan kelas SKScene
.
1 |
class GameScene: SKScene { |
2 |
let moneyContainer = SKSpriteNode(color: .clear, size: CGSize(width:250, height: 150)) |
3 |
let dealBtn = SKSpriteNode(imageNamed: "deal_btn") |
4 |
let hitBtn = SKSpriteNode(imageNamed: "hit_btn") |
5 |
let standBtn = SKSpriteNode(imageNamed: "stand_btn") |
6 |
let money10 = Money(moneyValue: .ten) |
7 |
let money25 = Money(moneyValue: .twentyFive) |
8 |
let money50 = Money(moneyValue: .fifty) |
9 |
let instructionText = SKLabelNode(text: "Place your bet") |
10 |
|
11 |
Kita membuat beberapa SKSpriteNode
di sini. SKSpriteNode
digunakan untuk membuat node yang berwarna, atau biasanya menggunakan sebuah SKTexture
, yang biasanya berisi sebuah gambar. Kita menggunakan convenience initializer init(color:size:)
untuk membuat sebuah node berwarna transparan bernama moneyContainer
. moneyContainer
akan digunakan untuk menyimpan nilai uang yang ditaruhkan pemain, dan di akhir masing-masing ronde, kita akan animasikan node ini untuk bergerak menuju pemain yang memenangkan game. Menempatkan semua uang di satu node memudahkan kita untuk menganimasikan semua uang di waktu yang bersamaan.
Berikutnya, kita buat konstanta dealBtn
, hitBtn
, dan standBtn
. Sesuai dengan namanya, tombol-tombol ini digunakan dalam game untuk aksi deal, hit, dan stand. Kita menggunakan convenience initializer init(imageNamed:)
, yang menerima nama gambar tanpa ekstensi file sebagai parameter.
Laku kita buat tiga konstanta money10
, money25
, dan money50
, yang merupakan jenis Money
. Money
adalah kelas khusus turunan dari SKSpriteNode
dan akan membuat salah satu jenis uang tergantung dari tipe moneyValue
yang dilempar sebagai parameter. Parameter moneyValue
adalah tipe dari MoneyValue
, yang berupa enum
. Lihatlah kelas Money
di GitHub repo project ini untuk melihat bagaimana semua ini berkerja.
Terakhir kita buat sebuah SKLabelNode
menggunakan convenience initializer init(text:)
yang menerima parameter teks yang ingin ditampilkan pada label.
Mengimplementasi setupTable
Tambahkan kode barikut di bawah fungsi didMove(to:)
.
1 |
func setupTable(){ |
2 |
let table = SKSpriteNode(imageNamed: "table") |
3 |
addChild(table) |
4 |
table.position = CGPoint(x: size.width/2, y: size.height/2) |
5 |
table.zPosition = -1 |
6 |
addChild(moneyContainer) |
7 |
moneyContainer.anchorPoint = CGPoint(x:0, y:0) |
8 |
moneyContainer.position = CGPoint(x:size.width/2 - 125, y:size.height/2) |
9 |
instructionText.fontColor = UIColor.black |
10 |
addChild(instructionText) |
11 |
instructionText.position = CGPoint(x: size.width/2, y: 400) |
12 |
}
|
13 |
Di sini kita menginisialisasi konstanta table
dan menambahkannya ke scene menggunakan addChild(_:)
yang menerima parameter node yang akan ditambahkan ke scene. Kita atur nilai position
dari table
di dalam scene dan mengubah nilai zPosition
menjadi -1
. Properti zPosition
mengatur urutan node digambar. Benda dengan nilai terkecil akan digambar pertama, dan benda lain akan digambarkan berurutan sesuai nilainya. Karena kita perlu table
ada di bawah semua benda lain, kita atur nilai zPosition
ke -1
. Hal ini memastikan benda itu digambar sebelum node apapun.
Kita juga perlu tambahkan moneyContainer
dan instructionText
ke scene. Kita atur nilai fontColor
dari instructionText
menjadi hitam (awalnya putih).
Update didMove(to:)
menjadi berikut ini.
1 |
override func didMove(to view: SKView) { |
2 |
setupTable() |
3 |
}
|
Fungsi didMove(to:)
dipanggil tepat setelah scene ditampilkan oleh view. Umumnya, disinilah kamu melakukan pengaturan scene dan membuat asset-asset yang dibutuhkan. Jika kamu coba mainkan sekarang, kamu bisa melihat table
dan instructionText
sudah ditambahkan ke scene. moneyContainer
juga ada di sana tapi tidak bisa kamu lihat karena dibuat transparan.
5. Mengimplementasi setupMoney
Tambahkan kode berikut di bawah fungsi setupTable
.
1 |
func setupMoney(){ |
2 |
addChild(money10) |
3 |
money10.position = CGPoint(x: 75, y: 40) |
4 |
|
5 |
addChild(money25) |
6 |
money25.position = CGPoint(x:130, y:40) |
7 |
|
8 |
addChild(money50) |
9 |
money50.position = CGPoint(x: 185, y:40) |
10 |
}
|
Kita menambahkan instance dari uang dan mengatur posisinya. Panggil fungsi ini di dalam didMove(to:)
.
1 |
override func didMove(to view: SKView) { |
2 |
setupTable() |
3 |
setupMoney() |
4 |
}
|
5 |
6. Mengimplementasi setupButtons
Tambahkan kdoe berikut di bawah fungsi setupMoney
yang sudah dibuat di langkah sebelumnya.
1 |
func setupButtons(){ |
2 |
dealBtn.name = "dealBtn" |
3 |
addChild(dealBtn) |
4 |
dealBtn.position = CGPoint(x:300, y:40) |
5 |
|
6 |
hitBtn.name = "hitBtn" |
7 |
addChild(hitBtn) |
8 |
hitBtn.position = CGPoint(x:450, y:40) |
9 |
hitBtn.isHidden = true |
10 |
|
11 |
standBtn.name = "standBtn" |
12 |
addChild(standBtn) |
13 |
standBtn.position = CGPoint(x:600, y:40) |
14 |
standBtn.isHidden = true |
15 |
}
|
Seperti yang kita lakukan dengan objek uang di langkah sebelumnya, kita menambahkan tombol dan mengatur posisinya. Di sini kita menggunakan properti name
agar bisa membedakan setiap tombol melalui kode. Kita juga mengatur hitBtn
dan standBtn
menjadi tersembunyi atau tidak terlihat, dengan mengatur properti isHidden
menjadi true
.
Sekarang panggil fungsi ini di dalam didMove(to:)
.
1 |
override func didMove(to view: SKView) { |
2 |
setupTable() |
3 |
setupMoney() |
4 |
setupButtons() |
5 |
}
|
Jika kamu coba jalankan game ini, kamu akan melihat objek uang dan tombol sudah ditambahkan ke dalam scene.
7. Mengimplementasi touchesBegan
Kita perlu mengimplementasi fungsi touchesBegan(_:with:)
untuk mengetahui saat sebuah objek dalam scene disentuh. Fungsi ini dipanggil saat satu atau lebih jari menyentuh layar. Tambahkan kode berikut di dalam touchesBegan
.
1 |
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { |
2 |
guard let touch = touches.first else { |
3 |
return
|
4 |
}
|
5 |
|
6 |
let touchLocation = touch.location(in: self) |
7 |
let touchedNode = self.atPoint(touchLocation) |
8 |
|
9 |
if(touchedNode.name == "money"){ |
10 |
let money = touchedNode as! Money |
11 |
bet(betAmount: money.getValue()) |
12 |
}
|
13 |
}
|
Properti multiTouchEnabled
dari view scene kita awalnya di set sebagai false
, artinya vire hanya bisa menerima sentuhan pertama dari rangkaian sentuhan. Dengan properti ini dinonaktifkan, kita bisa mendapat sentuhan dengan menggunakan properti first
dari set touch karena hanya ada satu objek di set tersebut.
Kita bisa mendapat touchLocation
dalam scene dengan menggunakan properti location
dari sentuhan. Lalu kita bisa menentukan node mana yang disentuh dengan memanggil atPoint(_:)
dan melempar touchLocation
sebagai parameter.
Kita periksa jika nama properti touchNode
adalah "money", dan jika iya maka kita tahu pemain menyentuh salah satu dari tiga objek uang. Kita menginisialisasi konstanta money
dengan men-downcast touchedNode
ke Money
, lalu kita panggil fungsi bet
yang memanggil fungsi getValue()
di konstanta money
.
8. Mengimplementasi bet
Masukkan kode berikut di bawah fungsi setupButtons
yang kamu buat di langkah sebelumnya.
1 |
func bet(betAmount: MoneyValue ){ |
2 |
if(betAmount.rawValue > player1.bank.getBalance()){ |
3 |
print("Trying to bet more than have"); |
4 |
return
|
5 |
}else{ |
6 |
pot.addMoney(amount: betAmount.rawValue) |
7 |
let tempMoney = Money(moneyValue: betAmount) |
8 |
tempMoney.anchorPoint = CGPoint(x:0, y:0) |
9 |
moneyContainer.addChild(tempMoney) |
10 |
tempMoney.position = CGPoint(x:CGFloat(arc4random_uniform(UInt32(moneyContainer.size.width - tempMoney.size.width))), y:CGFloat(arc4random_uniform(UInt32(moneyContainer.size.height - tempMoney.size.height)))) |
11 |
dealBtn.isHidden = false; |
12 |
}
|
13 |
}
|
Pertama kita pastikan pemain tidak mencoba untuk menaruhkan uang lebih banyak dari yang mereka punya, dan jika mereka melakukannya, kita langsung keluar dari fungsi. Jika tidak, kita tambahkan betAmount
ke pot
, buat konstanta tempMoney
, atur anchorPoint
ke (0,0)
, dan tambahkan ke moneyContainer
. Lalu kita bisa atur position
dan sembunyikan dealBtn
dengan mengatur properti isHidden
ke false.
SKSpriteNode
memiliki properti anchorPoint
yang awalnya bernilai (0.5,0.5)
. Sistem koordinat SpriteKit menyimpan (0,0)
di kiri bawah dan (1,1)
di kanan atas. Kamu bisa mengubah properti ini jika kamu mau memutar SKSpriteNode
dan ingin pusat rotasinya di titik yang berbeda. Contohnya, jika kamu mengubah properti anchorPoint
ke (0,0)
maka SKSpriteNode
akan berputar di pojok kiri bawah. Kamu akan mengubah properti ini untuk membantu mengatur posisi objek, seperti kode di bawah ini.
Kita perlu membuat instance dari kelas Pot
dan Player
agar kode ini bekerja. Tambahkan kode berikut bersama dengan konstanta dan variabel lain.
1 |
let pot = Pot() |
2 |
let player1 = Player(hand: Hand(),bank: Bank()) |
Jika kamu menjalankan game ini sekarang kamu sudah bisa menyentuh objek uang manapun dan menambahkannya ke moneyContainer
.
9. Mengimplementasi deal
Tambahkan kode berikut bersama kontanta dan variabel lain.
1 |
let dealer = Dealer(hand: Hand()) |
2 |
var allCards = [Card]() |
3 |
let dealerCardsY = 930 // Y position of dealer cards |
4 |
let playerCardsY = 200 // Y position of player cards |
5 |
var currentPlayerType:GenericPlayer = Player(hand: Hand(),bank: Bank()) |
6 |
let deck = Deck() |
Array allCards
akan digunakan untuk menyimpan semua kartu dalam game. Ini akan memudahkan kita memproses mereka dan menghilangkan mereka dari scene sekaligus. Konstanta dealerCardsY
dan playerCardsY
adalah posisi kartu di sumbu y. Ini akan membantu kita saat menempatkan kartu baru. currentPlayerType
digunakan untuk menunjukkan siapa yang akan mengambil kartu berikutnya. Nilainya antara dealer
atau player1
.
Tambahkan kode berikut di dalam didMove(to:)
.
1 |
override func didMove(to view: SKView) { |
2 |
setupTable() |
3 |
setupMoney() |
4 |
setupButtons() |
5 |
currentPlayerType = player1 |
6 |
}
|
Pada kode sebelumnya, kita menginisialisasi currentPlayerType
ke instance dari kelas Player
tanpa nama. Di sini kita memberinya nama player1
.
Kita perlu membuat satu deck kartu baru sebelum kita implementasi fungsi deal. Masukkan kode berikut dalam setupTable
.
1 |
func setupTable(){ |
2 |
let table = SKSpriteNode(imageNamed: "table") |
3 |
addChild(table) |
4 |
table.position = CGPoint(x: size.width/2, y: size.height/2) |
5 |
table.zPosition = -1 |
6 |
addChild(moneyContainer) |
7 |
moneyContainer.anchorPoint = CGPoint(x:0, y:0) |
8 |
moneyContainer.position = CGPoint(x:size.width/2 - 125, y:size.height/2) |
9 |
instructionText.fontColor = UIColor.black |
10 |
addChild(instructionText) |
11 |
instructionText.position = CGPoint(x: size.width/2, y: 400) |
12 |
deck.new() |
13 |
} |
Sekarang kita bisa mengimplementasi fungsi deal. Tambahkan kode berikut di bawah fungsi bet
.
1 |
func deal() { |
2 |
instructionText.text = "" |
3 |
money10.isHidden = true; |
4 |
money25.isHidden = true; |
5 |
money50.isHidden = true; |
6 |
dealBtn.isHidden = true; |
7 |
standBtn.isHidden = false |
8 |
hitBtn.isHidden = false |
9 |
let tempCard = Card(suit: "card_front", value: 0) |
10 |
tempCard.position = CGPoint(x:630, y:980) |
11 |
addChild(tempCard) |
12 |
tempCard.zPosition = 100 |
13 |
|
14 |
let newCard = deck.getTopCard() |
15 |
var whichPosition = playerCardsY |
16 |
var whichHand = player1.hand |
17 |
if(self.currentPlayerType is Player){ |
18 |
whichHand = player1.hand |
19 |
whichPosition = playerCardsY; |
20 |
} else { |
21 |
whichHand = dealer.hand |
22 |
whichPosition = dealerCardsY; |
23 |
}
|
24 |
|
25 |
whichHand.addCard(card: newCard) |
26 |
let xPos = 50 + (whichHand.getLength()*35) |
27 |
let moveCard = SKAction.move(to: CGPoint(x:xPos, y: whichPosition),duration: 1.0) |
28 |
tempCard.run(moveCard, completion: { [unowned self] in |
29 |
self.player1.setCanBet(canBet: true) |
30 |
if(self.currentPlayerType is Dealer && self.dealer.hand.getLength() == 1){ |
31 |
self.dealer.setFirstCard(card: newCard) |
32 |
self.allCards.append(tempCard) |
33 |
tempCard.zPosition = 0 |
34 |
} else { |
35 |
tempCard.removeFromParent() |
36 |
self.allCards.append(newCard) |
37 |
self.addChild(newCard) |
38 |
newCard.position = CGPoint( x: xPos, y: whichPosition) |
39 |
newCard.zPosition = 100 |
40 |
}
|
41 |
if(self.dealer.hand.getLength() < 2){ |
42 |
if(self.currentPlayerType is Player){ |
43 |
self.currentPlayerType = self.dealer |
44 |
}else{ |
45 |
self.currentPlayerType = self.player1 |
46 |
}
|
47 |
self.deal() |
48 |
}else if (self.dealer.hand.getLength() == 2 && self.player1.hand.getLength() == 2) { |
49 |
if(self.player1.hand.getValue() == 21 || self.dealer.hand.getValue() == 21){ |
50 |
self.doGameOver(hasBlackJack: true) |
51 |
} else { |
52 |
self.standBtn.isHidden = false; |
53 |
self.hitBtn.isHidden = false; |
54 |
}
|
55 |
}
|
56 |
|
57 |
if(self.dealer.hand.getLength() >= 3 && self.dealer.hand.getValue() < 17){ |
58 |
self.deal(); |
59 |
} else if(self.player1.isYeilding() && self.dealer.hand.getValue() >= 17){ |
60 |
self.standBtn.isHidden = true |
61 |
self.hitBtn.isHidden = true |
62 |
self.doGameOver(hasBlackJack: false) |
63 |
}
|
64 |
if(self.player1.hand.getValue() > 21){ |
65 |
self.standBtn.isHidden = true; |
66 |
self.hitBtn.isHidden = true; |
67 |
self.doGameOver(hasBlackJack: false); |
68 |
}
|
69 |
|
70 |
})
|
71 |
}
|
Fungsi ini cukup besar, tapi hal itu diperlukan untuk mengimplementasi logika membagi kartu. Kita periksa langkah per langkah. Kita menginisialisasi konstanta tempCard
sebagai instance dari Card
, atur posisinya, dan menambahkannya ke scene. Kita ingin kartu ini digambar dengan zPosition
lebih besar dari 0
, karena kartu pertama dealer perlu ada pada 0
. Kita isi dengan suatu nilai, misalnya 100
. Kita juga perlu membuat konstanta newCard
dengan memanggil fungsi getTopCard()
pada deck
.
Berikutnya, kita membuat dua variabel, whichPosition
dan whichHand
, lalu menjalankan beberapa logika untuk menentukan nilai akhirnya. Lalu tambahkan newCard
ke tangan yang sesuai (antara tangan pemain atau dealer). Konstanta xPos
menentukan posisi akhir kartu saat selesai beranimasi.
Kelas SKAction
memiliki beberapa fungsi yang bisa dipanggil untuk mengubah properti node seperti posisi, skala, dan rotasinya. Di sini kita panggil fungsi move(to:duration:)
, yang akan menggerakkan node dari satu posisi ke posisi lain. Tapi, untuk menjalankan SKAction
, kamu perlu memanggil fungsi run(_:)
dari node dan melempar SKAction
sebagai parameter. Di sini, kita memanggil fungsi run(_:completion:)
, yang menyebabkan kode pada bagian closure dijalankan setelah aksi selesai dijalankan.
Setelah aksi tersebut dijalankan, kita akan membolehkan pemain untuk bertaruh dengan memanggil fungsi setCanBet(canBet:)
pada instance player1
. Lalu kita perlu periksa apakah currentPlayerType
adalah instance dari Dealer
, dan periksa bahwa dealer
hanya punya satu kartu dengan memanggil hand.getLength()
. Jika benar, kita atur kartu pertama dealer
, yang akan kita butuhkan di akhir game.
Karena kartu pertama dealer
akan selalu tertutup sampai akhir permainan, kita perlu referensi ke kartu tersebut agar di akhir kita bisa menampilkannya. Tambahkan kartu ini ke array allCards
agar nanti kita bisa menghapusnya, lalu atur propert zPosition
menjadi 0
karena kita perlu kartu ini di bawah semua kartu lain. (Ingat kartu lain memiliki z-position 100
).
Jika currentPlayerType
bukan instance dari Dealer
, dan jumlah kartu di tangan bukan 1
, kita hilangkan tempCard
dan simpan newCard
di posisi yang sama, pastikan zPosition
bernilai 100
.
Menurut aturan blackjack, dealer dan pemain mendapat dua kartu untuk memulai game. Di sini kita memeriksa apa tipe currentPlayerType
dan mengubahnya jadi lawannya. Karena dealer memiliki kartu kurang dari dua, kita panggil fungsi deal
lagi. Jika tidak, kita periksa apakah dealer
dan player1
memiliki dua kartu, jika iya, kita periksa lagi apakah kartu memiliki nilai total 21, yaitu kondisi untuk menang. Jika salah satu tangan mendapat 21 maka game akan berakhir karena salah satu sudah mendapatkan blackjack. Jika tidak ada yang mendapat 21, maka kita menampilkan standBtn
dan hitBtn
dan game akan dilanjutkan.
Aturan blackjack menyebutkan bahwa dealer
harus memilih stand jika mendapat 17 atau lebih. Beberapa baris kode berikut memeriksa apakah nilai tangan dealer
lebih kecil dari 17 dan memanggil fungsi deal
. Jika nilainya 17 atau lebih, maka game akan berakhir. Terakhir, jika nilai tangan player1
lebih dari 21 maka game akan berakhir karena mereka kalah.
Banyak sekali logika yang harus dipahami. Jika ada yang tidak jelas, baca kembali perlahan sampai kamu mengerti.
Berikutnya, kita perlu mengimplementasi fungsi gameover
.
Kita perlu tahu saat pemain menekan tombol deal. Tambahkan kode berikut pada fungsi touchesBegan(_:with:)
.
1 |
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { |
2 |
guard let touch = touches.first else { |
3 |
return
|
4 |
}
|
5 |
|
6 |
let touchLocation = touch.location(in: self) |
7 |
let touchedNode = self.atPoint(touchLocation) |
8 |
|
9 |
if(touchedNode.name == "money"){ |
10 |
let money = touchedNode as! Money |
11 |
bet(betAmount: money.getValue()) |
12 |
}
|
13 |
|
14 |
if(touchedNode.name == "dealBtn"){ |
15 |
deal() |
16 |
}
|
17 |
}
|
10. Mengimplementasi doGameOver
Berikutnya, tambahkan kode berikut di bawah fungsi deal
yang sudah dibuat di langkah sebelumnya.
1 |
func doGameOver(hasBlackJack: Bool){ |
2 |
hitBtn.isHidden = true |
3 |
standBtn.isHidden = true |
4 |
let tempCardX = allCards[1].position.x |
5 |
let tempCardY = allCards[1].position.y |
6 |
let tempCard = dealer.getFirstCard() |
7 |
addChild(tempCard) |
8 |
allCards.append(tempCard) |
9 |
tempCard.position = CGPoint(x:tempCardX,y:tempCardY) |
10 |
tempCard.zPosition = 0 |
11 |
var winner:GenericPlayer = player1 |
12 |
|
13 |
if(hasBlackJack){ |
14 |
if(player1.hand.getValue() > dealer.hand.getValue()){ |
15 |
//Add to players Bank Here (pot value * 1.5)
|
16 |
instructionText.text = "You Got BlackJack!"; |
17 |
moveMoneyContainer(position: playerCardsY) |
18 |
}else{ |
19 |
//Subtract from players bank here
|
20 |
instructionText.text = "Dealer got BlackJack!"; |
21 |
moveMoneyContainer(position: dealerCardsY) |
22 |
}
|
23 |
return
|
24 |
}
|
25 |
|
26 |
if (player1.hand.getValue() > 21){ |
27 |
instructionText.text = "You Busted!" |
28 |
//Subtract from players bank
|
29 |
winner = dealer |
30 |
}else if (dealer.hand.getValue() > 21){ |
31 |
//Add to players bank
|
32 |
instructionText.text = "Dealer Busts. You Win!" |
33 |
winner = player1 |
34 |
}else if (dealer.hand.getValue() > player1.hand.getValue()){ |
35 |
//Subtract from players bank
|
36 |
instructionText.text = "You Lose!" |
37 |
winner = dealer |
38 |
}else if (dealer.hand.getValue() == player1.hand.getValue()){ |
39 |
//Subtract from players bank
|
40 |
instructionText.text = "Tie - Dealer Wins!" |
41 |
winner = dealer |
42 |
}else if (dealer.hand.getValue() < player1.hand.getValue()){ |
43 |
//Add to players bank
|
44 |
instructionText.text="You Win!"; |
45 |
winner = player1 |
46 |
}
|
47 |
|
48 |
if(winner is Player){ |
49 |
moveMoneyContainer(position: playerCardsY) |
50 |
}else{ |
51 |
moveMoneyContainer(position: dealerCardsY) |
52 |
}
|
53 |
}
|
Kita ambil posisi x dan y dari kartu pertama di array allCards
, yang merupakan kartu pertama dealer. Lalu kita buat konstanta tempCard
dengan memanggil getFirstCard
pada dealer. Ingatkah saat kita mengatur Card
di fungsi deal sebelumnya? Di sini kita menambahkannya ke scene, mengatur posisinya menggunakan konstanta tempCardX
dan tempCardY
, dan mengatur zPosition
menjadi 0
agar ada di bawah semua kartu lain.
Kita perlu tahu siapa yang memenangkan game, jadi kita menginisialisasi variabel winner
dan mengisi nilainya menjadi player1
, walau hal ini akan berubah jika dealer
menjadi pemenang game ini.
Lalu kita menjalankan beberapa logika untuk menentukan siapa yang mememangkan game. Jika parameter hasBlackjack
bernilai true maka kita bisa tahu siapa yang m enang dan keluar dari fungsi tersebut. Jika tidak, kita melanjutkan logika yang ada untuk menentukan siapa yang memenangkan game. Saya tidak akan membahas tiap langkah logika ini karena akan cukup mudah dimengerti. Tidak peduli siapa yang menang, kita panggil fungsi moveMoneyContainer(position:)
, yang menerima parameter berisi posisi container tersebut perlu digeser. Ini akan berisi nilai posisi y kartu dealer
atau player1
.
11. Mengimplementasi moveMoneyContainer
Tambahkan kode berikut di bawah fungsi doGameOver
.
1 |
func moveMoneyContainer(position: Int){ |
2 |
let moveMoneyContainer = SKAction.moveTo(y: CGFloat(position), duration: 3.0) |
3 |
moneyContainer.run(moveMoneyContainer, completion: { [unowned self] in |
4 |
self.resetMoneyContainer() |
5 |
});
|
6 |
}
|
Fungsi moveMoneyContainer(position:) menggerakkan
menangkan game, antara pemain atau dealer. Saat moneyContainer
ke pihak yang meSKAction
selesai, kita panggil resetMoneyContainer
.
12. Mengimplementasi resetMoneyContainer
Fungsi resetMoneyContainer
menghapus semua uang dengan memanggil fungsi removeAllChildren()
, mengembalikan moneyContainer
ke posisi aslinya, dan memanggil newGame
.
1 |
func resetMoneyContainer(){ |
2 |
moneyContainer.removeAllChildren() |
3 |
moneyContainer.position.y = size.height/2 |
4 |
newGame() |
5 |
} |
13. Mengimplementasi newGame
Tambahkan kode berikut di bawah fungsi resetMoneyContainer
yang sudah dibuat di langkah sebelumnya.
1 |
func newGame(){ |
2 |
currentPlayerType = player1 |
3 |
deck.new() |
4 |
instructionText.text = "PLACE YOUR BET"; |
5 |
money10.isHidden = false; |
6 |
money25.isHidden = false; |
7 |
money50.isHidden = false; |
8 |
dealBtn.isHidden = false |
9 |
player1.hand.reset() |
10 |
dealer.hand.reset() |
11 |
player1.setYielding(yields: false) |
12 |
|
13 |
for card in allCards{ |
14 |
card.removeFromParent() |
15 |
}
|
16 |
allCards.removeAll() |
17 |
}
|
Di sini kita mengembalikan nilai semua variabel yang dibutuhkan dan menghapus semua kartu dari scene dengan memproses array allCards
dan memanggil removeFromParent()
pada setiap elemen.
14. Mengimplementasi htiBtn
dan standBtn
Yang tersisa untuk menyelesaikan game kita adalah mengimplementasi fitur sentuh pada hitBtn
dan standBtn
. Masukkan kode berikut di dalam fungsi touchesBegan(_:with:)
.
1 |
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { |
2 |
guard let touch = touches.first else { |
3 |
return
|
4 |
}
|
5 |
|
6 |
let touchLocation = touch.location(in: self) |
7 |
let touchedNode = self.atPoint(touchLocation) |
8 |
|
9 |
if(touchedNode.name == "money"){ |
10 |
let money = touchedNode as! Money |
11 |
bet(betAmount: money.getValue()) |
12 |
}
|
13 |
|
14 |
if(touchedNode.name == "dealBtn"){ |
15 |
deal() |
16 |
}
|
17 |
|
18 |
if(touchedNode.name == "hitBtn"){ |
19 |
hit() |
20 |
}
|
21 |
|
22 |
if(touchedNode.name == "standBtn"){ |
23 |
stand() |
24 |
}
|
25 |
}
|
Sekarang kita implementasi fungsi yang dipanggil di event handler. Tambahkan dua fungsi di bawah ini di bawah fungsi newGame
.
1 |
func hit(){ |
2 |
if(player1.getCanBet()){ |
3 |
currentPlayerType = player1 |
4 |
deal() |
5 |
player1.setCanBet(canBet: false) |
6 |
} |
7 |
} |
8 |
|
9 |
func stand(){ |
10 |
player1.setYielding(yields: true) |
11 |
standBtn.isHidden = true |
12 |
hitBtn.isHidden = true |
13 |
if(dealer.hand.getValue() < 17){ |
14 |
currentPlayerType = dealer |
15 |
deal(); |
16 |
}else{ |
17 |
doGameOver(hasBlackJack: false) |
18 |
} |
19 |
} |
Di dalam fungsi hit
, kita pastikan pemain bisa bertaruh, dan jika pemain melakukannya, kita mengubah currentPlayerType
menjadi player1
, lalu memanggil fungsi deal
dan mencegah pemain bertaruh lagi.
Di dalam fungsi stand kita panggil setYielding
pada player1
, dengan parameter true
. Lalu kita periksa apakah dealer
punya nilai lebih kecil dari 17, dan jika iya kita panggil deal, tapi jika tangan dealer
bernilai 17 atau lebih maka game akan berakhir.
Sekarang kamu bisa menjalankan game yang sudah selesai.
Kesimpulan
Ini adalah tutorial yang cukup panjang dengan banyak logika pada fungsi deal. Kita tidak mengimplementasi penggunakan Pot
dan penambahan atau pengurangan uang yang dimiliki pemain. Kenapa kamu tidak coba melengkapinya sebagai latihan untuk menyelesaikan game ini?
Sekarang kamu punya game blackjack yang bisa kamu banggakan. Terima kasih sudah membaca, dan saya harap tutorial ini bisa berguna. Selagi kamu di sini, lihatkan beberapa course lain dan tutorial tentang membuat aplikasi dengan Swift dan SpriteKit!