Move a skspritenode while changing its texture - objective-c

So. A (hopefully) simple question. I have a spritenode in a game I am making. I want the character to animate its legs while it is moving. I can get it to animate its legs then move, on key press. But I cant get it to do both at the same time.
Here is my code (naturally I also have the other 3 directions laid out prior.):
‘’’
case 0x7C /* LEFT */:
TextureChangeIceMan1 = [SKAction animateWithTextures:[IcedMan returnMovementArrayWithKey: #"0x7C"] timePerFrame:0.1];
MoveIceMan1 = [SKAction moveByX:[IcedMan returnCharacterSpeed] y:0.0 duration:0.4];
break;
default:
NSLog(#"keyDown:'%#' keyCode: 0x%02X", theEvent.characters, theEvent.keyCode);
break;
}
TextureChangeIceMan1 = [SKAction repeatActionForever: TextureChangeIceMan1]; //make it unending
[IceMan1 runAction: MoveIceMan1];
[IceMan1 runAction: TextureChangeIceMan1];
‘’’

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 texture change according to sprite x-axis direction

This seems like a simple problem to solve, yet I can't. As the title suggests, I need my sprite to change to a different texture depending on which way it's going - left or right. Here's what I tried doing:
if (self.sprite.position.x > 0) //also tried "self.sprite.position.x" instead of 0.
{
[self.sprite setTexture:[SKTexture textureWithImageNamed:#"X"]];
}
if (self.sprite.position.x < 0)
{
[self.sprite setTexture:[SKTexture textureWithImageNamed:#"XX"]];
}
Neither worked (Except when the sprite's x-axis == 0 -_-).
I've read this: SpriteKit: Change texture based on direction
It didn't help.
EDIT:
Pardon, I forgot to mention the movement of the sprite is completely random in 2D space. I use arc4random() for that. I don't need any texture changes when it's going up or down. But left & right are essential.
I must be writing something wrong:
if (self.sprite.physicsBody.velocity.dx > 0)
if (self.sprite.physicsBody.velocity.dx < 0)
isn't working for me. Neither is
if (self.sprite.physicsBody.velocity.dx > 5)
if (self.sprite.physicsBody.velocity.dx < -5)
Just to be clear about this sprite's movement one more time: it is randomly moving with the help of the
-(void)update:(CFTimeInterval)currentTime
method. This is achieved like so:
-(void)update:(CFTimeInterval)currentTime
{
/* Called before each frame is rendered */
if (!spritehMoving)
{
CGFloat fX = [self getRandomNumberBetweenMin:-5 andMax:5];
CGFloat fY = [self getRandomNumberBetweenMin:-5 andMax:7];
int waitDuration = arc4random() %3;
SKAction *moveXTo = [SKAction moveBy:CGVectorMake(fX, fY) duration:1.0];
SKAction *wait = [SKAction waitForDuration:waitDuration];
SKAction *sequence = [SKAction sequence:#[moveXTo, wait]];
[self.sprite runAction:sequence];
}
}
You could just check to see if fX is greater or lower than 0, meaning the 'force' on the x-axis is either positive (right movement) or negative (left movement).
Here is a modified code that should do the trick-
BTW- I've also set the SpritehMoving BOOL at the start and at the end of the movement, otherwise, this method will be called even if the sprite is moving (which there is nothing wrong with it, but it seems to beat the purpose of checking the BOOL value.
if (!spritehMoving)
{
spritehMoving = YES;
CGFloat fX = [self getRandomNumberBetweenMin:-5 andMax:5];
CGFloat fY = [self getRandomNumberBetweenMin:-5 andMax:7];
int waitDuration = arc4random() %3;
SKAction *changeTexture;
if(fX > 0) { // Sprite will move to the right
changeTexture = [SKAction setTexture:[SKTexture textureWithImageNamed:#"RightTexture.png"]];
} else if (fX < 0) { // Sprite will move to the left
changeTexture = [SKAction setTexture:[SKTexture textureWithImageNamed:#"LeftTexture.png"]];
} else {
// Here I'm creating an action that doesn't do anything, so in case
// fX == 0 the texture won't change direction, and the app won't crash
// because of nil action
changeTexture = [SKAction runBlock:(dispatch_block_t)^(){}];
}
SKAction *moveXTo = [SKAction moveBy:CGVectorMake(fX, fY) duration:1.0];
SKAction *wait = [SKAction waitForDuration:waitDuration];
SKAction *completion = [SKAction runBlock:(dispatch_block_t)^(){ SpritehMoving = NO; }];
SKAction *sequence = [SKAction sequence:#[changeTexture, moveXTo, wait, completion]];
[self.sprite runAction:sequence];
}

SpriteKit SKSpriteNode slide

I'm trying determine why slide is happened. I have created 2 nodes, and one of them is moving using SKAction. But when one of them above on other, it does not moving with node under them.
It's a little bit hard to explain, so I have created the video:
https://www.youtube.com/watch?v=F4OuTBVd5sM&feature=youtube_gdata_player
Thanks for your replies!
Parts of code:
CGPoint positionPoint = [valuePoint CGPointValue];
SKSpriteNode *slab = [SKSpriteNode spriteNodeWithTexture:plitaTexture];
slab.name = #"plita";
slab.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:plitaTexture.size];
slab.position = positionPoint;
slab.physicsBody.dynamic = NO;
CGFloat duration = 3.0;
CGFloat firstDuration = duration/(self.size.width/slab.position.x);
SKAction *moveLeftFirstTime = [SKAction moveTo:CGPointMake(0.0 + plitaTexture.size.width/2, positionPoint.y) duration:firstDuration];
SKAction *moveLeft = [SKAction moveTo:CGPointMake(0.0 + plitaTexture.size.width/2, positionPoint.y) duration:5];
SKAction *moveRight = [SKAction moveTo:CGPointMake(self.size.width - plitaTexture.size.width/2, positionPoint.y) duration:5];
SKAction *movingRightLeft = [SKAction sequence:#[moveRight, moveLeft]];
SKAction *moving = [SKAction sequence:#[moveLeftFirstTime,[SKAction repeatActionForever:movingRightLeft]]];
[slab runAction:moving];
[self addChild:slab];
Rabbit init code:
-(instancetype)init {
if (self = [super initWithImageNamed:#"eating-rabbit1"]) {
self.physicsBody = [SKPhysicsBody bodyWithTexture:self.texture size:self.texture.size];
self.physicsBody.allowsRotation = NO;
self.physicsBody.dynamic = YES;
self.physicsBody.friction = 1;
}
return self;
}
The main character is sliding because you are moving the slab by changing its position over time. Imagine you are standing on top of a train that is moving, friction will cause you to move along in the direction of the train. However, if the train magically moved from point A to point B by disappearing and reappearing 1 cm at a time, you will remain at point A. That is what's happening to your main character.
To correct this issue, you will need to move the slabs using physics. Here is an example of how to do that:
slab.physicsBody.affectedByGravity = NO;
slab.physicsBody.dynamic = YES;
// These will help prevent the slab from twisting due to the weight of the main character
slab.physicsBody.angularDamping = 1.0;
slab.physicsBody.mass = 1e6;
// Air resistance will slow slab's movement, set the damping to 0
slab.physicsBody.linearDamping = 0;
Define a set of SKActions that will move the slabs left and right. Adjust the speed and duration variables to achieve the desired motion.
SKAction *moveLeft = [SKAction customActionWithDuration:0 actionBlock:^(SKNode *node, CGFloat elapsedTime){
node.physicsBody.velocity = CGVectorMake(-speed, 0);
}];
SKAction *moveRight = [SKAction customActionWithDuration:0 actionBlock:^(SKNode *node, CGFloat elapsedTime){
node.physicsBody.velocity = CGVectorMake(speed, 0);
}];
SKAction *wait = [SKAction waitForDuration:duration];
SKAction *action = [SKAction sequence:#[moveRight, wait, moveLeft, wait]];
[slab runAction:[SKAction repeatActionForever:action]];
You need to set the slab's physicsBody to be dynamic.
slab.physicsBody.dynamic = YES;
slab.physicsBody.affectedByGravity = NO;
A non-dynamic physicsBody is rendered static and ignores any forces and impulses applied on it. Look up the property in the documentation here.

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.