Apple states in https://developer.apple.com/documentation/spritekit/skscenedelegate :
Modifying SpriteKit objects outside of the ordained callbacks (a background queue or anything else non-main-thread) can result in concurrency related problems. Even dispatching work on the main thread asynchronously or at a later time is risky because the closure is likely to be done outside of the timeframe SpriteKit expects. If you're experiencing a segmentation fault or other type of crash occurring deep within the SpriteKit framework, there's a good chance your code is modifying a SpriteKit object outside of the normal callbacks.
I'm using gesture recognizers to interact with my sprite kit objects. A simple example would be to add a SKAction to a node when the user tapped an object:
func tapAction(gr:UITapGestureRecognizer) {
scene.childNode(withName: "balloon")!.run(SKAction.fadeOut(withDuration: 2))
}
Despite the fact that this "just works" for the moment, I'm afraid that this does not work in more complicated cases.
Is there any hint from Apple that this is allowed? Or do I really have to defer the modification of the SpritKit object from the gesture action to an ordained callback?
It looks like you are safe, you are just assigning an action. That is going to run during the normal sprite kit updates
if you were manipulating the actual object, or removing a node, you would come into problems. Let's say you tap to remove a node. This tap happens right before didContactBegin
. didContactBegin
would expect a node, but alas, you removed it, so it will crash.
If you want to feel safe about this, then set up a queue to fire at the beginning of your update.
class GameScene : SKScene
{
public typealias Closure = ()->()
public var processOnUpdate = [Closure]()
override func update(_ currentTime: TimeInterval) {
proceseOnUpdate.forEach{$0()}
processOnUpdate = [Closure]()
....//do other stuff
}
}
//SKView Code
func tapAction(gr:UITapGestureRecognizer) {
scene.processOnUpdate.append(
{
scene.childNode(withName: "balloon")!.run(SKAction.fadeOut(withDuration: 2))
}}
}
My apologies if this does not run the first time, I am not on a Mac now to test this.