Set the angle of a sprite in SpriteKit - ios

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)

Related

How to get correct zRotation during [SKAction rotateBy:duration:]

I'm running a rotateBy action that's repeated forever for a node, and occasionally I want to get the node's current rotation. But zRotation always return 0. How can I get the correct zRotation for this node?
Edit: so finally what I'm doing is creating a custom rotation action instead of using Sprite Kit's default action. A lot more complicated and hard to extend but it does what I want and can be encapsulated in each node's own class.
I believe the reason why you are getting 0 is because you are using animation. The properties of nodes undergoing animation are not updated at each step in the simulation. You will need to manually rotate the node in the update method or create a physics body and set the angular velocity. Using real-time motion will allow you to constantly monitor the properties of you nodes at each step in the simulation.
You can see an example of simulating rotation using real-time motion (albeit more complex) in my answer here.
If you paste the following into a new project, you should see the zRotation property change over time. With an 180 degree/second rotational rate and 60 FPS, the zRotation changes by ~3 degrees per update.
#implementation GameScene {
SKSpriteNode *sprite;
}
-(void)didMoveToView:(SKView *)view {
self.scaleMode = SKSceneScaleModeResizeFill;
sprite = [SKSpriteNode spriteNodeWithImageNamed:#"Spaceship"];
sprite.xScale = 0.5;
sprite.yScale = 0.5;
sprite.position = CGPointMake (CGRectGetMidX(view.frame),CGRectGetMidY(view.frame));
SKAction *action = [SKAction rotateByAngle:M_PI duration:1];
[sprite runAction:[SKAction repeatActionForever:action]];
[self addChild:sprite];
}
-(void)didEvaluateActions {
// Wrap at 360 degrees
CGFloat angle = fmod(sprite.zRotation*180/M_PI, 360.0);
NSLog(#"%g",angle);
}
#end

SKAction to Change AnchorPoint?

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

Shooting objects in a circular pattern SpriteKit

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.

Inverted texture caused from SKAction following a CGPath

"orientToPath:YES" is causing the texture of an SKSpriteNode to be rotated 180 degrees while it follows the path. I still want the node's zRotation to adjust while following the path, but I want the texture to be facing its original direction.
SKAction *path = [SKAction followPath:cgpath asOffset:NO orientToPath:YES duration:5];
How can I fix this?
Setting your sprite node's yScale to -1 will flip the texture vertically. Your sprite should then be oriented correctly along the path.

SpriteKit MoveToX:duration from Current Stat

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

Resources