'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 1:[1]
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)")
}
}
Solution 2:[2]
I am sorry but previous answer is simply incomplete. Here is the same, with all the code needed to make it work.
import SwiftUI
import SpriteKit
class GameScene: SKScene {
private var cat1: Cat!
private var cat2: Cat!
func createCats() {
cat1 = Cat(imageNamed: "img1")
cat1.position = CGPoint(x: 64, y: 128)
cat1.color = .magenta
cat1.isUserInteractionEnabled = true
cat1.delegate = self
addChild(cat1)
cat2 = Cat(imageNamed: "img2")
cat2.position = CGPoint(x: 128, y: 128)
cat2.color = .green
cat2.isUserInteractionEnabled = true
cat2.delegate = self
addChild(cat2)
}
override func didMove(to view: SKView) {
createCats()
}
}
extension GameScene: CatDelegate {
func catTouched(cat: Cat) {
print("cat of color \(cat.color)")
}
}
protocol CatDelegate {
func catTouched(cat: Cat)
}
class Cat: SKSpriteNode {
var delegate: CatDelegate!
var isEnabled = true
var sendTouchesToScene = true
var colour: SKColor = .blue
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)
}
}
struct ContentView: View {
var scene: SKScene {
let scene = GameScene()
scene.size = CGSize(width: 256, height: 256)
scene.scaleMode = .fill
scene.backgroundColor = .white
return scene
}
var body: some View {
SpriteView(scene: scene)
.frame(width: 256, height: 256)
.border(Color.red)
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | user3069232 |
| Solution 2 | user3069232 |
