Run SKActions sequence with different nodes - objective-c

I know that I can create an SKAction.sequence which will run the actions of one node one by one. But how can I do that if I want to do a sequence with different nodes. I'd like to do something like that:
Run action from node A
wait 2 seconds
Run action from node B.

If both nodes are uniquely named and are children of the same node, you can use runAction:onChildWithName:, as follows:
SKAction *action = [SKAction sequence:
#[[SKAction runAction:[SKAction moveTo:CGPointMake(200.0f, 200.0f) duration:1.0f]
onChildWithName:#"NODEA"],
[SKAction waitForDuration:2.0f],
[SKAction runAction:[SKAction moveTo:CGPointMake(200.0f, 200.0f) duration:1.0f]
onChildWithName:#"NODEB"]]];
[parent runAction:action];
More generally, you can use runBlock: to do pretty much anything as a step in an SKAction sequence:
SKAction *action = [SKAction sequence:
#[[SKAction runBlock:^{
[nodeA runAction:[SKAction moveTo:CGPointMake(200.0f, 200.0f) duration:1.0f]];
}],
[SKAction waitForDuration:2.0f],
[SKAction runBlock:^{
[nodeB runAction:[SKAction moveTo:CGPointMake(200.0f, 200.0f) duration:1.0f]];
}]]];
[parent runAction:action];

Related

SpriteKit Docs in obj-C, conversion, and applying conversion to specific node in Game

As much as Stack is great source I spend some time reading the Objective C docs for SpriteKit. I wanted to create a run block of actions that manipulate different "Characters" SKSprite Nodes in a particular scene, "For example if you pick a particular character, and you x number of rounds, with this character, the character will disappear over time. Would this particular behavior need to be defined, in both GameScene Class, and HUD Scene? I saw this example in the Docs and this was my attempt to convert from OjC to Swift...
SKAction *moveUp = [SKAction moveByX:0 y:100.0 duration:1.0];
SKAction *zoom = [SKAction scaleTo:2.0 duration:0.25];
SKAction *wait = [SKAction waitForDuration: 0.5];
SKAction *fadeAway = [SKAction fadeOutWithDuration:0.25];
SKAction *removeNode = [SKAction removeFromParent];
SKAction *sequence = [SKAction sequence:#[moveUp, zoom, wait, fadeAway, removeNode]];
[node runAction: sequence];
Then Swift:
var moveUp: SKAction = SKAction.moveByX(0, y: 100.0, duration: 1.0)
var zoom: SKAction = SKAction.scaleTo(2.0, duration: 0.25)
var wait: SKAction = SKAction.waitForDuration(0.5)
var fadeAway: SKAction = SKAction.fadeOutWithDuration(0.25)
var removeNode: SKAction = SKAction.removeFromParent()
var sequence: SKAction = SKAction.sequence([moveUp, zoom, wait, fadeAway, removeNode])
node.runAction(sequence)
This is just a test scenario, yet I am little confused on how to apply to specific node. Would this be called in touches began section where one would break out touch events for that particular node. Any feed back would be greatly appreciated.

Sprite Kit. Follow Node Position (Objective C)

Is there a way for a sprite node to follow another sprite node with an SKAction? i am using this but it doesn't seem to fully catch up with my rocket.
move = [SKAction moveTo:_rocket.position duration:1.0];
[beams runAction:move];
I have a rocket firing a laser beam at another rocket, and i want the beam to follow the rocket when it fires, but also slowly catch up to it so it hits it. The code above does follow it but stays slightly behind it.
Here is the code for my two movements for the rockets
SKAction *moveRight = [SKAction moveByX: 100.0 y:0 duration:2];
SKAction *sequence = [SKAction sequence:#[moveRight]];
SKAction *endless = [SKAction repeatActionForever:sequence];
[spaceShip runAction:endless withKey:#"rocketKey"];
Second Rocket
SKAction *moveShip = [SKAction moveByX:150.0f y:0 duration:2.00f];
SKAction *sequence = [SKAction sequence:#[moveShip]];
SKAction *repeat = [SKAction repeatActionForever:sequence];
[spaceShipL runAction:repeat];
I've tested it without the SKAction and it would work fine but i need both of the rockets to be always moving.
There's a few ways to achieve this that I know of.
You could use some kind of pathfinding.
You could add your laser beam as a child to rocket2, and move it relative to that rocket. (You would need some extra calculations to ensure that if rocket2 should move towards the laser, the laser won't slow down/move backwards)
Best solution, regularly get the position of rocket2 and have the laser follow it. Rather than using moveTo, use applyForce. This won't be bound by time, and so should be able to catch up to your rocket:
SKSpriteNode *laser = [SKSpriteNode spriteNodeWithImageNamed:#"laser"];
laser.physicsBody = [SKPhysicsBody bodyWithTexture: laser.texture size: laser.texture.size];
static const CGFloat thrust = 0.10;
CGVector thrustVector = CGVectorMake(thrust*cosf(laserDirection),
thrust*sinf(laserDirection));
[laser.physicsBody applyForce:thrustVector];

runAction:onChildWithName: don't work

I'm learning some Thing About SKAction class and I'm trying to use the method runAction:onChildWithName:, for this I'm trying this code:
SKSpriteNode *sprite0 = [SKSpriteNode spriteNodeWithImageNamed:#"sprite1.png"];
sprite0.position = CGPointMake(0, 0);
sprite0.name = #"spriteMegamen";
[self addChild:sprite0];
SKAction *action2 = [SKAction moveTo:CGPointMake(120, 120) duration:5];
[SKAction runAction:action2 onChildWithName:#"spriteMegamen"];
But the code onChildWithName don't recognize the name of my sprite and doesn't run the action, why?
That method is another constructor method for SKAction. That is, all it does is create another action that you can run on a node.
So you should have...
SKAction *runOnChildAction = [SKAction runAction:action2 onChildWithName:#"SpriteMegamen"];
[self runAction:runOnChildAction];
Just a note.
I found all this out purely by reading the documentation for SKAction. You should always be consulting the documentation. It is what it is there for and it is the best way to learn.

Spritekit how to run a method for a certain amount of time, then start another one

I'm making a game and i'm currently stumped on how to have a method run for a certain amount of time, turn off, then to have another method start running. Currently i have:
[self spawn];
when the scene sets up. Here is the spawn method:
-(void)spawn {
int xMin = 0;
int xMax = 460;
CGPoint startPoint = CGPointMake(xMin + arc4random_uniform(xMax - xMin),320);
[self performSelector:#selector(spawn) withObject:nil afterDelay:.20];
SKSpriteNode *blue = [SKSpriteNode spriteNodeWithImageNamed:#"whitecircle"];
blue.position = CGPointMake(startPoint.x,startPoint.y);
blue.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:21.5];
blue.physicsBody.usesPreciseCollisionDetection = NO;
blue.physicsBody.categoryBitMask = gainCategory;
blue.physicsBody.contactTestBitMask = playerCategory;
blue.physicsBody.dynamic = NO;
[self addChild:blue];
SKAction *runBlue = [SKAction moveToY:0 duration:1.5];
SKAction *remove = [SKAction removeFromParent];
[blue runAction:[SKAction sequence:#[runBlue,remove]]];
}
What i want to know is how can i make this method run for a certain amount of time, then how can i start another method after this one finishes. Thanks
I think this is what you are looking to do :
// set duration to seconds you want to wait between spawns
float duration = 1;
SKAction *actionWait = [SKAction waitForDuration:duration];
SKAction *actionSpawn = [SKAction runBlock:^(void)
{
[self spawn];
}];
SKAction *actionSequence = [SKAction sequence:#[actionWait, actionSpawn]];
SKAction *actionRepeat = [SKAction repeatActionForever:actionSequence];
[self runAction:actionRepeat];
It's worthwhile to give the Class Reference for SKAction a good reading at least once, so you know it's capabilities.
SKAction Class Reference

Moving node with Sprite Kit

I would like to run two actions at exactly same time. I would like to move object in X way and Y way at the same time. I am trying running actions like this:
sprite.position = CGPointMake(300, 300);
SKAction *action3 = [SKAction moveTo:CGPointMake(sprite.position.x,100) duration:0.5];
SKAction *action2 = [SKAction moveTo:CGPointMake(50,300) duration:1];
SKAction *group = [SKAction group:#[action2,action3]];
[sprite runAction:group];
But this is making first action3 and then action2. What i am trying to make is that object(node) is moving up and down on Y coordinate and at the same time node have to move by X coordinate.
It sounds like you're wanting to basically have it bounce vertically and move in the X direction while doing that, if I'm understanding correctly.
If so, this should work:
SKAction *up = [SKAction moveByX:0 y: 100 duration:1];
SKAction *down = [SKAction moveByX:0 y:-100 duration:1];
SKAction *action1 = [SKAction repeatActionForever:[SKAction sequence:#[up, down]]];
SKAction *action2 = [SKAction moveByX:200 y:0 duration:5];
SKAction *group = [SKAction group:#[action1, action2]];
SKAction *action3 = [SKAction moveTo:CGPointMake(sprite.position.x,100) duration:1];
SKAction *action2 = [SKAction moveTo:CGPointMake(50, 300) duration:1];
The problem is that you can't run two move actions simultaneously. One will override the other's behavior, so effectively only one action's results will show up on screen.
You have to ask yourself what your goal here is, how exactly should the sprite move? Assuming you want to move the sprite on the X axis to point 50 (because the other action leave's the X coord unchanged) but where should it go on the Y axis, 100 or 300? The sprite can't do both at the same time.
If you want a more complex movement behavior, such as a continuous up/down movement while moving horizontally, you'll have to use moveToX: and moveToY: independently and time them differently. For example:
SKAction *moveLeft = [SKAction moveToX:50 duration:4];
SKAction *moveUp = [SKAction moveToY:300 duration:2];
SKAction *moveDown = [SKAction moveToY:100 duration:2];
[sprite runAction:moveLeft];
[sprite runAction:[SKAction sequence:#[moveUp, moveDown]]];
The sprite takes 4 seconds to move to the left, while it takes 2 seconds to move up to 200 and another 2 seconds to move down to 100. At the end it will be at 50,100.
Btw using a group action is equivalent to simply calling runAction: once for each action in the group.
Use group: method, not sequence:.