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
]]]
]];
]]
Related
I am making a game and I want an enemy to move in a circle but the enemy should have constant movement to the left as well. I have tried to create a circle path with CGPath and make it follow that path and then added a SKAction with constant left movement. But it seems that the node is just following the CGPath without left movement.
Is there any other way to make this possible?
This is my code at the moment:
CGMutablePathRef circle = CGPathCreateMutable();
CGPathAddArc(circle, NULL, 0, 0, 80, 0, 2*M_PI, true);
CGPathCloseSubpath(circle);
SKAction *followTrack = [SKAction followPath:circle asOffset:NO orientToPath:NO duration:1.5];
SKAction *forever = [SKAction repeatActionForever:followTrack];
[enemy runAction:[SKAction moveByX:-1000 y:0 duration:3.0]];
[enemy runAction:forever];
You can't move the same node with 2 move actions. What you can do is place the node in a container node. Then move the container node left while its child moves in a circle.
Another option is to not use SKActions and use something more dynamic and real-time by computing the centripetal velocity manually and shifting the centripetal point overtime. You can see an example of this in my answer here.
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 don't really know how to explain what i'm after, so I drew some (very) artistic diagrams to help convey the idea. I'll also try and explain it the best I can.
I'm essentially trying to 'shoot' bullets/lasers/whatever from a circle in the center of the screen, and for it to repeat this at a rather rapid rate. Here are two pictures which kind of show what i'm trying to achieve: (Don't have enough reputation to post them here.
(1) http://i.imgur.com/WpZlTQ7.png
This is kind of where I want the bullets to shoot from, and how many I'd like.
(2) http://i.imgur.com/psdIjZG.png
This is pretty much the end result, I'd like them to repeatedly fire and make the screen kind of look like this.
Can anyone refer me to what I should be looking at in order to achieve this?
When dealing with circles, it's usually easier to use polar coordinates. In this case, each direction can be represented with a magnitude and an angle, where the magnitude is the amount of the force/impulse to apply to the bullet and angle is the direction to shoot the bullet.
The basic steps are
Determine the number of directions
Determine the angle increment by dividing 2*PI (360 degrees) by the number of directions
Start at angle = 0
Shoot bullet in the direction specified by angle
Convert the angle and magnitude to cartesian coordinates to form a vector
Apply an impulse or a force to the bullet using the vector
Increment angle by angle increment
Here's an example of how to do that in Obj-C:
#implementation GameScene {
CGFloat angle;
SKTexture *texture;
CGFloat magnitude;
CGFloat angleIncr;
}
-(void)didMoveToView:(SKView *)view {
/* Setup your scene here */
long numAngles = 15;
magnitude = 1;
angleIncr = 2 * M_PI / numAngles;
angle = 0;
texture = [SKTexture textureWithImageNamed:#"Spaceship"];
SKAction *shootBullet = [SKAction runBlock:^{
SKSpriteNode *bullet = [SKSpriteNode spriteNodeWithTexture:texture];
bullet.size = CGSizeMake(8, 8);
bullet.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:bullet.size.width/2];
bullet.physicsBody.affectedByGravity = NO;
bullet.position = self.view.center;
bullet.zRotation = angle-M_PI_2;
[self addChild:bullet];
CGFloat dx = magnitude * cos(angle);
CGFloat dy = magnitude * sin(angle);
CGVector vector = CGVectorMake(dx,dy);
[bullet.physicsBody applyImpulse:vector];
angle = fmod(angle+angleIncr,2*M_PI);
}];
SKAction *wait = [SKAction waitForDuration:0.25];
SKAction *shootBullets = [SKAction sequence:#[shootBullet, wait]];
[self runAction:[SKAction repeatActionForever:shootBullets]];
}
From what I understand, you want this sprite to shoot a bunch of projectiles every X seconds.
SKAction *ShootProjectiles = [SKAction runBlock:^{
//Create Projectile1
projectile1.physicsBody.applyImpulse(CGVectorMake(1, 0)); //Shoot directly right
//Create Projectile2
projectile2.physicsBody.applyImpulse(CGVectorMake(1, 1)); //Shoot diagnally Up and to the right
//Follow this pattern to create all projectiles desired to be shot in one burst
}];
SKAction *delayBetweenShots = [SKAction scaleBy:0 duration:5];
SKAction* ShootSequence= [SKAction repeatActionForever:[SKAction sequence:#[ShootProjectiles, delayBetweenShots]]];
[self runAction: ShootSequnce];
What this does is create as many projectiles as you desire and fire them in the direction of the vectors you define. It then waits 5 seconds (the scaleBy:0 action does nothing except delay) and then repeats over and over again until you remove the action.
How would I set the angle of a sprite to 45 degrees?
SKAction *rotate = [SKAction rotateByAngle: M_PI/4.0 duration:1];
only increases the angle by 45 degrees, what I want to do is have to SKSprite rotate however long it takes to get to 45 and then stop. Is there a method for that or will I have to hard code it?
Thanks!
The method you’re looking for is +rotateToAngle:duration:shortestUnitArc:, as in:
SKAction *rotate = [SKAction rotateToAngle:M_PI_4 duration:1 shortestUnitArc:YES];
You can also just use +rotateToAngle:duration:, but that always rotates counterclockwise; this variant goes in whichever direction requires the least rotation.
(also note that π/4 is already defined as a constant, M_PI_4; see usr/include/math.h)
I have a trouble trying to make one circle big and small using [SKAction scaleBy: duration:]
SKAction *scaleDown = [SKAction scaleBy:0.2 duration:1.8];
SKAction *scaleUp= [scaleDown reversedAction];
SKAction *fullScale = [SKAction sequence:#[scaleDown, scaleUp, scaleDown, scaleUp]];
[_circleChanging runAction:fullScale];
What I get is the circle becoming so small that disappears and then doesn't come back. It has to become small and then come back to his original size doing it 2 times.
Try:
SKAction *scaleDown = [SKAction scaleTo:0.2 duration:0.75];
SKAction *scaleUp= [SKAction scaleTo:1.0 duration:0.75];
SKAction *fullScale = [SKAction repeatActionForever:[SKAction sequence:#[scaleDown, scaleUp, scaleDown, scaleUp]]];
[_circleChanging runAction:fullScale];
Not all actions are reversible, and the reverse sometimes doesn't mean "go back to the original value".
If you check the documentation, the reverse action of scaleBy is actually scaling to -0.2 in your case. Just create a new scale action instead of reversing.
Also try making a copy of the actions for the 2nd use:
SKAction *fullScale = [SKAction sequence:
#[scaleDown, scaleUp, [scaleDown copy], [scaleUp copy]]];