Does SpriteKit apply physics if I modify the position and rotation of a SKSpriteNode directly without an SKAction? - ios

For example if I want to do my own custom animation and move an SKSpriteNode every frame programmatically by x += 10, will Sprite Kit still apply physics correctly or must I always use SKAction?

Manually moving a node with a physics body is possible regardless of how or when you do it. But in any case it's not recommended since it can adversely affect the physics simulation. The node (view) could be out of sync with the position of the body for 1 frame, and you might move the body into a collision which the physics engine will forcibly resolve, causing jumps, jitter, exploding velocities or skipping collisions.
When you use physics, stick to moving the physics body through force, impulse or changing the body velocity directly.

No, you don't have to use SKAction to move SKSpriteNode. This approach works fine for me:
- (void)update:(CFTimeInterval)currentTime {
myNode.position = CGPointMake(myNode.position.x + 0.1,myNode.position.y);
}
All physics work as expected

Related

How to reduce the impact of gravity on a single SKSpriteNode

I am building an endless runner game where platforms appear at random intervals and then scroll right to left across the screen.
I have a sprite that jumps up vertically using
- (void)makeCharacterJump {
[self.spriteCaveman.physicsBody applyImpulse:CGVectorMake(0.0f, 80.0f)];
}
The problem is that the effect of gravity on the sprite means that it falls quite quickly and cant make the gap between the platforms.
What I would like to do is slightly slow down the effect of gravity on the falling sprite so it creates the impression of slightly floating down.
Any ideas?
If the character is the only node affected by gravity then you can change the scene’s gravity with:
self.physicsWorld.gravity = CGVectorMake(0, desiredGravity);
If it is not then you’ll have to play with the character’s physics body properties: friction, linearDamping or angularDamping values.
Hope that helps.

Can I move a node across the screen without SKAction?

I am developing the iPhone version of an Android game using SpriteKit and objective-c. The Android version was developed using libGDX. There the movement across the screen was done by updating the coordinate values whenever the predefined render method is called. While reading the docs in SpriteKit, I found that here the nodes are moved across the screen by using SKAction where the end coordinates and the duration is given and the action is applied to the node. Is this the only way the node can be moved? Or can I update the coordinates(in the update method according to the time interval) even after the adding the node to the parent node(without calling the SKAction anywhere)?
There are a couple of ways to make a node move.
Changing the node's coordinates myNode.position = CGPointMake(myNode.position.x+1, myNode.position.y); will move the node to the right. This method works with regardless on whether they have a physics body or not.
Apply velocity to the node's physics body myNode.physicsBody.velocity = CGVectorMake(100, myNode.physicsBody.velocity.dy); moves the node to the right. Your node requires a physics body for this to work.
Apply an impulse [myNode.physicsBody applyImpulse:CGVectorMake(100, myNode.physicsBody.velocity.dy)]; moves the node to the right. Your node requires a physics body for this to work.
Apply a force [myNode.physicsBody applyForce:CGVectorMake(100, myNode.physicsBody.velocity.dy)]; moves the node to the right. Your node requires a physics body for this to work.
When you apply force is like a motor pushing all the time. If you apply impulse it's like kicking a ball once.
Read up on each method in more detail in the SKPhysicsBody reference.
You can move a node by specifying the position property (see the SKNode
documentation)
node.position = CGPointMake(x, y);

SpriteKit Pinball Flipper Behavior

I'm trying to build a pinball game with spritekit. The ball is affected by gravity and reacts properly, but I can't figure out a way to move the flippers so that they affect the ball's physics. Right now I'm rotating the flippers around an anchor with an SKAction like so
//set anchor point to left edge for rotation
theLeftFlipper.anchorPoint = CGPointMake(0,0.5);
//rotate (done in touchesBegan)
SKAction *rotateLeft = [SKAction rotateByAngle: -1.5f duration:0.1];
[leftFlipper runAction:rotateLeft]
I'm using this to toggle the flipper as up or down around a pivot on its edge, as would be expected with a normal pinball machine's flippers. There are two problems with this. First, the physicsBody of the flipper isn't actually moved to match up with the new anchor of the flipper, so the physics body isn't in the same location as the rendered flipper. Second, when rotating with an action the ball's position is affected but not it's velocity so hitting the ball with the flipper doesn't shoot it upwwards. Is there a better way to achieve this?
Based on the little bit of code you posted, I suggest the following:
If you haven't already, add skView.showsPhysics = YES; in your project's View Controller viewDidLoad. This will allow you to see the physics bodies of your objects.
If the physics body for your flipper is not right, then use CGMutablePathRef to create a custom physics body or you can use this utility.
Make sure you have collisions setup correctly for your flipper and the ball.
Set the ball's and flipper's physics body restitution property to a higher level. This will create the more of a bounce when the two object collide.

Emitter not rotating with parent node

Code --> http://pastebin.com/W3DMYjXa
I am tinkering with SpriteKit and I cannot seem to get an emitter node to rotate with my spaceship the way I would like.
I watched the Apple Tech Talks Session where they described making a spaceship and using an emitter for the thrust exhaust, but they lock the ship to only face one direction.
In my experiment I am trying to allow the player sprite (spaceship) to travel in any direction, I have rotation and scrolling working for the player, but the particle emitter doesn't seem to rotate with the player.
my hierarchy looks something like this
Scene
-->World Node
-->Player Node
---->Player Sprite
---->Emitter Node
My theory is that If I rotate (Player Node) it should rotate both of its children, and it does rotate them, but the emitter continues to emit in the same direction.
I can change the emission angle manually, but it seems needlessly complicated.
here is what I am using to rotate
-(void)rotatePlayerToDirection:(TTDirection)direction {
CGFloat radDir;
CGFloat emiDir;
scrollDirection = direction;
switch (direction) {
case TTUp:
radDir = 0;
emiDir = 3.142;
break;
case TTRight:
radDir = 4.712;
emiDir = 1.571;
break;
case TTDown:
radDir = 3.142;
emiDir = 0;
break;
case TTLeft:
radDir = 1.571;
emiDir = 4.712;
break;
default:
break;
}
SKAction *rotatePlayer = [SKAction rotateToAngle:radDir duration:0.1 shortestUnitArc:YES];
[playerNode runAction:rotatePlayer];
}
Am I missing something here?
Here is a video of this in action
http://youtu.be/NGZdlB9-X_o
I'm fairly certain there is a bug in spritekit to do with the targetNode and rotation. The other answer seems to suggest this is expected behavior, but that ignores that the docs explicitly give this situation as a motivation for the existence of targetNode.
Working with Other Node Types
What you really want is for the particles to be spawned, but thereafter be
independent of the emitter node. When the emitter node is
rotated, new particles get the new orientation, and old
particles maintain their old orientation.
It then gives an example using targetEmitter on how to achieve this. However, the example code does not work.
It seems that setting targetNode breaks particle rotation.
UPDATE: I found a workaround.
Spritekit is failing to adjust the SKEmitterNode.emissionAngle property when SKEmitterNode.targetNode is set. You can work around this by setting it manually after actions have been processed.
Assuming that your SKEmitterNode has only one parent, you can do something like this in your scene.
- (void)didEvaluateActions
{
// A spaceship with a thrust emitter as child node.
SKEmitterNode *thrust = (SKEmitterNode *)[self.spaceship childNodeWithName:#"thrust"];
thrust.emissionAngle = self.spaceship.zRotation + STARTING_ANGLE;
}
Note that if there are multiple ancestor nodes which may be rotated, you will need to loop through them and add all of their zRotations together.
I know this is an old question, and the OP has probably found a solution or workaround, but I thought I would add by two pence worth, in case it helps anybody else.
I am just starting out with SpriteKit and Swift, and am developing a game where nodes fly around and collide with each other, changing direction of travel frequently.
I add an emitter to each node, and initially found that as the node rotated and changed direction, the emission 'trail' remained fixed. I then read that the emitter should have its target node set not as the node it is a child of, but the scene they both exist in.
That at least made the particle trail twist and turn realistically rather than just spurt out in one direction.
But the nodes can spin and changes direction, and I wanted the particles to trail away from the node in the opposite direction to its travel - just like a smoke trail from a plane or rocket.
As the node may rotate, setting the emitters angle of emission to the z-rotation of the node does not work. So...
I track all onscreen nodes in a dictionary, removing them from the scene as they travel off the screen.
So in didSimulatePhysics I use the same dictionary to get the node and calculate the angle of travel from the velocity vector, and then set the emitters emission angle accordingly :
import Darwin
// in your scene class...
override func didSimulatePhysics() {
removeOffScreenParticles()
for (key,particle) in particles {
let v = particle.physicsBody!.velocity
particle.trail!.emissionAngle = atan2(v.dy, v.dx) - CGFloat(M_PI)
}
}
I am aware that this calculation and adjustment is being performed for each on screen node, in every frame, so will probably use my frame counter to only do this check every n'th frame if performance becomes an issue.
I hope this helps someone!
You are setting the emitter's targetNode property to the background. Note that this forces the emitter's particles to be rendered by the background node, and they will be treated as if the background was their parent. So, the particles aren't changing the angle, because the rotation of the background does not change.
Remove [emitter setTargetNode:backgroundNode] and the emitter's emission angle should be rotating correctly along with the player.
If you're looking for a 'middle ground', where the angle rotates along with the player, while the already-rendered particles aren't 'stuck' to the emitter, try this:
First, do not set the targetNode on the emitter (it defaults to nil).
Second, just as you are about to run the rotation action, temporarily set the targetNode to another node, and reset it back to nil when it completes (so that the particles can rotate along):
emitter.targetNode = backgroundNode;
[playerNode runAction:rotatePlayer completion:^{
emitter.targetNode = nil;
}];
Note that the results are also 'middle ground' - the emitter 'puffs' as the target node changes (the previous particles are removed, and new ones are rendered by the new target node). Hope that helps.
Other than that, there's no really straightforward way of achieving this - setting the emission angle manually might be the simplest way, after all. Here's one way to do it, right before running the rotation (the emitter target node is still nil, also, there's no need to hardcode emission angles):
float originalAngle = emitter.emissionAngle;
emitter.emissionAngle = emitter.emissionAngle - radDir;
[playerNode runAction:rotatePlayer completion:^{
emitter.emissionAngle = originalAngle;
}];

SpriteKit: SKSpriteNode containing another SKSpriteNodes but only one SKPhysicsBody

I have a character in my game, it's an SKSpriteNode with few child SKSpriteNodes so I can animate various parts of my character (hands, feet etc.), it also has 1 SKSpriteNode (tried replacing with SKNode, but it was the same) with SKPhysicsBody for a body.
When I add the character to my layer in a scene it just hang in the position and the sprite with the body falls down.
My question and problem is: How can I keep all the child sprites in my main character sprite - how can I keep my character together?
Thanks for any ideas!
EDIT: How can I keep the child sprite with the body attached to my container sprite?
Re EDIT:
Use an SKNode to control all of your character's body parts, including the main body:
SKNode (controller)
SKSpriteNode (head)
SKSpriteNode (body)
SKSpriteNode (leg1)
SKSpriteNode (leg2)
That gives you more flexibility.
To make the head the "master" position, this should do the trick:
-(void) didSimulatePhysics
{
self.parent.position = [self convertPoint:self.position toNode:self.parent.parent];
self.position = CGPointZero;
}
The body is falling because its responding to gravity, as expected. The Other body part, if they do not have physics bodies will stay where you place them.
Some options :
1.
If you want all body parts to be physics body then I suggest looking at a joints, ie a pin joint - which will work like a shoulder joint for example.
Here is an example of pin joints for making a car with wheels.
2.
If you just want it not move, for whatever reason. On your physics body, just set this.
bodyNode.physicsBody.dynamic = NO;
If I am understanding you right, you can start off by saying the gravity on the SKScene is 0, by doing
self.physicsWorld.gravity = CGVectorMake(0,0);
This will ensure that nothing causes the character of your screen to fall down into the bottom from the gravity enforced on the screen.
Possible solution: remove SKPhysicBody from child node and add it to parent node.

Resources