Search code examples
iosswiftsprite-kitgameplay-kit

How to create a SKSpriteNode with Custom GKComponent?


So im currently working on a game with spriteKit and gameplayKit. I've created a custom GKComponent class that I'm giving to certain SKSpriteNode via the scene inspector in Xcode (image). Now, when I want to re-create random SKSpritenodes with the same GKComponent, it doesn't seems to work because the overridden update method of my custom GKComponent is not working. Any clues?

I've tried to give the node a new entity object (since it was returning nil) and I gave that entity my custom GKComponent but nothing is working

// In scene                
let node = SKSpriteNode(texture: "coin", color: UIColor.clear, size: CGSize(width: 60, height: 60))
let entities: GKEntity! = GKEntity()
let component: CollectableComponent = CollectableComponent()
component.itemType = 1

node.anchorPoint = CGPoint(x: 0.5, y: 0.5)
node.zPosition = 6
node.setScale(0.5)
node.size = CGSize(width: 60, height: 60)
node.position = CGPoint(x: 90, y: 90)
entities.addComponent(component)
node.entity = entities
self.scene?.addChild(node)


// GKComponent

class CustomComponent: GKComponent {
    var node: SKSpriteNode?

    @GKInspectable var itemType: Int = 0
    @GKInspectable var isStatic: Bool = false

    override init() {
        super.init()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override func update(deltaTime seconds: TimeInterval) {
        if node == nil {
            if let nodeComponent = self.entity?.component(ofType: GKSKNodeComponent.self){
                node = nodeComponent.node as? SKSpriteNode
            }
        }
    }
}

Desired behaviour: if the nodeComponent variable in CustomComponent is not nil, I would like to make it run an animation like this:

node = nodeComponent.node as? SKSpriteNode; node.run(SKAction.moveBy(x: 0, y: 15, duration: 0.3));

but with the code, the action would not be running


Solution

  • This is the way you want to be doing this.

    Take note that you need to retain your entities, so a global variable is needed.

    Also, when you add a node to GKSKNodeComponent, the node.entity variable is set automatically.

    class GameScene : SKScene{
        var entities: [GKEntity] = [GKEntity]()
    
        func doSomething(){
    
    
            let node = SKSpriteNode(texture: "coin", color: UIColor.clear, size: CGSize(width: 60, height: 60))
            node.anchorPoint = CGPoint(x: 0.5, y: 0.5)
    node.zPosition = 6
            node.setScale(0.5) //<--- YUCK!!!!!
            node.size = CGSize(width: 60, height: 60)
            node.position = CGPoint(x: 90, y: 90)
            self.scene?.addChild(node)
    
            let entity = GKEntity()
            let nodeComponent : GKSKNodeComponent = GKSKNodeComponent(node:node)
            let component: CollectableComponent = CollectableComponent()
            component.itemType = 1
            entity.addComponent(nodeComponent)
            entity.addComponent(component)
            entities.append(entity)
    
        }
    }
    
    // GKComponent
    
    class CustomComponent: GKComponent {
        //We want this line to crash if node is empty, so do not use ?
        lazy var node: SKSpriteNode! = {return self.entity!.component(ofType: GKSKNodeComponent.self).node as? SKSpriteNode! }
    
        @GKInspectable var itemType: Int = 0
        @GKInspectable var isStatic: Bool = false
    
        override init() {
            super.init()
        }
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
        }
    
        override func update(deltaTime seconds: TimeInterval) {
    
        }
    }