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
Related
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)];
I have a problem in pausing a sprite kit game with physics. The game contains a ball which moves in the SpriteScene and has the following parameters:
self.ball.physicsBody.friction = 0;
self.ball.physicsBody.linearDamping = 0;
self.ball.physicsBody.restitution = 1.0f;
self.ball.physicsBody.affectedByGravity = NO;
self.ball.physicsBody.usesPreciseCollisionDetection = YES;
The problem is that when I pause the game, I call these methods:
self.scene.physicsWorld.speed = 0;
self.ball_velocity = self.ball.physicsBody.velocity;
self.ball.physicsBody.velocity = CGVectorMake(0, 0);
self.ball.speed = 0;
self.ball.physicsBody.dynamic = NO;
[self.scene.view setPaused:YES];
and when resume, call these:
self.scene.physicsWorld.speed = 1;
self.ball.physicsBody.velocity = self.ball_velocity;
self.ball.physicsBody.dynamic = YES;
self.ball.speed = 1;
[self.scene.view setPaused:NO];
This stops the ball animation, but when resume, the ball position is changed and it seems if that was moving during the pause duration.
BTW, it works fine on iOS 8 but on iOS 9 it always fails.
Any suggestions ?!!
After chatting, we have come to the conclusion that between iOS8 and iOS9, Apple has done a change that pausing the scene now pauses the update loop. Since the update loop is being paused, the change in time is not being calculated correctly. What is now happening, is the change in time will be the time at unpause - the time at pause, simulating a lag state. The velocity will take the math into effect, and move objects based on this difference in time. To combat this issue, just make a parent node that will house all of your scenes's objects, and pause the parent. This will allow the update to still be called, thus allowing the change in time to stay consistent with the frame rate.
I wanted to mix 2 tutorials, namely:
http://hub.ae/blog/2014/03/26/soft-body-physics-jellyusing-spritekit/
and
https://developer.apple.com/library/ios/documentation/GraphicsAnimation/Conceptual/SpriteKit_PG/Actions/Actions.html
(Example: Centering the Scene on a Node part)
I got both working one by one, but I've a bug when I mix both.
If my code starts like this:
[self setAnchorPoint:CGPointMake(0.5f, 0.5f)];
_myWorld = [SKNode node];
[self addChild:_myWorld];
[self createPlayer]; // I replaced all [self addchild's to [_myWorld addChild]
then camera follows the player but there are no joints created!
If I put [self createPlayer] to the top and leave all [self addchilds] as they are (because _myWorld isn't created yet) then there are joints which are working perfectly but camera doesn't follow player, since player isn't in _myWorld.
I leave [self.physicsWorld addJoint:joint]; as it is all the time.
Any idea where I go wrong?
When I moved the parts into _myWorld as told in the Apple guide, I didn't know I also had to shift anchor points, as they are always in scene coordinates.
Adding these lines solved the problem:
CGPoint p1 = [self.scene convertPoint:CGPointMake(point1.position.x+self.frame.size.width/2, point1.position.y+self.frame.size.height/2) fromNode:_myWorld];
CGPoint p2 = [self.scene convertPoint:CGPointMake(point2.position.x+self.frame.size.width/2, point2.position.y+self.frame.size.height/2) fromNode:_myWorld];
in the AttachPoint function.
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;
I'm using collision detection in Sprite Kit. It is working and preventing my sprites from crossing paths. However, I'm not getting notifications in didBeginContact: and I don't seem to have any control over how the physics engine responds when a collision occurs.
I have various cars (SKSpriteNodes) moving around following paths using the SKAction followPath:asOffset:orientToPath:duration:
Previously, if two cars crossed paths they would both just continue as normal with one driving over the other. To implement collision detection I have made the following changes...
Added this to my #interface:
<SKPhysicsContactDelegate>
Added this to my #implementation:
static const uint32_t carCategory = 0x1 << 0;
Added this in my init method:
self.physicsWorld.contactDelegate = self;
self.physicsWorld.gravity = CGVectorMake(0.0, 0.0);
I create my blue car:
- (void)addBlueCar
{
_blueCar = [SKSpriteNode spriteNodeWithImageNamed:#"Blue Car.png"];
_blueCar.position = CGPointMake(818.0, -50.0);
_blueCar.name = #"car";
[self addChild:_blueCar];
CGSize contactSize = CGSizeMake(_blueCar.size.width - 5.0, _blueCar.size.height - 5.0);
_blueCar.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:contactSize];
_blueCar.physicsBody.categoryBitMask = carCategory;
_blueCar.physicsBody.collisionBitMask = carCategory;
_blueCar.physicsBody.contactTestBitMask = carCategory;
}
And I also create a red car:
- (void)addRedCar
{
_redCar = [SKSpriteNode spriteNodeWithImageNamed:#"Red Car.png"];
_redCar = CGPointMake(818.0, -50.0);
_redCar = #"car";
[self addChild: _redCar];
CGSize contactSize = CGSizeMake(_blueCar.size.width - 5.0, _blueCar.size.height - 5.0);
_redCar.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:contactSize];
_redCar.physicsBody.categoryBitMask = carCategory;
_redCar.physicsBody.collisionBitMask = carCategory;
_redCar.physicsBody.contactTestBitMask = carCategory;
}
There are other cars as well, but I'm just using these two until I figure out what the problem is. I want to be notified of any collision between any two cars. That's why I'm just using a single category, carCategory.
The first problem is that I get no notifications. I have this yet it never logs anything to the console:
- (void)didBeginContact:(SKPhysicsContact *)contact
{
NSLog(#"Contact");
}
The second problem is that when two cars cross paths they start nudging each other off course. This is ok, but I don't seem to able to manage any aspects of how the collision is handled. The bizarre thing is that nothing changes when I do this to both of my car creation methods:
- (void)addBlueCar
{
_blueCar = [SKSpriteNode spriteNodeWithImageNamed:#"Blue Car.png"];
_blueCar.position = CGPointMake(818.0, -50.0);
_blueCar.name = #"car";
[self addChild:_blueCar];
CGSize contactSize = CGSizeMake(_blueCar.size.width - 5.0, _blueCar.size.height - 5.0);
_blueCar.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:contactSize];
// _blueCar.physicsBody.categoryBitMask = carCategory;
// _blueCar.physicsBody.collisionBitMask = carCategory;
// _blueCar.physicsBody.contactTestBitMask = carCategory;
}
Collision detection still works the same with those three lines commented out for both cars. This just doesn't seem right to me. Collision detection only ceases when I also comment out this line:
_blueCar.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:contactSize];
So my main question is: why is the contactTest not reporting anything? My second question is: why is collision detection happening when I don't assign a categoryBitMask, collisionBitMask or contactTestBitMask to any car?
collisionBitMask's default value is 0xFFFFFFFF (all bits set). Therefore the node will collide with each physicBody on the scene.
Just a small observation, perhaps you should setup your nodes and set the contact delegate in the scene's didMoveToView method.
If that doesn't solve it, any chance you can post some working code to github for debugging?
Something that helped me with this: Collisions and contacts are different things.
Collision is handled by the physics engine and setting collisionBitMask will determine which bodies collide and interact with each other in the physics world. By default, all your bodies will collide with each other, which kinda makes sense.
Contacts are events- when bodies make contact, will they create a notification that you can use in the delegate method to do more interesting things. If you have a ball you want to hit with a paddle, you want them to collide at a minimum. But if you have a ball moving through a cloud, you probably don't want them to collide in the physics world, but you might want to register the contact.