runAction:onChildWithName: don't work - objective-c

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.

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];

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 Sprite in a Circle

I am trying to move a Sprite ("Spaceship") in a circle, where the click was performed.
Here is the code that is triggered when a click is performed:
- (void)newSpaceShipAt: (CGPoint)location
{
SKSpriteNode *hull = [[SKSpriteNode alloc]initWithImageNamed:#"Spaceship"];
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddArc(path, NULL, location.x, location.y, self.size.width / 5.0, 0, 360, YES);
SKAction *move = [SKAction sequence:#[
[SKAction followPath:path duration:1],
]];
[hull runAction:[SKAction repeatActionForever:move]];
hull.name = #"Spaceship";
hull.scale = 0.5;
hull.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:MAX(hull.size.width / 2.0, hull.size.height / 2.0)];
hull.physicsBody.dynamic = NO;
[self addChild:hull];
}
I was the Spaceship to move in the circle indefinitely about the point "location", which is where the click was performed. However, what happens is that after one successful revolution, the spaceship moves "location" relative from the click. For example, if the click was performed on (50, 100) then after one revolution it will move to (100, 200), relative to the scene, and it will carry on.
What can I do to fix this problem? Or if I my approach is totally wrong, what would be a better one?
Also, what is the best way to draw a trail (a simple line), to the movement of the sprite?
From the documentation of followPath:duration:...
Discussion
Calling this method is equivalent to calling the
followPath:asOffset:orientToPath:duration: method, passing in YES to
both the offset and orient parameters.
The behaviour of this method will tell the hull to orient to the path and also tell it to use the path as an offset. i.e. it will move 50,100 each time it runs.
Use this method instead...
[SKAction followPath:path asOffset:NO orientToPath:YES duration:1.0];
That should solve your problem.
Also, get rid of that sequence.
Do this...
SKAction *moveAction = [SKAction followPath:path asOffset:NO orientToPath:YES duration:1.0];
[hull runAction:[SKAction repeatActionForever:moveAction]];

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:.