Статьи

Создайте игру в блэкджек в Swift 3 и SpriteKit

Конечный продукт
Что вы будете создавать

В этом руководстве вы создадите игру для блэкджека в SpriteKit с использованием Swift 3. Вы узнаете о реализации касания, создании визуальной анимации и многих других концепциях, которые пригодятся при создании игры SpriteKit.

Откройте XCode и выберите « Создать новый проект XCode» или выберите « Создать»> «Проект …» в меню « Файл» . Убедитесь, что выбрана iOS и выберите шаблон Game .

новый проект

Затем выберите все, что вы хотите для Название продукта, Название организации и Идентификатор организации . Убедитесь, что для языка установлено значение Swift , для игровой технологии выбран SpriteKit , а для устройстваiPad .

project_options

Укажите место для сохранения файлов проекта и нажмите « Создать» .

Загрузите репозиторий GitHub для этого проекта. Внутри вы увидите папку классов . Откройте эту папку и перетащите все файлы в папку с именем вашего проекта, например, блэкджек . Убедитесь, что Копирование элементов, если необходимо , отмечен, а также основной цели в списке целей.

Параметры файла с флажком Копировать элементы при необходимости

Также в учебном репозитории GitHub есть папка с названием обучающие изображения . Внутри навигатора проекта откройте Assets.xcassets и перетащите все изображения на боковую панель. Xcode автоматически создаст текстурные атласы из этих изображений.

Папка с учебными изображениями в GitHub

В навигаторе проекта есть два файла, которые вы можете удалить ( Gamescene.sks и Actions.sks ). Удалите эти два файла и выберите « Переместить в корзину» . Эти файлы используются встроенным редактором сцены Xcode, который можно использовать для визуальной компоновки ваших проектов. Тем не менее, мы будем создавать все через код, поэтому эти файлы не нужны.

Откройте GameViewController.swift , удалите его содержимое и замените его следующим.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
import UIKit
import SpriteKit
 
class GameViewController: UIViewController {
     
    override func viewDidLoad() {
        super.viewDidLoad()
         
        let scene = GameScene(size:CGSize(width: 768, height: 1024))
        let skView = self.view as!
        skView.showsFPS = false
        skView.showsNodeCount = false
        skView.ignoresSiblingOrder = false
        scene.scaleMode = .aspectFill
        skView.presentScene(scene)
    }
     
    override var prefersStatusBarHidden: Bool {
        return true
    }
}

Класс GameViewController наследуется от UIViewController и будет иметь вид SKView . Внутри метода viewDidLoad мы viewDidLoad свойство view до экземпляра SKView , используя as! введите оператор приведения и настройте представление.

Если вы запустили этот проект, когда создали его заново, вы можете заметить текст в правом нижнем углу экрана. Это то, для showsFPS showsNodeCount свойства showsFPS и showsNodeCount , показывающие количество кадров в секунду, в котором работает игра, и количество SKNodes видимых в сцене. Нам не нужна эта информация, поэтому мы устанавливаем их в false .

Свойство ignoreSiblingOrder используется для определения порядка рисования SKNode в игре. Мы установили здесь значение false потому что нам нужно, чтобы наши SKNodes рисовали в порядке их добавления на сцену.

Наконец, мы устанавливаем режим масштабирования на .aspectFill , который заставит содержимое сцены масштабироваться, чтобы заполнить весь экран. Затем мы вызываем метод skView presentScene(_:) в skView который представляет или «показывает» сцену.

Затем удалите все в GameScene.swift и замените его следующим.

01
02
03
04
05
06
07
08
09
10
11
import SpriteKit
import GameplayKit
 
class GameScene: SKScene {
     
    override func didMove(to view: SKView) {
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    }
}

Теперь вы можете протестировать проект, и у вас должен появиться пустой черный экран. На следующем шаге мы начнем добавлять контент на нашу сцену.

Введите следующий код в начале класса GameScene прямо под тем GameScene где GameScene наследуется от SKScene .

1
2
3
4
5
6
7
8
9
class GameScene: SKScene {
   let moneyContainer = SKSpriteNode(color: .clear, size: CGSize(width:250, height: 150))
   let dealBtn = SKSpriteNode(imageNamed: «deal_btn»)
   let hitBtn = SKSpriteNode(imageNamed: «hit_btn»)
   let standBtn = SKSpriteNode(imageNamed: «stand_btn»)
   let money10 = Money(moneyValue: .ten)
   let money25 = Money(moneyValue: .twentyFive)
   let money50 = Money(moneyValue: .fifty)
   let instructionText = SKLabelNode(text: «Place your bet»)

Мы создаем несколько SKSpriteNode с здесь. SKSpriteNode используются для создания цветного узла, или чаще всего из SKTexture , которая чаще всего является изображением. Мы используем удобный инициализатор init(color:size:) чтобы создать чистый цветной узел moneyContainer . moneyContainer будет использоваться для удержания денег, на которые игрок ставит, и в конце каждого раунда мы будем анимировать это движение к тому, кто выиграл игру. Размещение всех денег в этом единственном узле позволяет легко анимировать все деньги за один раз.

Далее мы создаем константы dealBtn , hitBtn и standBtn . Как следует из названия, они будут использоваться в игре, чтобы наносить удары и стоять соответственно. Мы используем удобный инициализатор init(imageNamed:) , который принимает в качестве параметра имя изображения без расширения.

Затем мы создаем три константы money10 , money25 и money50 , которые имеют тип Money . Money — это пользовательский класс, который расширяет SKSpriteNode и в зависимости от типа moneyValue передаваемого в качестве параметра, создает один из трех различных типов денег. Параметр moneyValue имеет тип MoneyValue , который является enum . Взгляните на класс Money в репозитории GitHub проекта, чтобы увидеть, как все это работает.

Наконец, мы создаем SKLabelNode используя удобный инициализатор init(text:) который принимает в качестве параметра текст, который будет отображаться внутри метки.

Добавьте следующее под didMove(to:) .

01
02
03
04
05
06
07
08
09
10
11
12
13
func setupTable(){
    let table = SKSpriteNode(imageNamed: «table»)
    addChild(table)
    table.position = CGPoint(x: size.width/2, y: size.height/2)
    table.zPosition = -1
    addChild(moneyContainer)
    moneyContainer.anchorPoint = CGPoint(x:0, y:0)
    moneyContainer.position = CGPoint(x:size.width/2 — 125, y:size.height/2)
    instructionText.fontColor = UIColor.black
    addChild(instructionText)
    instructionText.position = CGPoint(x: size.width/2, y: 400)
}
    

Здесь мы инициализируем постоянную table и добавляем ее в сцену, используя addChild(_:) который принимает в качестве параметра узел, добавляемый в сцену. Мы устанавливаем position table внутри сцены и устанавливаем ее zPosition в -1 . Свойство zPosition контролирует порядок прорисовки узлов. Наименьшее число рисуется первым, а более высокие числа — по порядку. Поскольку нам нужна table ниже всего остального, мы устанавливаем ее zPosition в -1 . Это гарантирует, что это нарисовано перед любыми другими узлами.

Мы также добавляем на moneyContainer и moneyContainer . Мы устанавливаем fontColor для fontColor на черный (по умолчанию белый).

Обновите didMove(to:) к следующему.

1
2
3
override func didMove(to view: SKView) {
        setupTable()
}

Метод didMove(to:) вызывается сразу после представления сцены представлением. Как правило, именно здесь вы будете выполнять настройку для вашей сцены и создавать свои активы. Если вы сейчас тестируете, вы должны увидеть, что table и instructionText добавлены на сцену. moneyContainer есть moneyContainer но вы его не видите, потому что мы создали его с чистым цветом.

Добавьте следующее ниже метода setupTable .

01
02
03
04
05
06
07
08
09
10
func setupMoney(){
        addChild(money10)
        money10.position = CGPoint(x: 75, y: 40)
         
        addChild(money25)
        money25.position = CGPoint(x:130, y:40)
         
        addChild(money50)
        money50.position = CGPoint(x: 185, y:40)
}

Здесь мы просто добавляем денежные инстансы и устанавливаем их позицию. Вызовите этот метод в didMove(to:) .

1
2
3
4
override func didMove(to view: SKView) {
    setupTable()
    setupMoney()
}

Добавьте следующее ниже метода setupMoney который вы создали на шаге выше.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
func setupButtons(){
    dealBtn.name = «dealBtn»
    addChild(dealBtn)
    dealBtn.position = CGPoint(x:300, y:40)
         
    hitBtn.name = «hitBtn»
    addChild(hitBtn)
    hitBtn.position = CGPoint(x:450, y:40)
    hitBtn.isHidden = true
         
    standBtn.name = «standBtn»
    addChild(standBtn)
    standBtn.position = CGPoint(x:600, y:40)
    standBtn.isHidden = true
}

Как и в случае с денежными средствами на предыдущем шаге, мы добавляем кнопки и устанавливаем их положения. Здесь мы используем свойство name чтобы мы могли идентифицировать каждую кнопку с помощью кода. Мы также устанавливаем hitBtn и standBtn как скрытые или невидимые, устанавливая для свойства isHidden значение true .

Теперь вызовите этот метод в didMove(to:) .

1
2
3
4
5
override func didMove(to view: SKView) {
    setupTable()
    setupMoney()
    setupButtons()
}

Если вы запустите приложение сейчас, вы увидите, что денежные экземпляры и кнопки были добавлены на сцену.

Нам нужно реализовать метод touchesBegan(_:with:) чтобы можно было узнать, когда были затронуты какие-либо объекты в сцене. Этот метод вызывается, когда один или несколько пальцев касаются экрана. Добавьте следующее в пределах touchesBegan .

01
02
03
04
05
06
07
08
09
10
11
12
13
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let touch = touches.first else {
        return
    }
             
    let touchLocation = touch.location(in: self)
    let touchedNode = self.atPoint(touchLocation)
             
    if(touchedNode.name == «money»){
        let money = touchedNode as!
        bet(betAmount: money.getValue())
    }
}

Свойство multiTouchEnabled вида сцены по умолчанию имеет значение false , что означает, что вид получает только первое касание последовательности мультитач. Если это свойство отключено, вы можете получить касание, используя first вычисленное свойство из набора касаний, поскольку в наборе есть только один объект.

Мы можем получить touchLocation пределах сцены с помощью свойства location касания. Затем мы можем выяснить, какой узел был затронут, вызывая atPoint(_:) и передавая touchLocation .

Мы проверяем, touchedNode ли touchedNode имени touchedNode « money », и если мы знаем, что они затронули один из трех экземпляров money. Мы инициализируем money константу, touchedNode значение touchedNode до Money , а затем вызываем метод bet вызывая метод getValue() для money константы.

Введите следующее ниже функции setupButtons вы создали в setupButtons шаге.

01
02
03
04
05
06
07
08
09
10
11
12
13
func bet(betAmount: MoneyValue ){
    if(betAmount.rawValue > player1.bank.getBalance()){
        print(«Trying to bet more than have»);
        return
    }else{
        pot.addMoney(amount: betAmount.rawValue)
        let tempMoney = Money(moneyValue: betAmount)
        tempMoney.anchorPoint = CGPoint(x:0, y:0)
        moneyContainer.addChild(tempMoney)
        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))))
         dealBtn.isHidden = false;
    }
}

Сначала мы удостоверимся, что игрок не пытается поставить больше денег, чем они имеют, и если они есть, мы просто возвращаемся из функции. В противном случае мы добавляем betAmount в pot , создаем константу tempMoney , устанавливаем ее anchorPoint в (0,0) и добавляем ее в moneyContainer . Затем мы устанавливаем его position и скрываем dealBtn , устанавливая для его свойства isHidden значение false.

SKSpriteNode имеет свойство anchorPoint которое по умолчанию (0.5,0.5) . Система координат размещает (0,0) внизу слева и (1,1) вверху справа. Вы бы изменили это свойство по умолчанию, если бы вы SKSpriteNode и хотели, чтобы оно вращалось вокруг другой точки. Например, если вы изменили свойство anchorPoint на (0,0) то SKSpriteNode будет вращаться из своего нижнего левого угла. Вы часто будете менять это свойство, чтобы помочь с позиционированием, как у нас здесь.

Нам нужно создать экземпляр классов Pot и Player чтобы этот код работал. Добавьте следующее вместе с другими константами и переменными.

1
2
let pot = Pot()
let player1 = Player(hand: Hand(),bank: Bank())

Если вы тестируете сейчас, вы можете нажать на любую из денег и добавить ее в moneyContainer .

Добавьте следующее вместе с остальными вашими константами и переменными.

1
2
3
4
5
6
let dealer = Dealer(hand: Hand())
var allCards = [Card]()
let dealerCardsY = 930 // Y position of dealer cards
let playerCardsY = 200 // Y position of player cards
var currentPlayerType:GenericPlayer = Player(hand: Hand(),bank: Bank())
let deck = Deck()

Массив allCards будет использоваться для хранения всех карт в игре. Это позволит легко проходить через них и удалять их со сцены за один раз. Константы dealerCardsY и dealerCardsY — это позиции карт на оси у. Это поможет нам при размещении новых карт. currentPlayerType используется, чтобы указать, к кому следует обращаться дальше. Он будет равен либо dealer либо player1 .

Внутри didMove(to:) добавьте следующее.

1
2
3
4
5
6
override func didMove(to view: SKView) {
    setupTable()
    setupMoney()
    setupButtons()
    currentPlayerType = player1
}

В предыдущем коде мы инициализировали currentPlayerType для безымянного экземпляра класса Player . Здесь мы установили его на player1 .

Нам нужно создать новую колоду карт, прежде чем мы реализуем метод раздачи. Введите следующее в setupTable .

01
02
03
04
05
06
07
08
09
10
11
12
13
func setupTable(){
    let table = SKSpriteNode(imageNamed: «table»)
    addChild(table)
    table.position = CGPoint(x: size.width/2, y: size.height/2)
    table.zPosition = -1
    addChild(moneyContainer)
    moneyContainer.anchorPoint = CGPoint(x:0, y:0)
    moneyContainer.position = CGPoint(x:size.width/2 — 125, y:size.height/2)
    instructionText.fontColor = UIColor.black
    addChild(instructionText)
    instructionText.position = CGPoint(x: size.width/2, y: 400)
    deck.new()
}

Теперь мы можем реализовать функцию сделки. Добавьте следующее ниже метода bet .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
func deal() {
    instructionText.text = «»
    money10.isHidden = true;
    money25.isHidden = true;
    money50.isHidden = true;
    dealBtn.isHidden = true;
    standBtn.isHidden = false
    hitBtn.isHidden = false
    let tempCard = Card(suit: «card_front», value: 0)
    tempCard.position = CGPoint(x:630, y:980)
    addChild(tempCard)
    tempCard.zPosition = 100
         
    let newCard = deck.getTopCard()
    var whichPosition = playerCardsY
    var whichHand = player1.hand
    if(self.currentPlayerType is Player){
        whichHand = player1.hand
        whichPosition = playerCardsY;
    } else {
        whichHand = dealer.hand
        whichPosition = dealerCardsY;
    }
         
    whichHand.addCard(card: newCard)
    let xPos = 50 + (whichHand.getLength()*35)
    let moveCard = SKAction.move(to: CGPoint(x:xPos, y: whichPosition),duration: 1.0)
    tempCard.run(moveCard, completion: { [unowned self] in
    self.player1.setCanBet(canBet: true)
    if(self.currentPlayerType is Dealer && self.dealer.hand.getLength() == 1){
        self.dealer.setFirstCard(card: newCard)
        self.allCards.append(tempCard)
        tempCard.zPosition = 0
    } else {
        tempCard.removeFromParent()
        self.allCards.append(newCard)
        self.addChild(newCard)
        newCard.position = CGPoint( x: xPos, y: whichPosition)
        newCard.zPosition = 100
    }
    if(self.dealer.hand.getLength() < 2){
        if(self.currentPlayerType is Player){
            self.currentPlayerType = self.dealer
        }else{
            self.currentPlayerType = self.player1
        }
        self.deal()
    }else if (self.dealer.hand.getLength() == 2 && self.player1.hand.getLength() == 2) {
        if(self.player1.hand.getValue() == 21 || self.dealer.hand.getValue() == 21){
            self.doGameOver(hasBlackJack: true)
        } else {
            self.standBtn.isHidden = false;
            self.hitBtn.isHidden = false;
        }
    }
             
    if(self.dealer.hand.getLength() >= 3 && self.dealer.hand.getValue() < 17){
        self.deal();
    } else if(self.player1.isYeilding() && self.dealer.hand.getValue() >= 17){
        self.standBtn.isHidden = true
        self.hitBtn.isHidden = true
        self.doGameOver(hasBlackJack: false)
    }
    if(self.player1.hand.getValue() > 21){
        self.standBtn.isHidden = true;
        self.hitBtn.isHidden = true;
        self.doGameOver(hasBlackJack: false);
    }
             
    })
}

Этот метод довольно велик, но необходим для реализации логики дилинга. Давайте сделаем это шаг за шагом. Мы инициализируем константу tempCard для экземпляра Card , устанавливаем его положение и добавляем его в сцену. Нам нужно, чтобы эта карта была взята в позиции zPosition больше 0 , потому что первая карта дилера должна быть в 0 . Мы устанавливаем это на произвольное число — 100 будет делать. Мы также создаем константу newCard , вызывая метод deck getTopCard() .

Затем мы инициализируем две переменные whichPosition и whichHand , а затем выполняем некоторую логику, чтобы определить их окончательные значения. Затем мы добавляем newCard в соответствующую руку (игрока или дилера). Константа xPos определяет окончательную позицию x карты после ее анимации.

Класс SKAction имеет ряд методов класса, которые вы можете вызывать для изменения свойств узла, таких как положение, масштаб и вращение. Здесь мы вызываем метод move(to:duration:) , который перемещает узел из одной позиции в другую. Однако, чтобы фактически выполнить SKAction , вы должны вызвать метод run(_:) узла и передать SKAction в качестве параметра. Здесь, однако, мы вызываем метод run(_:completion:) , который заставит код внутри замыкания завершения выполняться после завершения действия.

После завершения действия мы разрешаем игроку делать ставки, вызывая setCanBet(canBet:) на экземпляре player1 . Затем мы проверяем, является ли currentPlayerType экземпляром Dealer , и проверяем, что у dealer есть только одна карта, вызывая hand.getLength() . Если это так, мы устанавливаем первую карту dealer , которая нам понадобится в конце игры.

Поскольку первая карта dealer всегда закрыта до конца игры, нам нужна ссылка на первую карту, чтобы мы могли показать ее позже. Мы добавляем эту карту в массив allCards чтобы потом ее можно было удалить, а затем устанавливаем для ее свойства zPosition значение 0 как нам нужна эта карта ниже всех других карт. (Помните, что у других карт есть z-позиция 100 )

Если currentPlayerType не является экземпляром Dealer , а длина руки не равна 1 , тогда мы удаляем tempCard и помещаем newCard в ту же позицию, удостоверившись, что его zPosition в 100 .

Согласно правилам блэкджека, и дилер, и игрок получают две карты, с которых можно начинать игру. Здесь мы проверяем, что такое currentPlayerType , и меняем его на противоположное. Поскольку у дилера менее двух карт, мы снова вызываем функцию deal . В противном случае мы проверяем, есть ли у dealer и player1 две карты, и если это так, мы проверяем, есть ли у них карты с общим значением 21 — выигрышная комбинация. Если у любого из них есть 21, то игра окончена, потому что один из них получил блэкджек. Если ни один из них не имеет 21, то мы показываем standBtn и hitBtn и игра продолжается.

Правила блэкджека гласят, что dealer должен стоять на уровне 17 или выше. Следующие несколько строк кода проверяют, является ли ценность руки dealer меньше 17, и если это так, вызывает метод deal . Если это 17 или больше, то игра окончена. Наконец, если player1 руки player1 больше 21, то игра заканчивается, потому что они проиграли.

Это было много логики, чтобы пройти! Если что-то неясно, просто прочитайте это снова и не торопитесь, чтобы понять это.

Далее нам нужно реализовать метод gameover .

Нам нужно знать, когда пользователь нажал кнопку сделки. Добавьте следующий код в метод touchesBegan(_:with:) .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let touch = touches.first else {
        return
    }
         
    let touchLocation = touch.location(in: self)
    let touchedNode = self.atPoint(touchLocation)
         
    if(touchedNode.name == «money»){
        let money = touchedNode as!
        bet(betAmount: money.getValue())
    }
         
    if(touchedNode.name == «dealBtn»){
        deal()
    }
}

Затем введите следующий текст под методом deal который вы создали на шаге выше.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
func doGameOver(hasBlackJack: Bool){
    hitBtn.isHidden = true
    standBtn.isHidden = true
    let tempCardX = allCards[1].position.x
    let tempCardY = allCards[1].position.y
    let tempCard = dealer.getFirstCard()
    addChild(tempCard)
    allCards.append(tempCard)
    tempCard.position = CGPoint(x:tempCardX,y:tempCardY)
    tempCard.zPosition = 0
    var winner:GenericPlayer = player1
         
    if(hasBlackJack){
        if(player1.hand.getValue() > dealer.hand.getValue()){
            //Add to players Bank Here (pot value * 1.5)
            instructionText.text = «You Got BlackJack!»;
            moveMoneyContainer(position: playerCardsY)
        }else{
            //Subtract from players bank here
            instructionText.text = «Dealer got BlackJack!»;
            moveMoneyContainer(position: dealerCardsY)
        }
        return
    }
         
    if (player1.hand.getValue() > 21){
        instructionText.text = «You Busted!»
        //Subtract from players bank
        winner = dealer
    }else if (dealer.hand.getValue() > 21){
        //Add to players bank
        instructionText.text = «Dealer Busts. You Win!»
        winner = player1
    }else if (dealer.hand.getValue() > player1.hand.getValue()){
        //Subtract from players bank
        instructionText.text = «You Lose!»
        winner = dealer
    }else if (dealer.hand.getValue() == player1.hand.getValue()){
         //Subtract from players bank
        instructionText.text = «Tie — Dealer Wins!»
        winner = dealer
    }else if (dealer.hand.getValue() < player1.hand.getValue()){
        //Add to players bank
        instructionText.text=»You Win!»;
        winner = player1
    }
         
    if(winner is Player){
        moveMoneyContainer(position: playerCardsY)
    }else{
        moveMoneyContainer(position: dealerCardsY)
    }
}

Мы получаем allCards x и y первой карты в массиве allCards , которая является первой картой дилера. Затем мы создаем постоянную tempCard , вызывая getFirstCard у дилера. Помните, мы установили эту Card ранее в методе сделки? Здесь мы добавляем его в сцену, устанавливаем его положение, используя tempCardX и tempCardY , и устанавливаем его zPosition в 0 так что под другими картами.

Нам нужно знать, кто выиграл игру, поэтому мы инициализируем переменного winner устанавливая его равным player1 , хотя это может измениться в зависимости от того, действительно ли dealer выиграл игру.

Затем мы проходим логику, чтобы определить, кто выиграл игру. Если параметр hasBlackjack true, тогда мы hasBlackjack , кто победил, и возвращаемся из функции. В противном случае мы продолжим логику, чтобы выяснить, кто выиграл игру. Я не собираюсь идти шаг за шагом через эту логику, поскольку это должно быть понятно для понимания. Независимо от того, кто выиграл, мы вызываем moveMoneyContainer(position:) , который принимает в качестве параметра позицию для перемещения контейнера денег. Это будет позиция y карт dealer или player1 .

Введите следующий код под методом doGameOver .

1
2
3
4
5
6
func moveMoneyContainer(position: Int){
    let moveMoneyContainer = SKAction.moveTo(y: CGFloat(position), duration: 3.0)
    moneyContainer.run(moveMoneyContainer, completion: { [unowned self] in
            self.resetMoneyContainer()
    });
}

Метод moveMoneyContainer(position:) перемещает moneyContainer тому, кто выиграл игру, будь то игрок или дилер. Когда SKAction завершается, мы вызываем resetMoneyContainer .

Метод resetMoneyContainer удаляет все деньги, вызывая метод removeAllChildren() , сбрасывает moneyContainer в исходное положение и вызывает newGame .

1
2
3
4
5
func resetMoneyContainer(){
    moneyContainer.removeAllChildren()
    moneyContainer.position.y = size.height/2
    newGame()
}

Добавьте следующее под методом resetMoneyContainer который вы реализовали на шаге выше.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
func newGame(){
   currentPlayerType = player1
   deck.new()
   instructionText.text = «PLACE YOUR BET»;
   money10.isHidden = false;
   money25.isHidden = false;
   money50.isHidden = false;
   dealBtn.isHidden = false
   player1.hand.reset()
   dealer.hand.reset()
   player1.setYielding(yields: false)
        
   for card in allCards{
       card.removeFromParent()
   }
   allCards.removeAll()
   }

Здесь мы сбрасываем все необходимые переменные и удаляем все карточки со сцены, allCards массив allCards и вызывая removeFromParent() для каждого элемента.

Все, что осталось, чтобы завершить нашу игру, это реализовать прикосновения к hitBtn и standBtn . Введите следующее в методе touchesBegan(_:with:) .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let touch = touches.first else {
        return
    }
         
    let touchLocation = touch.location(in: self)
    let touchedNode = self.atPoint(touchLocation)
         
    if(touchedNode.name == «money»){
        let money = touchedNode as!
        bet(betAmount: money.getValue())
    }
         
    if(touchedNode.name == «dealBtn»){
        deal()
    }
         
    if(touchedNode.name == «hitBtn»){
        hit()
    }
         
    if(touchedNode.name == «standBtn»){
        stand()
    }
}

А теперь мы реализуем методы, вызываемые в обработчике событий. Введите следующие два метода ниже метода newGame .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
func hit(){
    if(player1.getCanBet()){
        currentPlayerType = player1
        deal()
        player1.setCanBet(canBet: false)
    }
}
 
func stand(){
    player1.setYielding(yields: true)
    standBtn.isHidden = true
    hitBtn.isHidden = true
    if(dealer.hand.getValue() < 17){
        currentPlayerType = dealer
        deal();
    }else{
        doGameOver(hasBlackJack: false)
    }
}

В методе hit мы гарантируем, что игрок может делать ставки, и если это так, мы устанавливаем player1 в player1 , а затем вызываем метод deal и прекращаем дальнейшие ставки игрока.

В методе stand мы вызываем setYielding для player1 , передавая true . Затем мы проверяем, составляет ли рука dealer меньше 17 , и если это так, мы называем сделку, и если рука dealer 17 или больше, это означает, что игра окончена.

Теперь вы можете проверить завершенную игру.

Это был длинный урок с хорошей логикой в ​​методе сделки. Мы не реализовали использование банка и добавление и вычитание денег из банка игрока. Почему бы вам не попробовать сделать это как упражнение, чтобы закончить приложение?

Теперь у вас есть игра в блэкджек, которой можно гордиться. Спасибо за чтение, и я надеюсь, что этот урок оказался полезным. Пока вы здесь, ознакомьтесь с некоторыми другими нашими курсами и учебными пособиями по программированию приложений с использованием Swift и SpriteKit!

  • стриж
    Swift From Scratch: введение в классы и структуры
    Барт Джейкобс
  • стриж
    Запрограммируйте игру с боковой прокруткой с помощью Swift 3 и SpriteKit
    Дерек Дженсен
  • IOS
    Идите дальше со Swift: анимация, работа в сети и пользовательские элементы управления
    Маркус Мюльбергер