0
votes

I'm in the process of creating a card game in Swift using SKSprite Nodes to show the card faces.

In my 'deal' function, it deals 3 cards to each player, one at a time in a 'round-robin' fashion. This is working fine but I'm trying to add a bit of animation - I'm trying to make it show each card being dealt to the local player by animating it moving from the deck to the players hand position.

I can get the Sprite Nodes to show without the animation but when I try with SKAction, it gives me the following error after the action is completed:

reason: 'Attemped to add a SKNode which already has a parent: name:'local player node' texture:[ 'CARD39' (203 x 350)] position:{281.25, 100.05000305175781}

class GameScene: SKScene {

let tempCard = SKSpriteNode(texture: SKTexture(imageNamed: "back"))

        func deal() {

        players = createPlayers(with: numberOfPlayers)
        
        tempCard.setScale((screenSize.width/100) * 0.2)
        tempCard.zPosition = 10
        tempCard.name = "tempcard"
        addChild(tempCard)
        
        let localPlayer = 0
        var i = 0
        repeat {
            print("Card number: \(i)")
            var x = 0
            let xPos = screenSize.width * (0.25 * CGFloat(i+1))
            
            players.forEach { player in
                
                let newCard = self.deck.dealOneCard()
                player.hand.addCard(card: newCard)
                
                localPlayerNode = players[localPlayer].hand[i].cardImage()
                localPlayerNode.position = CGPoint(x: xPos, y: screenSize.height * 0.15)
                localPlayerNode.setScale((screenSize.width/100) * 0.2)
                localPlayerNode.name = "local player node"
                
                if player.id == localPlayer {
                    
                    let moveCard = SKAction.move(to: CGPoint(x: xPos, y: screenSize.height * 0.15),duration: 1.5)
                    
                    //addChild(localPlayerNode) --using this instead of SKAction works

                    tempCard.run(moveCard, completion: {() -> Void in
                        self.tempCard.removeFromParent()
                        self.addChild(self.localPlayerNode)
                    })
                }
                x+=1
            }
            i+=1
        } while i<3
1
"it gives me the following error" Where!? tempcard... What is it for? And where does it come from? - El Tomato
Hi! The error comes when the SKAction has finished. tempCard is only used in this function, its only used to show an image of the back of the card, I've updated the post, lt me know if I need to add more detail - Captain Le Chuck

1 Answers

0
votes

I believe the problem is with adding the player nodes within your loop, and then not removing them afterwards. I'm sure you don't want to remove the players every time you call the " deal() " function so you should add the player nodes in the didMove() method. I've put together a playground that demonstrates this (github Link).
I've tried to use a delay to make the cards deal 1 by one, but this is a different problem you will need to look elsewhere to solve. (Something with the .run( action) makes it so that it doesn't actually animate until the loop is complete.

example gif with bad pixel art

class GameScene: SKScene {
let scale : CGFloat = 50
var deck : [String] = []
let composition = deckComp()
let numberOfPlayers = 3
var players : [Player] = []
override func didMove(to view: SKView) {
    // creates deck
for c in deckComp().colors {
    for s in deckComp().suits {
        deck.append( s + " of " + c )
       }
    }
players = createPlayers(numberOfPlayers: numberOfPlayers, center : CGPoint(x : 25, y : 25))
// setup the scales, and players
for plyr in players {
    plyr.setScale(scale: (frame.width/10000) * scale)
    addChild( plyr.node)
    }
    }
func createPlayers(numberOfPlayers : Int, center : CGPoint) -> [Player] {

    let radius = Float(5*scale)
    let two_pi = 2 * 3.14159
    let angular_positions = two_pi / Double(numberOfPlayers)
    var players_out : [Player] = []
    for i in 0...numberOfPlayers - 1 {
        let sprite = SKSpriteNode(texture: SKTexture(imageNamed: "card_player.png"))
        sprite.zPosition = 1
        sprite.position = CGPoint( x : center.x + CGFloat(radius * sin( Float(angular_positions) * Float(i) )), y : center.y +  CGFloat(radius * cos( Float(angular_positions) * Float(i) )) )
        sprite.texture?.filteringMode = .nearest // .linear for blurry, .nearest for pixely
        let player_instance = Player(node : sprite, name : "Player " + String(i + 1), id : Int8(i + 1) )
        players_out.append(player_instance)
    }
    return players_out
}
func deal() {
         // I moved the setscale stuff for player sprites to didMove()
    // first check if there is enough in deck
    if deck.count > players.count {
    var i = 0
        repeat {
        // add the temp card
        let tempCard = SKSpriteNode(texture: SKTexture(imageNamed: "back"))
        tempCard.size = CGSize( width: tempCard.size.width * (frame.width/10000) * scale, height : tempCard.size.height * (frame.width/10000) * scale )
        tempCard.zPosition = 10
        tempCard.texture?.filteringMode = .nearest
        self.addChild(tempCard)
        // done adding temporary card
        let xPos = frame.width * (0.25 * CGFloat(i+1))
        tempCard.position = CGPoint(x : xPos, y : 0.75 * frame.height)
            let newCard = self.deck.popLast() // replaced dealOneCard() since I haven't defined it
            players[i].addCard(card: newCard!)  // removed hand.addCard(), since I don't have the array extensions
        //                player.name = "local player node"
        let moveCard = SKAction.move(to: players[i].node.position ,duration: 1.5)
                //addChild(localPlayerNode) --using this instead of SKAction works
            tempCard.run(moveCard, completion: { () -> Void in tempCard.removeFromParent();                })
            i += 1
        } while i < players.count
    } else { print("not enough cards to deal to everyone")} // when deck is empty
}

override func mouseUp(with event: NSEvent) {
    deal()
}