SpriteKit: SKSpriteNode containing another SKSpriteNodes but only one SKPhysicsBody - ios

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.

Related

How to return the SKSpriteNode within given constraints following an event?

Suppose the following:
You have a myriad of SKSpriteNodes in the view.
When the user taps the screen, you want the whatever sprite that is in / near a specific location to do an animation.
Question: How can figure out which SKSpriteNode is at the specific location without looping through all sprites?
For this, I have implemented a SKSpriteNode, box, which is transparent and has a texture which covers the span of the specific location, and is positioned accordingly.
The SKSpriteNode methods contains and intersects seem promising, but require that I pass a point or a sprite respectively.
Question: How can I get a SKSpriteNode to report what sprite, if any, it intersects with? Again, without looping through every sprite. If two sprites intersect with box, then return only that which is most prominently intersecting with box.
Diagram:
This is not my actual use case, but illustrates the point. There are a lot of sprites (more than visualized below) and there is an area of interest that:
if the user touches, and
a sprite is in that area
I want to know what sprite is there.
There is no way to do this without SOMETHING looping through the sprites. That's either:
The physics engine, as Stoneburner suggests
The scene, via update() setting flags on sprites when they're in the
region
Your code that handles the touch, searching for sprites in the region
GameplayKit offers some optimisations on doing this sort of thing: https://developer.apple.com/reference/gameplaykit/gkrtree
Attach a UITapGestureRecognizer to the view
On tap state UIGestureRecognizerStateRecognized get the location of the tap using CGPoint pointInView = [tapper locationInView:mySKScene.view]
Convert from the view's coordinate system to the scene's coordinate system using CGPoint pointInScene = [mySKScene convertPointFromView:pointInView]
Get the node at that point by asking the scene. SKNode *touchedNode = [self nodeAtPoint:pointInScene];
You can use SKPhysicsBodies to detect collisions (overlaps).
Assign physicsbodies to all sknodes, add one dynamically on the region you want to detect sknodes inside, handle the SKPhysicsContactDelegate, remove the body again

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.

How to draw SKNode PhysicsBody for debugging?

I create my SKPhysicsBody with rectangle from size, I set it to size of the node, but the collision detection does not work on good portion of the node.
How do I draw the red frame around physics body?
I tried using precise collision detection, but it doesn't help.
I have checked some debug library but it draws only direct descendants of the scene, and my nodes are few levels deep. Author apparently doesn't know what recursion is.
How do I draw red frame around physics body if my body is rectangle?
This works as of iOS 7.1 and in all OSX versions supporting SpriteKit.
In this example I'm using the variable mySKView to hold the SKView of the game.
Set the "showPhysics" property of your SKView to true/YES. This is usually done by adding the following line after the line "mySKView.showsFPS = true". Usually in the viewcontroller owning the SKView.
Obj-C:
mySKView.showsPhysics = YES;
Swift
mySKView.showsPhysics = true

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

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

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

Resources