Search code examples
swiftsprite-kitevent-handlingskspritenodeskscene

How to properly encapsulate touch event handling in a SKSpriteNode and pass data back to a SKScene


I'm writing a game in SpriteKit that has a joystick to control the player. Previously, I had kept most of the joystick logic in the main GameScene's touchesBegan, touchesMoved, and touchesEnded methods.

Since I have multiple SKScenes, I wanted to abstract this logic out into a SKSpriteNode subclass JoyStick that contains its own touches event handlers. This seems like a good solution as it automatically handles whether or not the touch is "in bounds" of the joystick and allows me to remove logic from the scene itself.

However, I can't find any good resources outlining how to properly pass information back and forth between a SKScene and a SKSpriteNode instance that has implemented touch event handlers. I could pass the JoyStick an instance of the object that I want to modify (like the player sprite) but I'm curious if there is a proper way to pass data back and forth without coupling the joystick to a specific "instance-to-modify".

Additionally, are there any other downsides to implementing touch event handlers in my SKSpriteNode instances, rather than handling everything in the scene's handlers?


Solution

  • I like to handle as much of the code in the object class as possible. So I would handle any of the object touch code inside of it's class file and send the touch back to the scene to be handle separately by the scene if needed, and use delegation to send the information regarding the touch to the scene.

    There are no downsides to removing this code, and several upsides.

    cleaner code faster loading time in xcode if the scenes have less line (my own findings) you don't have to pinpoint the node that the touch is landing on because it is encapsulated in a sublass

    In subclass (cat)

    protocol CatDelegate: class{
        func catTouched(cat: Cat)
    }
    
    class Cat: SKSpriteNode {
    
        weak var delegate: CatDelegate!
    
        var isEnabled = true
        var sendTouchesToScene = true
        var color: SKColor = .white
    
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent!) {
    
             guard isEnabled else { return }
    
             //send touch to scene if you need to handle further touches by the scene
             if sendTouchesToScene {
                 super.touchesBegan(touches, with event)
             }
    
             //handle touches for cat
             delegate?.catTouched(cat: self)
        }
    }
    

    meanwhile in Game Scene...

    class GameScene: SKScene {
    
        private var cat1: Cat!
        private var cat2: Cat!
    
        …
    
        func createCats() {
       
         cat1 = Cat()
            cat1.color = .magenta
            cat1.delegate = self
            addChild(cat1)
        
            cat2 = Cat()
            cat2.color = .green
            cat2.delegate = self
            addChild(cat2)
        }
    }
    
    extension GameScene: CatDelegate {
    
        func catTouched(cat: Cat) {
            print("cat of color \(cat.color)")
        }
    }