Stopping bounce when repeatedly jumping in spritekit - ios

I have a player character and a ground tile. On both physics bodies, I have restitution set to 0.
To jump, I directly set the velocity.dy of the player. When the player comes back to the ground due to gravity, for the most part it works, or seems to.
Problems occur when I repeatedly jump. If I jump right as the player lands, there's some bounce and the height the player reaches on the next bounce doesn't always match the initial bounce.
I've tried various ways to force the velocity.dy to 0 when the user lands, but nothing fixes the weird jumping issue. How can I properly and smoothly have a consistent physics jump?

Honestly, I am not sure what you are trying to accomplish. Normally we shouldn't mess with velocities of objects. In a typical Spritekit game, we must treat it like a "real world" situation and generally apply force or impulse on the object.
I suspect you are trying to make a Mario-like game. All you have to do is apply a big enough gravity to the physicsworld and apply impulse on the sprite to jump (on touchesBegan delegate).
Just now I went ahead and made a simple mario jump scenario in Spritekit. And this is what I ended up with by setting gravity -30 for the y-component and impulse y=100 on the mario sprite. (Frame rate is bad. Looks better on simulator/device)
PhysicsWorld setup:
[self.physicsWorld setGravity:CGVectorMake(0, -30)];
self.physicsWorld.contactDelegate = self;
Mario and platform sprite setup code:
SKSpriteNode *platform = [SKSpriteNode spriteNodeWithImageNamed:#"platform"];
platform.position = CGPointMake(view.frame.size.width/2.0,0);
platform.name = #"platform";
platform.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:platform.frame.size];
platform.physicsBody.categoryBitMask = platformCategory;
platform.physicsBody.dynamic = NO;
platform.physicsBody.usesPreciseCollisionDetection = YES;
platform.physicsBody.affectedByGravity = NO;
[self addChild:platform];
SKSpriteNode *mario = [SKSpriteNode spriteNodeWithImageNamed:#"mario"];
mario.position = CGPointMake(view.frame.size.width/2.0, 400);
mario.name = #"mario";
mario.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:mario.frame.size];
mario.physicsBody.categoryBitMask = marioCategory;
mario.physicsBody.dynamic = YES;
mario.physicsBody.usesPreciseCollisionDetection = YES;
mario.physicsBody.contactTestBitMask = platformCategory;
mario.physicsBody.affectedByGravity = YES;
[self addChild:mario];
touchesBegan:
SKSpriteNode *mario = (SKSpriteNode*)[self childNodeWithName:#"mario"];
[mario.physicsBody applyImpulse:CGVectorMake(0, 100)];

Related

SpriteKit physics giving different results each time

In my game I'm creating some balls which are effected by gravity. When game starts they fall from out of screen and get their places at the bottom. No other force is applied to them.
The problem is I'm creating these balls with exact coordinates, exact physics attributes like previous one but the result is not the same. It is similar but not same. But I think it should be exactly the same because in every time the values are same.
You can understand what I've said in these 3 pictures below.
Do you have any idea why this is happening? How can I solve it?
This is how I create the sprite nodes:
BallSpriteNode *sprite = [BallSpriteNode spriteNodeWithTexture:ballTexture];
sprite.xScale = scale;
sprite.yScale = scale;
sprite.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:sprite.size.width/2];
sprite.physicsBody.density = 1.0f;
sprite.physicsBody.restitution = 0;
sprite.physicsBody.dynamic = YES;
sprite.physicsBody.categoryBitMask = ballHitCategory;
sprite.physicsBody.contactTestBitMask = ballHitCategory;
sprite.physicsBody.collisionBitMask = ballHitCategory;
CGPoint startPosition = CGPointMake(xPosition, yPosition);
sprite.position = startPosition;
[bounceScene addChild:sprite];
There are lots of factors involved in making a physics engine less random.
The FPS greatly affects the randomness. In SKSpriteKit Physics, you have a Update method in the SKScene, which is called once every frame and carry the time difference from the previous frames. Usually to make it less random you have to somehow override the method.
Or use other Physics engine's which are customisable (Bullet Physics). It is very easy to get same result by only adjusting the StepSimulation function call.

SpriteKit SKPhysicsBody applyTorque causes sprite position to change

I have an interesting problem. I create an SKSpriteNode with associated physics body.
SKTexture *shipTexture = [SKTexture textureWithImageNamed:#"Spaceship"];
self.heroSpriteNode = [HeroSpriteNode spriteNodeWithTexture:shipTexture size:CGSizeMake(40, 35.2)];
self.heroSpriteNode.physicsBody = [SKPhysicsBody bodyWithTexture:shipTexture size:CGSizeMake(40, 35.2)];
self.heroSpriteNode.physicsBody.dynamic = YES;
self.heroSpriteNode.physicsBody.mass = 1000.0;
self.heroSpriteNode.physicsBody.affectedByGravity = NO;
self.heroSpriteNode.physicsBody.allowsRotation = YES;
self.heroSpriteNode.physicsBody.angularDamping = 0.5;
self.heroSpriteNode.anchorPoint = CGPointMake(0.5,0.5);
self.heroSpriteNode.physicsBody.categoryBitMask = heroCategory;
self.heroSpriteNode.physicsBody.contactTestBitMask = groundCategory;
self.heroSpriteNode.physicsBody.collisionBitMask = groundCategory | edgeCategory;
The Scene itself has no gravity.
In the update routine I call applyTorque: to the physics body.
-(void) updateWithDeltaTime:(NSTimeInterval)seconds
{
CGFloat rotateTorque = self.rotateRate;
switch (self.rotateForceApplied)
{
case leftForceApplied:
break;
case rightForceApplied:
rotateTorque = -1.0 * rotateTorque;
break;
case zeroForceApplied: // separate from default in case behavior should change
rotateTorque = 0.0;
break;
default:
rotateTorque = 0.0;
break;
}
NSLog(#"before physics position: x %f, y %f", self.heroSpriteNode.position.x, self.heroSpriteNode.position.y);
[self.heroSpriteNode.physicsBody applyTorque:rotateTorque];
NSLog(#"after physics position: x %f, y %f", self.heroSpriteNode.position.x, self.heroSpriteNode.position.y);
}
The sprite rotates and appears to rotate in place. What it is actually doing is rotating around a center point (so no net lateral movement). This is seen by logging the sprite position before and after applying the torque. No other actions are being applied to the physics body. The movement in position is about 9 points in each direction at most.
Because the camera is pinned by constraints to the "hero" sprite, and the world moves with the camera (the centerOnNode: sample code), this causes the whole world to move in a circular pattern as the sprite spins. The world itself does not spin but moves at the same rate in a circular pattern with the spinning.
With the sprite anchorPoint being 0.5, 0.5 I would think it should rotate around a center point which should not change the position of the sprite.
What would I being doing wrong with this?
If it matters this is on iOS9, Xcode7, running on device not in the simulator. (The iOS9 SpriteKit documentation is publicly available on Apple's website, as is a public beta of iOS9 itself so this should not be breaking any NDA, and I don't think anything here is iOS9 specific anyway)
Sprite Kit is a physics engine that attempts to mimic characteristics of objects in the real world, so you should expect an object with a physics body in the simulation to behave as they would in nature. In this case, you are rotating a non-uniform physics body that may rotate non-uniformly and, possibly, move over time (unless the body is rotating about its center of mass). If you change the physics body to a circle, the sprite should rotate uniformly and should remain at a fixed location.

Why is my camera (that follows the player) not working (SpriteKit)?

So, in this game I'm making the player can move left and right using the accelerometer and jump by tapping the screen. Now the player must evade enemies frome the sky and climb up by jumping on de fallen enemies. The player should be able to go up infinitely until he dies, therefore a camera is needed to follow the player on his journey up. I have read some other Stack Overflow posts and consulted Apple's programming guide, but I'm not able to figure it out. This is what I added:
-(void) createSceneContents
{
self.world = [SKNode node];
self.playerSize = self.frame.size.width/7;
self.player = [self.playerData newPlayer:self.playerSize];
self.player.name = #"player";
self.player.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(self.playerSize-5,self.playerSize-5)];
self.player.physicsBody.allowsRotation = NO;
self.player.physicsBody.categoryBitMask = self.playerCategory;
self.player.physicsBody.contactTestBitMask = self.enemyCategory | self.edgeCategory;
self.isTouchingGround = NO;
self.player.position = CGPointMake(CGRectGetMidX(self.frame), 100);
[self.playerData movementSetup];
[self addChild:self.world];
[self.world addChild:self.player];
}
There are more things in this method, but those aren't important for this question.
-(void) didSimulatePhysics
{
self.world.position = CGPointMake(-(self.player.position.x-(self.size.width/2)), -(self.player.position.y-(self.size.height/2)));
}
Then there are a lot of other methods adding other things to the game including enemies, collision handeling etc.
Ok, so when I did this the player movement suddenly stopped working, the players spawn position was changed and the rest of the sprites, that weren't child nodes of world, just kept on going.
Why isn't this working, how can I fix it, and how does it work?
In order to make a camera follow your player add these simple lines of code:
int cameraEdge = 100;
if (self.player.position.y > cameraEdge) {
worldPosition.y = cameraEdge - self.player.position.y;
}
self.world.position = worldPosition;
What this does: Whenever your player's y-position is higher than 100 (or whatever you set it to) the world's position starts following the player. I've tested it and it works perfectly

Sprite Kit - Make SKPhysicsBody with restitution only on sides

I am trying to create a complex SKPhysicsBody that does not bounce on the top, but does allow bouncing on the sides and bottom. Currently I am creating two nodes. One that has the image of the Sprite and a no restitution PhysicsBody on the top.
My second node matches the first node, but is clear with the same size as the first node It just has a PhysicsBody on the front and bottom and has a restitution. to allow my main character to bounce off the bottom and front.
Here is the code for my current setup:
//This is the top part.
- (void)nodePhysicsBodySetup:(SKSpriteNode *)node
{
CGPoint topStart = CGPointMake(0, node.size.height);
CGPoint topEnd = CGPointMake(node.size.width, node.size.height);
SKPhysicsBody *topEdge = [SKPhysicsBody bodyWithEdgeFromPoint:topStart toPoint:topEnd];
node.physicsBody = topEdge;
//[SKPhysicsBody bodyWithBodies:#[topEdge, frontEdge, bottomEdge]]
//node.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:node.frame.size];
node.physicsBody.dynamic = NO;
node.physicsBody.restitution = 0.0;
node.physicsBody.affectedByGravity = NO;
node.physicsBody.categoryBitMask = groundCategory;
node.physicsBody.collisionBitMask = mainHeroCategory;
node.physicsBody.contactTestBitMask = mainHeroCategory;
}
//This is the bottom physicsbody that matches up with the image of the main node.
- (void)bottomNodePhysicsBodySetup:(SKSpriteNode *)node
{
CGPoint topStart = CGPointMake(0, node.size.height);
CGPoint frontEnd = CGPointMake(0,0);
CGPoint bottomEnd = CGPointMake(node.size.width,0);
SKPhysicsBody *frontEdge = [SKPhysicsBody bodyWithEdgeFromPoint:topStart toPoint:frontEnd];
SKPhysicsBody *bottomEdge = [SKPhysicsBody bodyWithEdgeFromPoint:frontEnd toPoint:bottomEnd];
bottomNode.physicsBody = [SKPhysicsBody bodyWithBodies:#[frontEdge, bottomEdge]];
bottomNode.physicsBody.dynamic = NO;
bottomNode.physicsBody.affectedByGravity = NO;
bottomNode.physicsBody.restitution = 0.5;
bottomNode.physicsBody.categoryBitMask = otherCategory;
bottomNode.physicsBody.collisionBitMask = mainHeroCategory;
bottomNode.physicsBody.contactTestBitMask = mainHeroCategory;
}
Picture as screenshots are under NDA:
Currently, the Green PhysicsBody is attached to the node, and the red PhysicsBody is attached to a transparent second node with the same position as the first node.
This does not really work. My main character (mainHeroCategory) gets stuck sometimes on the node corner where the PhysicsBody with restitution and the one without meet. What is a better way to do this?
I would try to do this using only one physics body and look at the contact delegate methods to determine where the contact occurred and what the normal vector is (that information is in the SKPhysicsContact object passed in the delegate method).
Once I got that information I would apply an impulse to the mainHeroCategory object, or not, depending on where the contact occurred.
I suggest you join the physics bodies with an SKPhysicsJointFixed or by constraining the bodies with an SKConstraint or two. SpriteKit only uses the shape when merging two or more bodies with bodyWithBodies. From the docs...
The properties on the children, such as mass or friction, are ignored.
Only the shapes of the child bodies are used.
The restitution property is ignored.

iOS Sprite Kit- SKSpriteNode seems to participate in simulation without physics body

I am working on an iOS game using Sprite Kit.
I recently added an SKSpriteNode into my scene and did NOT create a physics body for it. However, when I build and run, when the player's character(which does have a physics body) moves into the SKSpriteNode, it spins and moves away, like it has a physics body -but I didn't create one for the SKSpriteNode.
Even if I type
sprite.physicsBody = nil;
it still behaves like it's part of the simulation.
I wondered if a physics body is created automatically when you make an SKSpriteNode, but I looked at the documentation and searched it on Google and couldn't find anything to suggest that is the case.
Any help or suggestions would be much appreciated.
This is the code I used to create the sprite (the one that should not be affected by the simulation)
-(void)addSprite
{
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:#"image"];
sprite.position = CGPointMake(40, 30);
sprite.zPosition = 30;
sprite.name = #"spriteName";
[self addChild:sprite];
}
In another part of the project I have
[self addSprite];
to call the method.
Yes every SKSpriteNode comes with a physics body. Its best to change the physics body's properties instead of getting rid of it altogether. If you want your sprite to be on screen and not interact with any other sprite nodes, do the following:
sprite.physicsBody.dynamic = NO;
sprite.physicsBody.collisionBitMask = 0x0;
sprite.physicsBody.contactTestBitMask = 0x0;

Resources