В этом руководстве вы создадите игру для блэкджека в SpriteKit с использованием Swift 3. Вы узнаете о реализации касания, создании визуальной анимации и многих других концепциях, которые пригодятся при создании игры SpriteKit.
1. Создание проекта и импорт ресурсов
Откройте XCode и выберите « Создать новый проект XCode» или выберите « Создать»> «Проект …» в меню « Файл» . Убедитесь, что выбрана iOS и выберите шаблон Game .
Затем выберите все, что вы хотите для Название продукта, Название организации и Идентификатор организации . Убедитесь, что для языка установлено значение Swift , для игровой технологии выбран SpriteKit , а для устройства — iPad .
Укажите место для сохранения файлов проекта и нажмите « Создать» .
Импорт вспомогательных классов
Загрузите репозиторий GitHub для этого проекта. Внутри вы увидите папку классов . Откройте эту папку и перетащите все файлы в папку с именем вашего проекта, например, блэкджек . Убедитесь, что Копирование элементов, если необходимо , отмечен, а также основной цели в списке целей.
Импорт изображений
Также в учебном репозитории GitHub есть папка с названием обучающие изображения . Внутри навигатора проекта откройте Assets.xcassets и перетащите все изображения на боковую панель. Xcode автоматически создаст текстурные атласы из этих изображений.
2. Настройка проекта
В навигаторе проекта есть два файла, которые вы можете удалить ( 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?) {
}
}
|
Теперь вы можете протестировать проект, и у вас должен появиться пустой черный экран. На следующем шаге мы начнем добавлять контент на нашу сцену.
3. Переменные и константы
Введите следующий код в начале класса 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:)
который принимает в качестве параметра текст, который будет отображаться внутри метки.
4. Реализация setupTable
Добавьте следующее под 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
но вы его не видите, потому что мы создали его с чистым цветом.
5. Реализация setupMoney
Добавьте следующее ниже метода 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()
}
|
6. Реализация setupButtons
Добавьте следующее ниже метода 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()
}
|
Если вы запустите приложение сейчас, вы увидите, что денежные экземпляры и кнопки были добавлены на сцену.
7. Реализация touchesBegan
Нам нужно реализовать метод 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
константы.
8. Реализация bet
Введите следующее ниже функции 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
.
9. Реализация deal
Добавьте следующее вместе с остальными вашими константами и переменными.
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()
}
}
|
10. Реализация doGameOver
Затем введите следующий текст под методом 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
.
11. Реализация moveMoneyContainer
Введите следующий код под методом 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
.
12. Реализация resetMoneyContainer
Метод resetMoneyContainer
удаляет все деньги, вызывая метод removeAllChildren()
, сбрасывает moneyContainer
в исходное положение и вызывает newGame
.
1
2
3
4
5
|
func resetMoneyContainer(){
moneyContainer.removeAllChildren()
moneyContainer.position.y = size.height/2
newGame()
}
|
13. Реализация 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()
для каждого элемента.
14. Реализация hitBtn
и standBtn
Все, что осталось, чтобы завершить нашу игру, это реализовать прикосновения к 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!