在Swift 3和SpriteKit中创建二十一点游戏

最终产品图片
您将要创造的

在本教程中,您将使用Swift 3在SpriteKit中创建一个二十一点游戏。您将学习实现触摸,创建视觉动画以及许多其他概念,这些概念在构建SpriteKit游戏时会派上用场。

1.创建项目并导入资源

打开Xcode,然后选择“ 创建新的Xcode项目”,或从“ 文件”菜单中选择“ 新建”>“项目... ”。 确保选择了iOS ,然后选择“ 游戏”模板。

新项目

接下来,选择所需的产品名称组织名称组织标识符 。 确保“ 语言”设置为“ Swift” ,“ 游戏技术”设置为“ SpriteKit” ,“ 设备”设置为iPad

project_options

指定保存项目文件的位置,然后单击创建

导入助手类

下载此项目的GitHub存储库。 在其中,您将看到一个classes文件夹。 打开此文件夹,然后将所有文件拖到名称为项目名称的文件夹上,例如blackjack 。 确保选中“ 如果需要复制项目”以及目标列表中的主要目标。

选中需要复制项目的文件选项框

导入图像

在教程GitHub存储库中也有一个名为教程图像的文件夹。 在项目导航器中,打开Assets.xcassets并将所有图像拖到侧栏中。 Xcode将自动从这些图像创建纹理图集。

GitHub中的Tutorial images文件夹

2.设置项目

在项目导航器中,可以删除两个文件( Gamescene.sksActions.sks )。 删除这两个文件,然后选择“ 移至废纸rash” 。 这些文件由Xcode的内置场景编辑器使用,该编辑器可用于可视化地布局您的项目。 但是,我们将通过代码创建所有内容,因此不需要这些文件。

打开GameViewController.swift ,删除其内容,并将其替换为以下内容。

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
        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方法内部,我们使用as!view属性向下转换为SKView实例as! 键入强制转换运算符,然后配置视图。

如果要在重新创建该项目时运行它,则可能会注意到屏幕右下方的文本。 这就是showsFPSshowsNodeCount属性的用途,显示游戏正在运行的每秒帧数以及场景中可见的SKNodes数。 我们不需要此信息,因此将它们设置为false

ignoreSiblingOrder属性用于确定游戏中SKNode的绘制顺序。 我们在此将其设置为false是因为我们需要我们的SKNodes绘制它们添加到场景中的顺序。

最后,我们将缩放模式设置为.aspectFill ,这将导致场景的内容缩放以填满整个屏幕。 然后,我们调用presentScene(_:)的方法skView呈现或“表演”的场景。

接下来,删除GameScene.swift中的所有内容并将其替换为以下内容。

import SpriteKit
import GameplayKit

class GameScene: SKScene {
    
    override func didMove(to view: SKView) {
    }
   
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {    
    }
}

现在,您可以测试项目,并且应该出现空白的黑屏。 在下一步中,我们将开始向场景添加内容。

3.变量和常量

GameScene继承自SKScene位置下面的GameScene类的开始处输入以下代码。

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")

我们在这里创建许多SKSpriteNodeSKSpriteNode用于创建彩色节点,或更常见的是从SKTexture创建彩色节点, SKTexture通常是图像。 我们使用便捷的初始化程序init(color:size:)创建一个清晰的彩色节点moneyContainermoneyContainer将用于持有玩家下注的钱,并且在每轮结束时,我们将为向谁赢得比赛的方向设置动画。 将所有资金放置在该单个节点中,可以轻松地一次动画化所有资金。

接下来,我们创建常量dealBtnhitBtnstandBtn 。 顾名思义,它们将分别在游戏中用于发动,击中和站立。 我们正在使用便捷的初始化程序init(imageNamed:) ,它以不带扩展名的图像名称作为参数。

然后,我们创建三个常量money10money25money50 ,它们是Money类型。 Money是扩展SKSpriteNode的自定义类,并根据作为参数传递的moneyValue的类型创建三种不同的Money类型之一。 moneyValue参数的类型为MoneyValue ,这是一个enum 。 看一下项目GitHub存储库中的Money 类,看看这一切如何工作。

最后,我们使用便利初始化程序init(text:)创建一个SKLabelNode ,该初始化程序将要在标签内显示的文本作为参数。

4.实现setupTable

didMove(to:)函数下面添加以下内容。

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(_:)将其添加到场景中,该对象将要添加到场景中的节点作为参数。 我们设置table在场景中的position ,并将其zPosition设置为-1zPosition属性控制绘制节点的顺序。 首先绘制最低的数字,依次绘制更大的数字。 因为我们需要其他所有table都在下面,所以我们将其zPosition设置为-1 。 这样可以确保在其他任何节点之前绘制它。

我们还将moneyContainerinstructionText添加到场景中。 我们将instructionText fontColor设置为黑色(默认为白色)。

didMove(to:)更新为以下内容。

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

视图呈现场景后立即调用didMove(to:)方法。 通常,在这里您将为场景进行设置并创建资产。 如果现在进行测试,则应该看到该tableinstructionText文本已添加到场景中。 moneyContainer那里,但是您看不到它,因为我们用清晰的颜色创建了它。

5.实施setupMoney

setupTable方法下面添加以下内容。

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:)调用此方法。

override func didMove(to view: SKView) {
    setupTable()
    setupMoney()
}

6.实现setupButtons

将以下内容添加到在上述步骤中创建的setupMoney方法下面。

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属性,以便能够通过代码识别每个按钮。 通过将isHidden属性设置为true ,我们还可以将hitBtnstandBtn设置为隐藏或不可见。

现在在didMove(to:)调用此方法。

override func didMove(to view: SKView) {
    setupTable()
    setupMoney()
    setupButtons()
}

如果现在运行该应用程序,应该会看到货币实例和按钮已添加到场景中。

7.实施touchesBegan

我们需要实现touchesBegan(_:with:)方法,以判断何时触摸了场景中的任何对象。 当一个或多个手指触摸屏幕时,将调用此方法。 在touchesBegan添加以下内容。

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! Money
        bet(betAmount: money.getValue())
    }
}

默认情况下,场景视图的multiTouchEnabled属性设置为false ,这意味着视图仅接收多点触摸序列的第一次触摸。 禁用此属性后,由于触摸集中只有一个对象,因此可以使用触摸组的first计算属性来检索触摸。

我们可以通过touch的location属性在场景中获取touchLocation 。 然后,我们可以找出哪些节点是通过调用感动atPoint(_:)并传入touchLocation

我们检查touchedNode的name属性是否等于“ money ”,是否知道他们已经触碰到了这三个money实例之一。 我们初始化money通过向下转型的不断touchedNodeMoney ,然后我们称之为bet方法调用getValue()的方法money不变。

8.实施bet

在您在上面的步骤中创建的setupButtons函数下输入以下内容。

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 。 然后,通过将其isHidden属性设置为false来设置其position并隐藏dealBtn

SKSpriteNode具有一个anchorPoint属性,默认为(0.5,0.5) 。 坐标系将(0,0)放在左下角,将(1,1)放在右上角。 如果要旋转SKSpriteNode并希望它绕不同的点旋转,则可以更改此属性的默认值。 例如,如果将anchorPoint属性更改为(0,0)SKSpriteNode将从其左下角旋转。 就像我们在这里一样,您经常会更改此属性以帮助定位。

我们需要创建PotPlayer类的实例,此代码才能正常工作。 将以下内容与其他常量和变量一起添加。

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

如果您现在进行测试,则可以按任意一项,然后将其添加到moneyContainer

9.实施deal

将以下内容与其余的常量和变量一起添加。

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数组将用于容纳游戏中的所有卡。 这样可以轻松遍历它们,并一次性将它们从场景中删除。 dealerCardsYplayerCardsY常数是纸牌在y轴上的位置。 这将有助于我们放置新卡。 currentPlayerType用于指示下一个交易对象。 它将等于发dealerplayer1

didMove(to:)内部,添加以下内容。

override func didMove(to view: SKView) {
    setupTable()
    setupMoney()
    setupButtons()
    currentPlayerType = player1
}

在前面的代码中,我们将currentPlayerType初始化为Player类的未命名实例。 在这里,我们将其设置为player1

在实施Deal方法之前,我们需要创建一个新的扑克牌。 在setupTable输入以下setupTable

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方法下面添加以下内容。

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就可以了。 我们还通过调用deckgetTopCard()方法来创建newCard常量。

接下来,我们初始化两个变量whichPositionwhichHand ,然后通过一些逻辑来确定它们的最终值。 然后,我们添加newCard到适当的手(或者玩家的或经销商的)。 一旦完成动画制作, xPos常数将确定卡片的最终x位置。

SKAction类具有许多可用于更改节点属性(例如位置,比例和旋转)的类方法。 在这里,我们调用move(to:duration:)方法,该方法会将节点从一个位置移动到另一个位置。 但是,要实际执行SKAction ,您必须调用节点的run(_:)方法并将SKAction作为参数传递。 但是,在这里,我们正在调用run(_:completion:)方法,该方法将导致操作完成执行后,完成闭包中的代码运行。

操作完成后,我们可以通过在player1实例上调用setCanBet(canBet:)来允许玩家下注。 然后,我们通过调用hand.getLength()检查currentPlayerType是否是Dealer的实例,并检查dealer只有一张卡。 在这种情况下,我们设置了dealer的第一张牌,这是我们在游戏结束时需要的。

因为发dealer者的第一张牌始终面朝下直到比赛结束,所以我们需要参考第一张牌,以便稍后显示。 我们将此卡添加到allCards数组中,以便稍后将其删除,然后将其zPosition属性设置为0因为我们需要此卡在所有其他卡下面。 (请记住其他卡的z位置为100

如果currentPlayerType不是Dealer的实例,并且手的长度不等于1 ,则我们删除tempCard并将newCard放在相同位置,确保将其zPosition设置为100

根据二十一点的规则,发牌者和玩家都会获得两张牌以开始游戏。 在这里,我们正在检查currentPlayerType是什么,并将其更改为相反的值。 因为发牌者的卡少于两张,所以我们再次调用deal功能。 否则,我们检查发dealerplayer1是否都持有两张牌,如果是这种情况,我们将检查两者中是否有总值为21的牌(获胜手)。 如果其中一个有21,则游戏结束,因为其中一个已经赢得了21点。 如果两者都不是21,则我们显示standBtnhitBtn ,然后游戏继续进行。

二十一点规则规定dealer必须年满17岁 。 码检查的接下来的几行,如果dealer的手的值小于17,如果是调用deal方法。 如果大于等于17 ,则游戏结束。 最后,如果player1 1的手牌价值大于21,则游戏已经结束,因为他们已经破产。

这是要经历的许多逻辑! 如果不清楚,请重新阅读并花一些时间来理解它。

接下来,我们需要实现gameover方法。

我们需要能够分辨出用户何时按下了交易按钮。 将以下代码添加到touchesBegan(_:with:)方法。

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! Money
        bet(betAmount: money.getValue())
    }
        
    if(touchedNode.name == "dealBtn"){
        deal()
    }
}

10.实现doGameOver

接下来,在您在上述步骤中创建的deal方法下方输入以下内容。

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位置,这是发牌人的第一张卡。 然后,通过在经销商处调用getFirstCard实例化一个恒定的tempCard 。 还记得我们在发Card方法中设置了这张Card吗? 在这里,我们将其添加到场景中,使用tempCardXtempCardY常量设置其位置,并将其zPosition设置为0 所以它在其他卡的下面。

我们需要知道谁赢得了比赛,因此我们将变量winner初始化为将其设置为player1 ,尽管这可能会有所变化,具体取决于发dealer真正赢得了比赛。

然后,我们通过一些逻辑来确定谁赢得了比赛。 如果hasBlackjack参数为true,则我们确定谁赢了,并从函数中返回。 否则,我们将继续按照逻辑找出谁赢得了比赛。 我不会逐步讲解这个逻辑,因为应该很容易理解。 无论谁赢了,我们都将调用moveMoneyContainer(position:) ,该方法将钱moveMoneyContainer(position:)作为参数。 这将是发dealer人或player1的y位置。

11.实施moveMoneyContainer

doGameOver方法下面输入以下代码。

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

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

13.实施newGame

将以下内容添加到您在上述步骤中实现的resetMoneyContainer方法下方。

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数组并在每个元素上调用removeFromParent() ,重置所有必需的变量并从场景中删除所有卡。

14.实施hitBtnstandBtn

完成游戏所需hitBtn就是实现hitBtnstandBtn的接触。 在touchesBegan(_:with:)方法中输入以下内容。

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! Money
        bet(betAmount: money.getValue())
    }
        
    if(touchedNode.name == "dealBtn"){
        deal()
    }
        
    if(touchedNode.name == "hitBtn"){
        hit()
    }
        
    if(touchedNode.name == "standBtn"){
        stand()
    }
}

现在,我们将实现事件处理程序中调用的方法。 在newGame方法下面输入以下两种方法。

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方法中,我们确保玩家可以下注,如果是这种情况,我们将currentPlayerType设置为player1 ,然后调用deal方法并进一步停止玩家下注。

在stand方法中,我们在setYielding上调用player1 ,并传入true 。 然后,我们检查发dealer者的手牌价值是否小于17 ,如果是这种情况,我们称之为交易;如果发dealer者的手数等于或大于17 ,则表示游戏结束了。

您现在可以测试完成的游戏。

结论

这是一个很长的教程,在Deal方法中包含了很多逻辑。 我们没有实现使用底Pot以及从玩家的银行中增加和减少钱款的方法。 您为什么不尝试将其作为完成应用程序的练习?

翻译自: https://code.tutsplus.com/tutorials/create-a-blackjack-game-in-swift-3-and-spritekit--cms-28511

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值