Search code examples
swiftsprite-kitsubclassing

How to get SKSpriteNode subclassed object back from click


I have the following Object I have subclassed from SKSpriteNode

class PlayingCard : SKSpriteNode
{
  var suit:Int = 0
  var rank:Int = 0
  var visible:Bool = false

  init()
    {
        super.init(
         texture: SKTexture(imageNamed: "card.png"), 
         color: NSColor.blackColor(), 
         size: SKTexture(imageNamed: "card.png").size())
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func updateSprite()
    {
       if (suit == 1 && rank ==1) 
       { self.texture = SKTexture(imageNames: "ace_of_clubs.png") }
       .
       .
       .
       .
       // A bunch of switches and if's to assign the right image
    }
}

Now I initialize this class and add it to my scene. Everything works fine, the card shows up. Except when I look for self.nodeAtPoint(location) after a click, I get a SKNode back!! Are all my values lost? Whats going on here?

override func mouseDown(theEvent: NSEvent) 
{
    // Called when a mouse click starts
    let location = theEvent.locationInNode(self)
    let t = self.nodeAtPoint(location)
    NSLog("%s",String(t.suit)) //<-------- Won't Compile
}

Any idea how to do this? Trying to type cast using: t as! PlayingCard builds the project but I get gibberish printed in Log.

Here are the enums and the PlayingCards class:

enum Suits:Int
{
    case Diamond, Heart, Club, Spade
}

enum Ranks:Int
{
    case ace, two, three, four, five, six, seven, eight, nine, ten, jack, queen, king
}

class PlayingCards : SKSpriteNode
{
    var CardRank:Ranks = Ranks.ace
    var CardSuit:Suits = Suits.Club
    var Visible:Bool = false
    //var Sprite:SKSpriteNode

    init()
    {
        super.init(texture: SKTexture(imageNamed: "b1fv.png"), color: NSColor.blackColor(), size: SKTexture(imageNamed: "b1fv.png").size())
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func updateSprite()
    {
        self.name = Description(Set: 2)

        if self.Visible
        {
            self.texture = SKTexture(imageNamed: generateSpriteFilename())
        } else {
            self.texture = SKTexture(imageNamed: "b1fv.png")
        }
    }

    func generateSpriteFilename() -> String
    {
        return self.Description(Set: 2) + ".png"
    }

    func Description(Set s:Int) -> String
    {
        var cr = ""
        var cs = ""

        switch self.CardRank
        {
        case .ace:
            cr = (s == 1) ? "ace" : "1"
            break
        case .jack:
            cr = (s == 1) ? "jack" : "j"
            break
        case .king:
            cr = (s == 1) ? "king" : "k"
            break
        case .queen:
            cr = (s == 1) ? "queen" : "q"
            break
        default:
            cr = String(self.CardRank.rawValue + 1)
        }

        switch self.CardSuit
        {
        case .Club:
            cs = (s == 1) ? "clubs" : "c"
            break
        case .Diamond:
            cs = (s == 1) ? "diamonds" : "d"
            break
        case .Heart:
            cs = (s == 1) ? "hearts" : "h"
            break
        case .Spade:
            cs = (s == 1) ? "spades" : "s"
            break
        }

        return (s == 1) ? cr + "_of_" + cs : cs + cr
    }
}

Here is how I populate the array:

func prepareDeck() -> [PlayingCards]
    {
        var deck = [PlayingCards]()

        for i in 0...3
        {
            let s:Suits = Suits(rawValue: i)!
            for j in 0...12
            {
                let r:Ranks = Ranks(rawValue: j)!
                let c:PlayingCards = PlayingCards()
                c.CardRank = r
                c.CardSuit = s

                deck.append(c)
            }
        }

        return deck
    }

Solution

  • Th method nodeAtPoint does return you an SKNode.

    Of course we know that node could be more specific but at compile time all we can say is that it will be an SKNode.

    Now, if you believe that the object is something more specific that a simple SKNode (like a PlayingCard) you can try to cast the node to your type.

    override func mouseDown(theEvent: NSEvent) {
        let location = theEvent.locationInNode(self)
        let node = self.nodeAtPoint(location)
    
        if let playingCard = node as? PlayingCard {            
            // here you have your PayingCard object!
            print(playingCard.suit)
        }
    }
    

    In the code above, the body of the IF is entered only if the retrieved node is a PlayingCard. In this case a new constant playingCard having the type of PlayingCard is created. So inside the body of the IF you can use all the methods and properties you defined in your PlayingCard class.