Search code examples
iosswiftsprite-kitskspritenodecoordinate-systems

iOS Swift SpriteKit: How to make a child spritenode's position and movements to be the same as its parent spritenode?


I want to implement a spritenode with a "colored highlight". The "highlight can be of a number of colors. This is done in sandbox app to try things out.

I have considered using SKTextures to represent the "highlight" of the node. However, with this method, I will have to upload a number of "highlight" files which must be "pre-attached" to the host spritenode png file. So, if I have 5 spritenode "host" png files and 5 "highlight" png files, then I will have a total of 5x5=25 png files that represents all the possible combinations. That will be way too many files to prepare and maintained.

So, I rather make the spritenode "host" have a child spritenode which is the highlight.

Both the host and highlight spritenode need to move together and the highlight spritenode is 1 zPosition infront of the "host" spritenode. So, the cgposition of the 2 spritenodes should be the same.

I create a Class file for the host spritenode.

class Host : SKSpriteNode {

    var highlight = SKSpriteNode()
        init(strPieceName : String) { 
        let texture = SKTexture(imageNamed: strPieceName)
        super.init(texture: texture, color: UIColor.clear, size: texture.size())

    }

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

    func addHighlight(withColour strColour : String) {

        highlight = SKSpriteNode(imageNamed: strColour)
        addChild(highlight)
    }        
}

In the GameScene class, I call the addHighlight function in touchesBegan

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

    for t in touches {

        let cgPointTouched = t.location(in: self)
        let skNodeTouched : SKNode = self.atPoint(cgPointTouched)
        switch skNodeTouched.name {
        case "Highlight":
         host.highlight.position = skNodeTouched.position
         host.highlight.anchorPoint = CGPoint(x: 0.5, y: 0.5)
         host.highlight.size = CGSize(width: 0.2, height: 0.2)
         host.highlight.alpha = 1
         host.highlight.zPosition = 3
         addChild(host.highlight)
    }
    }
 }

The problem with this code is that although the highlight spritenode "follows" the host spritenode, the screen appearance of the position of highlight spritenode is not "on top" of the host spritenode. But strangely, the cgposition of the 2 spritenodes are the same.

I think it has something to coordinates systems of the GameScene and the Highlight class not being the same but I cannot figure out how to solve this and why this is happening.


Solution

  • Not sure why you are doing all this complicated stuff, but you are not suppose to touch the position. Leave it at 0,0, and never add it to the scene. Instead leave it on the sprite, and it will always follow your sprite.

    Of course, you can always use colorBlendFactor to blend the sprite color with your texture to create the highlight

    Your class should look like this:

    class Host : SKSpriteNode {
    
        var highlight : SKSpriteNode!
    
        func addHighlight(withColour strColour : String) {
            highlight.removeFromParent()
            highlight = SKSpriteNode(imageNamed: strColour)
            //highlight.anchorPoint = CGPoint(x: 0.5, y: 0.5)
            highlight.size = CGSize(width: 0.2, height: 0.2)
            //highlight.alpha = 1
            highlight.zPosition = 3
    
            highlight.moveToParent(self)
        }        
    
    }
    

    There is no need to add extra inits that do exactly the same thing as other inits

    Your touch code should look like this:

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    
        for t in touches {
    
            let cgPointTouched = t.location(in: self)
            let skNodeTouched : SKNode = self.atPoint(cgPointTouched)
            switch skNodeTouched.name {
                case "Highlight":
                host.addHighlight(withColour:"whatevermycoloris")
            }
        }
     }
    

    Of course, you can always avoid the extra node by just doing:

    class Host : SKSpriteNode {
    
        func addHighlight(withColour : UIColor) {
            color = withColour
            colorBlendFactor = 0.75 //Change this intensity to 1 to add more color
        }        
    
    }
    

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    
        for t in touches {
    
            let cgPointTouched = t.location(in: self)
            let skNodeTouched : SKNode = self.atPoint(cgPointTouched)
            switch skNodeTouched.name {
                case "Highlight":
                host.addHighlight(withColour:UIColor.yellow)
            }
        }
     }