Sprite Kit. Follow Node Position (Objective C) - 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];

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.

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.

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

How to set horizontal back and forth movements on a sprite?

I am writing a game where i'm spawning some sprites that move on the same horizontal plane, i.e., their y-coordinate is always the same. I'm not sure physics, but rather only SKActions.
Initially I wrote this to spawn the sprite at location Y, and move to a point off the screen in a randomly chosen direction. Now, I'd like this sprite to instead turn around and go backwards until it hits the opposite end of the screen, where I wan it to again turn around, etc.
Initially I have the action set up as such :
SKAction * actionMove = [SKAction moveTo:destination duration:duration];
where destination and duration are basically randomly generated. This obviously moves the sprites in one direction only.
what's an elegant way to I make this an un-ending loop of sprites turning around and repeating their path over and over again?
I figured out how to do this with action sequences. I initially tried setting this up in the
update
method of the scene, and that made everything terribly slow, as expected. So I set up the following sequence of actions (I have the sprites moving back and forth at the same y-coordinate, so they just need to turn around the continue their journey)
CGPoint destination1, destination2;
if (sprite.direction == LeftDirection)
{
destination1 =CGPointMake(-sprite.size.width/2, sprite.position.y);
destination2 = CGPointMake(self.frame.size.width + sprite.size.width/2, sprite.position.y);
}else {
destination1 =CGPointMake(self.frame.size.width + sprite.size.width/2
, sprite.position.y);
destination2 = CGPointMake(-sprite.size.width/2, sprite.position.y);
}
int duration = [sprite getDuration]; //this generates a random duration amount
// Create the actions
SKAction * actionMove1 = [SKAction moveTo:destination1 duration:duration];
//turn the sprite around
SKAction * rotateAction = [SKAction runBlock:^{
sprite.xScale = sprite.xScale * -1;
sprite.direction = (sprite.direction == LeftDirection) ? RightDirection : LeftDirection;
}];
//reverse the movement
SKAction * actionMove2 = [SKAction moveTo:destination2 duration:duration];
SKAction * actionMove = [SKAction sequence:#[actionMove1, rotateAction, actionMove2, rotateAction]];
SKAction * repeat = [SKAction repeatActionForever:actionMove];
once this is created, we run the repeat action on the sprite. And voila! continuous movement that changes direction.