I want to move two (or more) SKSpriteNode
s in sync. Any difference will show. I tried to trigger the SKAction
for each sprite in order and when the last one is finished it triggers a new move. But it turns out that the actions doesn't end in the same order they are started, which causes a slightly time difference that is noticeable.
Is there a way to run parallel SKActions
on two or more sprites with a duration so that they end at exactly the same time or at least in the order they are started?
Here is an principle example of what is not working:
- (void)testMethod1{
SKSpriteNode *child_1=[arrayWithSprites objectAtIndex:1];
SKSpriteNode *child_2=[arrayWithSprites objectAtIndex:2];
//This doesn't work.
[child_1 runAction:[SKAction moveToX:20.0 duration:0.5]];
[child_2 runAction:[SKAction moveToX:20.0 duration:0.5]
completion:^{[self testMethod1];}];
//Actions might not be finished in the order they are started.
}
And here is a way I haven't tried yet but wonder if it might solve my problem:
- (void)testMethod2{
SKSpriteNode *child_1=[arrayWithSprites objectAtIndex:1];
SKSpriteNode *child_2=[arrayWithSprites objectAtIndex:2];
//Will this guarantee total syncronisation?
[self runAction:[SKAction group:[NSArray arrayWithObjects:
[SKAction runBlock:^{[child_1 runAction:[SKAction moveToX:20.0 duration:0.5]];}],
[SKAction runBlock:^{[child_2 runAction:[SKAction moveToX:20.0 duration:0.5]];}],
nil]]
completion:^{[self testMethod2];}];
}
I hope my English and thoughts are understandable.
//Micke....
Just add a container SKNode
, and then they will both move at once:
SKNode *containerNode = [[SKNode alloc] init];
[containerNode addChild:node1];
[containerNode addChild:node2]; //add as many as necessary
[containerNode runAction:someAction]; //declare the action you want them both to perform
The solution by Tyler works perfectly.
But if you can't or don't want to do that, you should know that very likely actions are multithreaded and thus they can finish in any order, but they should still finish in the same frame.
To ensure they both ran to completion before running testMethod, you should perform the code in didEvaluateActions. Then you need to find a way to figure out whether both actions have finished. One way is to use a key.
[child_1 runAction:[SKAction moveToX:20.0 duration:0.5] withKey:@"action1"];
[child_2 runAction:[SKAction moveToX:20.0 duration:0.5] withKey:@"action2"];
Then check if both of these actions have run to completion by checking if they still exist:
-(void) didEvaluateActions
{
if ([child_1 actionForKey:@"action1"] == nil &&
[child_2 actionForKey:@"action2"] == nil)
{
// both actions have ended, start new ones here ...
}
}
Alternatively you can use a completion block that both actions run. Increase a NSUInteger counter variable and in the block increase the counter by 1, then test if the counter's value is equal to (or greater) than the number of concurrent actions. If it is, you know that both actions have run to completion:
__block NSUInteger counter = 0;
void (^synchBlock)(void) = ^{
counter++;
if (counter == 2)
{
[self testMethod1];
}
};
[child_1 runAction:[SKAction moveToX:20.0 duration:0.5] completion:synchBlock];
[child_2 runAction:[SKAction moveToX:20.0 duration:0.5] completion:synchBlock];
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With