i want to make a parallax effect on my background title screen with mountains moving. The "trick" i want to make is when a mountain goes to the end of the screen, instead of destroying the node and create it again at the begining position, i just want to change it's position to the begining position and loop the rest of actions. For this i create my moutain sprite at the begining of the createSceneContents() function and i pass the sprite to a method animate() wich do allways the same combo of actions forever: animate to the right, then when it's at x position, change mountain.position.x to the begining...
-(void)createSceneContents{
//crear
SKSpriteNode *mountain = [SKSpriteNode spriteNodeWithImageNamed:#"mountain.png"];
mountain.name = #"mountain";
//initial position
mountain.position = CGPointMake(-161.5,15);
//animate
SKAction *animRight = [SKAction moveToX:801.5 duration:4];
SKAction *comboActions = [SKAction repeatActionForever:[SKAction performSelector:#selector(animate:mountain) onTarget:#"mountain"]];
And here i have my method declaration:
- (void) animate:(SKSpriteNode*)mountain
{
// code....
}
My problem is i'm allways having errors passing the SKSpriteNode *mountain in my method and
I'm going crazy trying everithing.
You can avoid performSelector action, using a custom action like this.
SKAction* parallaxMoveAction = [SKAction sequence:#[[SKAction moveToX:801.5 duration:4.0f],[SKAction customActionWithDuration:0 actionBlock:^(SKNode* node, CGFloat elapsedTime){
[node setPosition:CGPointMake(-161.5,15)];
}]]];
If you set custom action duration to 0 it will be performed only once.
On the other hand your perform selector action should look like this:
[SKAction performSelector:#selector(animate:) onTarget:self]
As far as i know you can't pass arguments to it.
Related
I am creating a relatively complicated animation sequence. In it, a certain SKSpriteNode (shark) does two rotations. At the beginning of the animation, it rotates around a certain anchor point ap1, then later rotates around a different anchor point ap2. How should I change anchor points midway through an animation sequence?
Some initial thoughts:
I could change the anchor point outside of SKActions, in the update: loop perhaps.
I could use multiple SKSpriteNodes for the same shark sprite (with their respective anchor points), switching (hiding/showing) the sprite nodes when I need to change the anchor point.
Since changing a sprite's anchor point affects where it's rendered, you will likely need to make some sort of adjustment to prevent the sprite from appearing to suddenly move to a new location. Here's one way to do that:
Create action that changes the anchor point
SKAction *changeAnchorPoint = [SKAction runBlock:^{
[self updatePosition:sprite withAnchorPoint:CGPointMake(0, 0)];
}];
SKAction *rotate = [SKAction rotateByAngle:2*M_PI duration: 2];
Run action to rotate, change the anchor point, and rotate
[sprite runAction:[SKAction sequence:#[rotate,changeAnchorPoint,rotate]] completion:^{
// Restore the anchor point
[self updatePosition:sprite withAnchorPoint:CGPointMake(0.5, 0.5)];
}];
This method adjusts a sprite's position to compensate for the anchor point change
- (void) updatePosition:(SKSpriteNode *)node withAnchorPoint:(CGPoint)anchorPoint
{
CGFloat dx = (anchorPoint.x - node.anchorPoint.x) * node.size.width;
CGFloat dy = (anchorPoint.y - node.anchorPoint.y) * node.size.height;
node.position = CGPointMake(node.position.x+dx, node.position.y+dy);
node.anchorPoint = anchorPoint;
}
As Julio Montoya said, the easiest way to do this is to just "translate" code into an SKAction with the method [SKAction runBlock:myBlock].
I am planning to move batch of sprite randomly accross x- axis from left to right say 0 to 320 and
right to left say 320 to 0 with some Constant duration,
Further each sprite i am placing at random position across x-axis,
but when i create my batch of sprite and apply that skaction on each one
SKAction *moveRight = [SKAction moveToX:320 duration:walkAnim.duration];
SKAction *moveLeft = [SKAction moveToX:0 duration:walkAnim.duration];
after some time the whole batch of sprites is moving in one direction, from left to right and then right to left
i know the problem is with my approach and to moveToX with Constant duration
I need Constant duration in my case, Is there something we have in moveToX like we do have in
[UIView setAnimationBeginsFromCurrentState:YES]
so that i can tackle the issue for batch sprites with random position across x-axis
Note when i give some space to call that action on each sprite, then its working fine, but i need all at one time.
One can get Sample app from here
EDIT
What i need i have updated the code here
but I need all the sprites present on the dice without any Timeinterval with the same actions applied on all of the sprites.
Any suggestion would be appreciated
thanks
Umer
To have a constant speed you will need to calculate the duration for the first move Action based on the current position of your sprite. after doing that first move, you can use your moveRight and moveLeft actions. Here is an example for starting to move Right.
CGFloat durationForFullDistance = walkAnim.duration;
CGFloat fullDistance = 320;
CGFloat firstDistance = fullDistance - sprite.position.x;
CGFloat durationForFirstMove = durationForFullDistance*firstDistance/fullDistance;
SKAction *firstMoveRight = [SKAction moveToX:320 duration:durationForFirstMove];
SKAction *moveRight = [SKAction moveToX:320 duration:durationForFullDistance];
SKAction *moveLeft = [SKAction moveToX:0 duration:durationForFullDistance];
SKAction *continousMove = [SKAction sequence:#[
firstMoveRight,
[SKAction repeatActionForever:[SKAction sequence:#[
moveLeft,
moveRight
]]]
]];
]]
I'm currently trying SpriteKit for first time and using some actions. I was under the impression that every node/sprite you create you would need to remove it but for some reason they are removed by themselfs as soon as they pass the screen height. Is this a normal behavior in SpriteKit?
Here is the code.
I'm basically using an action to animate a sprite across the screen (from bottom to top in protrait-mode), the funny thing is that the node count stays at 0 no matter how many sprites I create, it actually counts them but it subtracts them as soon as they pass the screen height.
-(void) shoot
{
bullet = [SKSpriteNode spriteNodeWithImageNamed:#"bullet"];
bullet.position = CGPointMake(airPlane.position.x, airPlane.position.y + 50);
[self addChild:bullet];
SKAction *moveBullet = [SKAction moveTo:CGPointMake(airPlane.position.x, self.size.height + 20) duration:0.5];
[bullet runAction:moveBullet];
}
If I change my action to where the sprite doesn't cross the screen height then the node count keeps the count.
Change this line...
SKAction *moveBullet = [SKAction moveTo:CGPointMake(airPlane.position.x, self.size.height + 20) duration:0.5];
... to this
SKAction *moveBullet = [SKAction moveTo:CGPointMake(airPlane.position.x, self.size.height) duration:0.5];
Again Is this a normal behavior in SpriteKit?
Thanks a lot
The node count displayed on the lower right corner is just an indication of the nodes which are being rendered on the screen. They are not an indication of the total nodes in the scene.
Use the following code
[skView setValue:#(YES) forKey:#"_showsCulledNodesInNodeCount"];
At the same place where you call
skView.showsFPS = YES;
skView.showsNodeCount = YES;
Also, please have a look at this answer.
that can be a memory leak because sprite kit holds textures in the memory for further use for better result remove it from the screen when its passing out of the screen and set it manually to nil
for eg
[self enumerateChildNodesWithName:name usingBlock:^(SKNode *node, BOOL *stop) {
if(node.x>900)
{
//remove all the action
[node removeAllActions];
//remove node from parent
[node removeFromParent];
//set nil//not required set automatically to nil when node is remove from parent
node=nil;
}
}];
I'm basically trying to code a "find the ball under the cups" game for practice.
So there is 3 cups, and one target to find. All using SKSpriteNode. The target is randomly a child of one cup, and follows rotations as the parent rotate around an SKNode.
Between each game, the program is supposed to show where is the target, by simply animate it up, then down. Here the sequence code :
//THE ANIMATIONS
SKAction *moveUp = [SKAction moveByX:0.0 y:100 duration:1];
SKAction *moveDown = [SKAction moveByX:0.0 y:-100 duration:1];
SKAction *wait = [SKAction waitForDuration:0.5];
_presentTargetSequence = [SKAction sequence:#[moveUp,wait,moveDown]];
And the method using it :
- (void) presentTarget
{
NSLog(#"presentTarget()");
[_target runAction:_presentTargetSequence completion:^{
_canMove = YES;
}];
}
The code works fine, but only the first time, after that, the method is called but never go through [_target runAction ...].
BUT it's working if the target does get in the rotation/swap.
So my question is : is there anything that can make a node ignore it run action method ? NSLog(#"presentTarget()") is called as I said, but not reaction.
I find that every time I'm confused about an action not running, it's because the object running the action is not currently in the node hierarchy for the scene. There is no error for this, it just doesn't run the action. So double check to make sure that the object is added to the current scene at the time you ask it to run the action.
i have a method to create sprites with an initial position, final position, height, image of the sprite and a name property to the sprite (to access the sprite later)
The problem is when i need to debug, because i can't display, for example, a number (of a position in this case...)
In this case, i want to know what sprite is disapearing sooner than expected, so i want to set a new parameter in my method which would be an NSString number(1,2,3..)so i could localize what sprite is making troubles... but i cant display this number or i don't know how. The other debugging technique i thought to do were to put an if(x.position> 600)callAMethod with a label, but... the coordinates are CGPoint and not float. So i'm a bit lost... Do anybody know a technique to do this??
here is my method to create sprites:
-(void)crearMontanaconLaPosicionFinal:(CGFloat)xFinal inicial:(CGFloat)xInicial altura:(CGFloat)altura imagen:(NSString*)imagen nombre:(NSString*)nombre
{
CGPoint posicionFinal = CGPointMake(xFinal, altura);
SKSpriteNode *montaña = [SKSpriteNode spriteNodeWithImageNamed:imagen];
montaña.name = nombre;
SKAction* parallaxMoveAction3 = [SKAction sequence:#[[SKAction moveTo:posicionFinal duration:20],[SKAction customActionWithDuration:0 actionBlock:^(SKNode* montaña, CGFloat elapsedTime){
[montaña setPosition:CGPointMake(xInicial,altura)];
}]]];
montaña.position = CGPointMake(xInicial, altura);
SKAction *repetir=[SKAction repeatActionForever:parallaxMoveAction3];
[montaña runAction:repetir];
[self addChild:montaña];
}