Advertisement
  1. Code
  2. iOS SDK

Membuat Game Blackjack dengan Swift 3 dan SpriteKit

Scroll to top
Read Time: 19 min

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

Final product imageFinal product imageFinal product image
What You'll Be Creating

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.

new_projectnew_projectnew_project

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.

project_optionsproject_optionsproject_options

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.

File options with Copy items if needed box checkedFile options with Copy items if needed box checkedFile options with Copy items if needed box checked

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.

Tutorial images folder in GitHubTutorial images folder in GitHubTutorial images folder in GitHub

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 moneyContainer ke pihak yang memenangkan game, antara pemain atau dealer. Saat SKAction 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!

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.