SpriteKit - Objective C - camera Constraints - ios

I am currently working a spriteKit 2D game using Objective C. It's a tile based game similar to superMario. I am having a problem with my Camera.
I added a camera that is following my player, but when the player gets closer to the edges of the scene, the camera goes out of bounds. Setting up the camera was easy.
-(void)didMoveToView:(SKView *)view {
//add Camera
mainCamera = [SKCameraNode node];
self.camera = mainCamera;
}
then in :
-(void)update:(NSTimeInterval)currentTime {
[super update: currentTime];
mainCamera.position = avatar.position;
}
However i need to add Constraints so that the camera stays within the container of the scene.

Add a physics body to the camera, make it a rectangle the size of the scene.
Since you are doing your update of the camera in the beginning of the update, the physics engine will move the camera prior to being drawn.
make sure you set the restitution to 0 to avoid bouncing

Related

Spritekit - Collision approach when touch begins

I'm a beginner with Swift and SpriteKit and I am working on a basic game. I have created a Sprite game character (Sapceman) using 2 SKTexture images. When the screen is touched the Sprite image changes from one texture image to another(Jet Pack Fires) and the Sprite is moved around the screen. I want to set up a collision detection (with an Alien Spite) that removes them from the screen but only when the screen is touched and second texture image is displayed and only for the bottom part of the sprite (i.e. when the person touches the screen and the JetPack is fired I want the fire part to "kill" the alien). Appreciate if anyone can offer any suggestions on this
Try this code
static const uint32_t fireBallCategory = 0x1;
static const uint32_t alienCategory = 0x1 << 1;
In adding fire ball and alien method add this,
fireBall.physicsBody.categoryBitMask = fireBallCategory;
alien.physicsBody.categoryBitMask = alienCategory;
fireBall.physicsBody.contactTestBitMask = alienCategory;
By doing this you will get notified when fireball and aliens meet each other. So after this you have to add logic to hide alien in delegate method. So first set self as contact delegate. Add this line in -(id)initWithSize:(CGSize)size method.
self.physicsWorld.contactDelegate = self;
Implement the contact delegate as follows,
-(void)didBeginContact:(SKPhysicsContact *)contact
{
SKPhysicsBody *notFireBall;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask){
notFireBall = contact.bodyB;
} else {
notFireBall = contact.bodyA;
}
[notFireBall removeFromParent];
}

Set permanent SKSpritenode image orientation

Hi I have a bunch of round SKSpriteNodes with a circle physical body. Now when these balls roll down a path I want some of these SKSpritenodes image to stay upright even when rolling. So think of an arrow pointing upwards. When the ball starts rolling the arrow spins in circles. But for some balls Id like the arrow to remain pointing up even when the ball rolls. Whats the best way of doing this?
Edit
So an answer was given but from testing it turns out it is not the correct one. Not allowing the ball to rotate affects the way it rolls down the path. So I guess what I want is rotation to be on but the image to always appear to the user like its not rotating. Thanks.
This looks like a job for SupermSKConstraint. Constraints are evaluated and applied after the physics simulation runs on each frame, so you can use them for tasks like making a node point a certain direction regardless of what physics does to it. For this, you'd want a zRotation constraint.
But there's a bit more to it than that. If you set a zero-rotation constraint on the ball:
// Swift
let constraint = SKConstraint.zRotation(SKRange(constantValue: 0))
ball.constraints = [constraint]
You'll find that SpriteKit resets the physics body's transform every frame due to the constraint, so it only sort-of behaves like it's rolling. Probably not what you want. (To get a better idea what's going on here, try adding a zero-rotation constraint to a rectangular physics body in a world without gravity, applying an angular impulse to it, and watching it try to spin in a view with showsPhysics turned on. You'll see the sprite and its physics body get out of sync and shake a bit -- probably due to accumulated rounding errors as the physics engine and the constraint engine fight it out.)
Instead, you can do a bit of what's in 0x141E's answer, but use constraints to make it less code (and run more efficiently):
Give the ball node a circular physics body. (And possibly no texture, if the only art you want for the ball is a non-rotating sprite.)
Add the arrow node as a child of the ball node. (It doesn't need its own physics body.)
Put a zero-rotation constraint on the arrow.
Wait, that doesn't work -- I told the arrow to not rotate, but it's still spinning?! Remember that child nodes are positioned (and rotated and scaled) relative to their parent node. So the arrow isn't spinning relative to the ball, but the ball is spinning. Don't worry, you can still solve this with a constraint:
Tell the constraint to operate relative to the node containing the ball (probably the scene).
Now the constraint will keep the arrow in place while allowing the ball to rotate however the physics simulation wants it to.
Here's some test code to illustrate:
// Step 1: A rectangular spinner so we can see the rotation
// more easily than with a ball
let spinner = SKSpriteNode(color: SKColor.redColor(), size: CGSize(width: 300, height: 20))
spinner.position.x = scene.frame.midX
spinner.position.y = scene.frame.midY
spinner.physicsBody = SKPhysicsBody(rectangleOfSize: spinner.size)
scene.addChild(spinner)
spinner.physicsBody?.applyAngularImpulse(0.1) // wheeeeee
// Step 2: Make the arrow a child of the spinner
let arrow = SKSpriteNode(color: SKColor.greenColor(), size: CGSize(width: 20, height: 50))
spinner.addChild(arrow)
// Step 3: Constrain the arrow's rotation...
let constraint = SKConstraint.zRotation(SKRange(constantValue: 0))
arrow.constraints = [constraint]
// Step 4: ...relative to the scene, instead of to its parent
constraint.referenceNode = scene
Here are two methods to create a ball with a physics body and an arrow:
Add an arrow as a child of a ball
Add both the ball and the arrow directly to the scene
Here's what will happen when you add the above to the SpriteKit simulation:
The arrow will rotate when the ball rotates
Both the arrow and the ball will move/rotate independently
If you want the arrow to rotate with the ball, choose Option 1. If you want the arrow to remain fixed, choose Option 2. If you choose Option 2, you will need to adjust the rotation of the arrow to ensure that it points upward. Here's an example of how to do that.
-(void)didMoveToView:(SKView *)view {
self.scaleMode = SKSceneScaleModeResizeFill;
/* Create an edge around the scene */
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:view.frame];
// Show outline of all physics bodies
self.view.showsPhysics = YES;
CGFloat radius = 16;
SKNode *balls = [SKNode node];
balls.name = #"balls";
[self addChild:balls];
// Create 5 balls with stationary arrows
for (int i = 0;i<5;i++) {
// Create a shape node with a circular physics body. If you are targeting iOS 8,
// you have other options to create circular node. You can also create an SKSpriteNode
// with a texture
SKShapeNode *ball = [SKShapeNode node];
// Create a CGPath that is centered
ball.path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-radius,-radius,radius*2,radius*2)].CGPath;
ball.fillColor = [SKColor whiteColor];
ball.position = CGPointMake(100, 100+i*radius*2);
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:radius];
[balls addChild:ball];
// Create an arrow node
CGSize size = CGSizeMake(2, radius*2);
SKSpriteNode *arrow = [SKSpriteNode spriteNodeWithColor:[SKColor blackColor] size:size];
arrow.name = #"arrow";
arrow.position = CGPointZero;
[ball addChild:arrow];
// Apply angular impulse to the ball so it spins when it hits the floor
[ball.physicsBody applyAngularImpulse:-1];
}
}
- (void) didSimulatePhysics
{
SKNode *balls = [self childNodeWithName:#"balls"];
for (SKNode *ball in balls.children) {
SKNode *arrow = [ball childNodeWithName:#"arrow"];
arrow.zRotation = -ball.zRotation;
}
}
sprite.physicsBody.allowsRotation = NO;
The allowRotation property should control exactly what you are asking.

CCSprite not responding to Collision

I am a newbie to Spritebuilder and trying to make MARIO Game prototype. What i am doing is I am having 2 sprites, one sprite has fast walking character and other has normally walking character. I am changing the sprite through a button. When i click on the button the normally walking sprite is replaced by fast walking sprite and again clicking back on that button make the normal sprite appear.
Problem :
For the very first time when the game starts, collision is detected between ground and character on jumping. But as soon as i change the sprite to fast walking character, no collision is detected on jumping. When i again switch back to normal walking character, then also no collision is detected on jumping.
When i make the sprite jump and it starts falling down, to ground some sort of activity (i would prefer saying bouncing) is observed which i also can't say whether it is bouncing back or not. I dont know why it is coming and how to rectify it. I searched stack overflow and some forums. What i found was to use
CCPhysicsBody.iterations
But i dont know how to use it.
For more detailed explanation you can see the video at this link to undersatnd the full problem. https://www.dropbox.com/s/0fz3qehie76r60k/Game.mp4?dl=0
Please help. Any suggestions are welcomed.
Edit : Here is the code where i am changing the sprites:
-(void)setCharacterAnimationFast
{
if (_turboButton.selected)
{
//changing button state;
_turboButton.selected=NO;
//changing value of background speed from screen;
speeedParameter = 50.0;
speedParameterOfBackgroundObjects=10.0;
//loading new chracter
_character =(RightCharacter*)[CCBReader load:#"RightCharacter"];
//position the laser at helicopter origin
CGPoint _characterPosition = _fastCharacter.position;
//transform the world position to the node space to which the laser will be added (_physicsNode)
_character.position = [_physicsNode convertToNodeSpace:_characterPosition];
//defining zorder for moving it to background
_character.zOrder= _fastCharacter.zOrder;
// add the laser to the physicsNode of this scene (because it has physics enabled)
[_physicsNode addChild:_character];
//removing the existing character
[_fastCharacter removeFromParentAndCleanup:YES];
CCLOG(#"ground : %#", _ground);
CCLOG(#"ground : %#", _ground.physicsBody);
CCLOG(#"ground : %#", _ground.physicsNode);
_ground.physicsBody.collisionMask=#[#"character"];
_character.physicsBody.collisionMask=#[#"ground"];
_ground.physicsBody.collisionType=#"ground";
_ground.physicsBody.elasticity=0;
_character.physicsBody.collisionType=#"character";
_character.physicsBody.elasticity=0;
_physicsNode.iterations=1000;
_fastCharacter=nil;
}
else
{
//changing button state
_turboButton.selected=YES;
//changing value of background speed from screen;
speeedParameter = 80.0;
speedParameterOfBackgroundObjects= 20.0;
//loading new fast character
_fastCharacter =(RightCharacterFast*)[CCBReader load:#"RightCharacterFast"];
//position the laser at helicopter origin
CGPoint _fastCharacterPosition = _character.position;
//transform the world position to the node space to which the laser will be added (_physicsNode)
_fastCharacter.position = [_physicsNode convertToNodeSpace:_fastCharacterPosition];
//defining zorder for moving it to background
_fastCharacter.zOrder= _character.zOrder;
// add the laser to the physicsNode of this scene (because it has physics enabled)
[_physicsNode addChild:_fastCharacter];
//removing the existing character
[_character removeFromParentAndCleanup:YES];
CCLOG(#"ground : %#", _ground);
CCLOG(#"ground : %#", _ground.physicsBody);
CCLOG(#"ground : %#", _ground.physicsNode);
_ground.physicsBody.collisionMask=#[#"fastCharacter"];
_fastCharacter.physicsBody.collisionMask=#[#"ground"];
_ground.physicsBody.elasticity=0;
_ground.physicsBody.collisionType=#"ground";
_fastCharacter.physicsBody.collisionType=#"fastCharacter";
_fastCharacter.physicsBody.elasticity=0;
_physicsNode.iterations=1000;
_character=nil;
}
}
And here is the link to the video of debug draw: https://www.dropbox.com/s/vt557ngpyj6z9g3/DebugDraw.mp4?dl=0
Use the same proxy body node for collisions, its very efficient using less intensive nodes for collision:
characterNode.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:sprite.size.width/2];
fastWalkingCharacterNode.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:sprite.size.width/2];

Sprite Kit - Do not allow node to move out of screen

I am creating a game where I do not want the player to be able to move out of the screen. The node follows the players moving touch. The code I have "under construction" so the player can't move out on the top or right side, but I do not want the player to be able to move out on any of the sides.
- (void)movementPlayer {
SKAction * actionMoveX = [SKAction moveToX:MIN(location2.x - playerPositionX, self.size.width - (_player.size.width/2)) duration:0];
SKAction * actionMoveY = [SKAction moveToY:MIN(location2.y - playerPositionY, self.size.height - (_player.size.height/2)) duration:0];
[_player runAction:[SKAction sequence:#[actionMoveX, actionMoveY]]];
}
You should create a physics world and add a border rectangle around the screen. This border must have a physics body that is set to collide with the physics body collision category given to your player node. If the player node starts inside the border, the player cannot leave the screen and no additional coding is required (besides properly setting up collision categories for each physics body)
RayWenderlich.com has easy to understand game tutorials that show how to handle collisions following the sprite kit manual.
In your SKScene:
self.physicsBody=[SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
self.physicsWorld.gravity=CGVectorMake=(0,0);
_player.physicsBody=[SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(10,10)];//example; use bodyWithPolygonFromPath:yourCGPath if player has more complex form, or bodyWithCircleOfRadius:radius if its shape is circle
_player.physicsBody.velocity=CGVectorMake(10,10); //or more then 10,10
Good luck!
just add the bottom code to your method that is called it should stop all physical bodies from leaving the screen!! veryuseful!
SKPhysicsBody* gameborderBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
// 2 Set physicsBody of scene to borderBody
self.physicsBody = gameborderBody;
// 3 Set the friction of that physicsBody to 0
self.physicsBody.friction = 1.0f;

SKPhysicBody In the Wrong position

I am creating a simple Sprite Kit game however when i am adding the PhysicsBody to one of my sprites it seems to be going in the wrong position. i know that it is in the wrong position as i have have set
skView.showsPhysics = YES;
and it is showing up in the wrong position.
The Square in the bottom corner is the physics body for the first semicircle. I am using a square at the moment just for testing purposes.
My app includes view following and follows my main sprite when it moves. I implemented this by following apples documentation and creating a 'myworld' node and creating all other nodes from that node.
myWorld = [SKNode node];
[self addChild:myWorld];
semicircle = [SKSpriteNode spriteNodeWithImageNamed:#"SEMICRICLE.png"];
semicircle.size = CGSizeMake(semicircle.frame.size.width/10, semicircle.frame.size.height/10);
semicircle.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:semicircle.frame.size];
semicircle.position = CGPointMake(self.frame.size.width/2, self.frame.size.height/2);
semicircle.physicsBody.dynamic = YES;
semicircle.physicsBody.collisionBitMask = 0;
semicircle.name = #"semicircle";
[myWorld addChild:semicircle];
To centre on the node I call these methods
- (void)didSimulatePhysics
{
[self centerOnNode: [self childNodeWithName: #"//mainball"]];
}
- (void) centerOnNode: (SKNode *) node
{
CGPoint cameraPositionInScene = [node.scene convertPoint:node.position fromNode:node.parent];
node.parent.position = CGPointMake(node.parent.position.x - cameraPositionInScene.x, node.parent.position.y - cameraPositionInScene.y);
}
I don't know if the my world thing makes any difference to the SkPhysics body...
SKPhysicsBody starts at coordinates 0,0 which is at the bottom left hand corner. If you make the area smaller, as you did by width/10 and height/10, you decrease the size but from the bottom left.
I think what you are looking for is bodyWithRectangleOfSize:center: which allows you to manually set the center from which you base your physics body area on.
Update:
Based on what I understand, your smallest semi circle pic size is the same as the screen size. I would suggest you modify the image size to something like the example I have. You can then set the sprite's position as required and set the physics body to the half of the image containing your semi circle.
Your centerOnNode call should be put in the didEvaluateActions function instead of the didSimulatePhysics function. This is because you need to move the world before the physics are drawn so that they stay in sync. Similar question found here: https://stackoverflow.com/a/24804793/5062806

Resources