Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it safe to change `SKNode`s in a gesture recognizer action method?

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?

like image 570
Klaas Avatar asked Oct 17 '25 00:10

Klaas


1 Answers

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.

like image 155
Knight0fDragon Avatar answered Oct 18 '25 15:10

Knight0fDragon



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!