SKAction runAction does not execute completion block
From the SKAction Reference:
An SKAction object is an action that is executed by a node in the
scene (SKScene)...When the scene processes its nodes, actions associated with those
nodes are evaluated.
In other words, the actions for a node is run if and only if that node is in the scene. By calling removeFromParent
, you remove the node from the scene, the runBlock
action is never called (since the node is no longer in the scene), and thus the sequence is never finished. Since the sequence doesn't finish, the completion block doesn't get called.
I would suggest moving the removeFromParent
call to the completion block for safety's sake. Something like this feels safer:
for position in listOfPositions {
let theSprite: SKSpriteNode = theGrid[position]
/// the SKSpriteNode referenced by theSprite :
/// - belongs to an array of SKSpriteNode: theGrid
/// - belongs to a SKNode: theGameLayer
///
/// In other words this SKSpriteNode is referenced twice
///
let theActions = SKAction.sequence([
/// Some actions here
/// ...
/// Remove theSprite from the Grid
/// - position is an instance of a structure of my own
/// - theGrid is accessed via a subscript
///
SKAction.runBlock({self.theGrid[position] = nil})
])
theSprite.runAction(theActions) {
/// remove theSprite from it's parent
/// Might need to weakly reference self here
theSprite.removeFromParent(),
NSLog("Deleted")
}
}
TL;DR
The sequence doesn't complete, so the completion block for sequence doesn't get called.
Code inside SKAction run action not executing
Actions will not fire for a node until it is placed on the scene, you have a chicken and egg dilemma going on here. You want to add the node (egg) to the scene (chicken) after the node (egg) exists in the world (chicken gives birth to the same egg). You need to have somethings else place the node on the scene, then the node will be able to run the actions.
Place your start action on your scene, and not your node, and it should start running
SKAction not running on SKSpriteNode
myNode
is a computed property. self.addChild(myNode)
adds a node, but there is no myNode
stored property.
myNode.run
First computes a new node without adding it to the scene. Then it calls the run the action on it. but since it's a different node that is not on the scene, it will never run.
Change your myNode
defintion to:
var myNode : SKShapeNode!
and in didMove(to view:
add:
myNode = SKShapeNode(rectOf: snake.componentSize)
myNode.position = snake.head
myNode.fillColor = .red
Run action only on parent sknode
SpriteKit does not work this way. If your children should not be affected by the parent's movement, perhaps you should reconsider making them children.
However, there is a fun workaround: run an action on the children that counteracts the parent's movement. For example, if you run an action on the parent that moves to the left, run an action on all the children that moves them to the right simultaneously.
SKNode's action run completion block does not get called
Can I answer my own question? I guess I can? Here goes:
I finally solved this. It turns out that the WKInterfaceScene
in WatchKit has ALSO an isPaused
property that you need to turn false
sometimes. So now in willActivate
of my InterfaceController I will also check that and turn it false
if it is true
. Since I made this change, I haven't seen a single hiccup, freeze or anything weird anymore.
Case closed, I guess. I leave this here for future generations who might face this issue.
iOS SpriteKit SKAction completion call not working/creating odd results
It does not seem like you understand how runAction is executed.
Using
[node runAction:moveUp completion:^{
NSLog(@"RUNNING MOVE DOWN");
[node setHidden: NO];
[node runAction: moveDown];
}];
will not just define a completion to be done at the end of the action, but it runs the action and then calls the completion, so the very first [node runAction:moveUp] needs to be removed.
Secondly, two runAction calls that are made in one function/block will be called simultaneously, and since you call [node runAction: moveUp], [node runAction:moveUp completion:] , and [node runAction:moveDown completion:] all in the same block, they will all be executed simultaneously.
This is the code you are looking for:
SKAction *moveUp = [SKAction moveTo: shipIntroSpot1 duration:3.0];
SKAction *moveDown = [SKAction moveTo:shipSpot1 duration:ship1MovementSpeed];
[self enumerateChildNodesWithName:@"ship1" usingBlock:^(SKNode *node, BOOL *stop) {
NSLog(@"RUNNING MOVE UP");
[node runAction:moveUp completion:^{
NSLog(@"RUNNING MOVE DOWN");
[node setHidden: NO];
[node runAction:moveDown completion:^{
NSLog(@"STARTING MOVEMENT");
}];
}];
Related Topics
Convert Integer to Roman Numeral String in Swift
Diddiscoverservices Is Not Being Called After a Ble Connection
Swift Deleting Table View Cell When Timer Expires
Persist Accessibility Permissions Between Builds in Xcode 13
New Value Is Only Available in Sendasynchronousrequest - Swift
How Could I Request Text from a Website in Swift
Store Data in Custom Class Array in Core Data
Code Only Retrieving One Value from Data in Firebase
Swift 3:Delegate Within Tapgesturerecognizer in Generics Doesn't Get Called
Swiftui - Navigation View Opening with Back Button and Half Grey Screen/Weird Behavior
Swift: Call Self Method Inside Init
iOS 13 Swiftui: App Crashes Upon Launch on Real Device
Generic Class with Class-Bound Constraint Cannot Be Parametrized by Class-Bound Protocol
How to Have Either or Wherefields for Firestore Query
C++ Dylib in Swift Project - Undefined Symbols for Function Exposed in Dylib