Sprite Kit Collision Detection - ios

Hi I am trying to setup collision detection in my game and I want to add collision detection so when the balloons hit the spikes they pop. I have looked at Ray Wenderliches tutorial but I couldn't figure it out because it didn't apply to my case. Any ideas how to set it up for my case?
The spikes are at the top of the screen and the balloons spawn at the bottom.

The basics for setting upp collision between 2 objects is to first setting upp constant that represent the different objects that can collide. I usually make a constants.h file where i keep all the variables that will be used thru out the game/application.
Declare following in either a constants.h file or just declare them as global variables in the class:
static const int balloonHitCategory = 1;
static const int spikeHitCategory = 2;
What you want to do now is set up the physics for both your balloon and spikes
SKSpriteNode *ballooon = [SKSpriteNode spriteNodeWithImageNamed:#"yourimagefilename"];
ballooon.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:yourSize];
ballooon.physicsBody.categoryBitMask = balloonHitCategory;
ballooon.physicsBody.contactTestBitMask = spikeHitCategory;
ballooon.physicsBody.collisionBitMask = spikeHitCategory;
you should set your size and set your images for both of the spritenodes
SKSpriteNode *spikes = [SKSpriteNode spriteNodeWithImageNamed:#"yourimagefilename"];
spikes.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(yourSizeX, yourSizeY)];
spikes.physicsBody.categoryBitMask = spikeHitCategory;
spikes.physicsBody.contactTestBitMask = balloonHitCategory;
spikes.physicsBody.collisionBitMask = balloonHitCategory;
for collision set up the following method:
-(void)didBeginContact:(SKPhysicsContact *)contact
{
SKPhysicsBody *firstBody, *secondBody;
firstBody = contact.bodyA;
secondBody = contact.bodyB;
if(firstBody.categoryBitMask == spikeHitCategory || secondBody.categoryBitMask == spikeHitCategory)
{
NSLog(#"balloon hit the spikes");
//setup your methods and other things here
}
}
Before the collision will work you should also add the . In your scenes .h file add .
#interface myScene : SKScene <SKPhysicsContactDelegate>
#end
and in the .m file in the init function add:
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
self.physicsWorld.contactDelegate = self;
}
return self;
}
For more about collision handling check out apple documentation and adventure game example:
https://developer.apple.com/library/ios/documentation/GraphicsAnimation/Conceptual/CodeExplainedAdventure/HandlingCollisions/HandlingCollisions.html#//apple_ref/doc/uid/TP40013140-CH5-SW1

I have just released a plug-in 2D collision engine for Sprite Kit. It's still in its early stages but could be of some help to you: https://github.com/henryeverett/ToastCollisions2D

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

Is there pixel perfect collision detection for Sprite Kit (iOS7)?

I need to spawn 8 random sprite objects on an iPad sprite game. The objects are fairly big, and with different sizes. They should not overlay. If one that is spawn on the scene overlays it will be removed (optional it removes the underlaying one).
I have been looking for a pixel perfect collision detection framework or helper class for Sprite Kit. So far I haven't found a tutorial or something alike. Most people use normal collision detection which will not be of any help since my objects are big. I tested standard approach but it creates rectangles which make the sprite area in my case even bigger. This is my sprite kit template testing project:
#import "WBMMyScene.h"
static const uint32_t randomObjectCategory = 0x1 << 0;
#interface WBMMyScene () <SKPhysicsContactDelegate>
#end
#implementation WBMMyScene
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
/* Setup your scene here */
self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
self.physicsWorld.gravity = CGVectorMake(0, 0);
self.physicsWorld.contactDelegate = self;
}
return self;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
for (UITouch *touch in touches)
{
CGPoint location = [touch locationInNode:self];
SKSpriteNode *spaceship = [SKSpriteNode spriteNodeWithImageNamed:#"Spaceship"];
//SKSpriteNode *spaceship = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:CGSizeMake(50, 50)];
spaceship.position = location;
[spaceship setSize:CGSizeMake(50, 50)];
[spaceship.texture setFilteringMode:SKTextureFilteringNearest];
//spaceship.texture setFilteringMode
spaceship.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:spaceship.size];
spaceship.physicsBody.dynamic = YES;
spaceship.physicsBody.categoryBitMask = randomObjectCategory;
spaceship.physicsBody.contactTestBitMask = randomObjectCategory;
spaceship.physicsBody.collisionBitMask = 0;
[self addChild:spaceship];
}
}
- (void)didBeginContact:(SKPhysicsContact *)contact
{
// 1
SKPhysicsBody *firstBody, *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
firstBody = contact.bodyA;
secondBody = contact.bodyB;
}
else
{
firstBody = contact.bodyB;
secondBody = contact.bodyA;
}
// 2
if (firstBody.categoryBitMask == secondBody.categoryBitMask)
{
[self projectile:(SKSpriteNode *) firstBody.node didColliteWithEachOther:(SKSpriteNode *) secondBody.node];
}
}
- (void)projectile:(SKSpriteNode *)object1 didColliteWithEachOther:(SKSpriteNode *)object2
{
NSLog(#"Hit");
[object1 removeFromParent];
[object2 removeFromParent];
}
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
}
#end
Thanks for your time :)
As far as I'm aware iOS7 doesn't have per pixel physics, that is due to come with XCode 6 which is in its beta testing phase and available to developers now. Remember it is a beta and does have a few bugs, it's full release is likely to be in September with iOS8 and the new iPhone.
In the mean time you have two options, you could download the XCode 6 beta and work within that for per pixel physics. Personally however I got a little sick of using a beta for full development and instead went back to XCode 5. Within iOS7 and XCode 5 you have the option of defining a physicsBody with a path, which gives an accurate depiction of a sprites physical shape.
The tool I use is here and lets you drag and drop an image and then define path points graphically and returns the code, it really is handy.
I hope this helps!
This solution does not give pixel-perfect detection, so may not be exactly what you (or others finding this question) want, but it is a really good alternative that will be ideal for some.
It automatically calculates a polygon-based physics body by analysing the texture of your sprite. It basically draws a path around the non-transparent pixels and uses that for collision.
Instead of this:
spaceship.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:spaceship.size];
Use this:
spaceship.physicaBody = [SKPhysicsBody bodyWithTexture:spaceship.texture alphaThreshold:0.5f size:spaceship.size];

SKSpriteNode ignoring physics deactivation

I have a node which is placed under my screen and works as a platform. As soon as another node touches the node a boolean is set to NO.
When I define my node with the SKPhysicsBody properties for the collision the node ignores the affectedByGravity property.
My code:
+ (void)addNewNodeTo:(SKNode *)parentNode
{
//Correct image size
SKSpriteNode *desertBottom = [SKSpriteNode node];
desertBottom = [[SKSpriteNode alloc] initWithImageNamed:#"Giraffe.png"];
desertBottom.position = CGPointMake(0, -200);
desertBottom.zPosition = 2;
desertBottom.physicsBody.collisionBitMask = lionType;
desertBottom.physicsBody.categoryBitMask = terrainType;
desertBottom.physicsBody.contactTestBitMask = lionType;
desertBottom.zPosition = 2;
desertBottom.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(desertBottom.size.width, desertBottom.size.height)];
desertBottom.physicsBody.dynamic = YES;
desertBottom.physicsBody.affectedByGravity = NO;
[parentNode addChild:desertBottom];
}
My collision methods:
- (void)didBeginContact:(SKPhysicsContact *)contact
{
uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
if (collision == (lionType | terrainType)) {
self.lionNode.lionIsJumping = NO;
NSLog(#"%i", self.lionNode.lionIsJumping);
}
}
- (void)didEndContact:(SKPhysicsContact *)contact
{
uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
if (collision == (lionType | terrainType)) {
self.lionNode.lionIsJumping = YES;
NSLog(#"%i", self.lionNode.lionIsJumping);
}
}
A physics body has a velocity. Gravity changes velocity over time. If the body is already traveling at a certain velocity due to gravity, and you disable gravity, it will continue to move according to its current velocity but no longer gain additional speed from gravity.
My guess is that you expect the body to stop when disabling gravity. If that's what you want you can do this manually by setting the y component of velocity to zero:
SKPhysicsBody* body = desertBottom.physicsBody;
body.velocity = CGVectorMake(body.velocity.x, 0.0);
If the platform should not move after a collision you have to set the dynamic property to false.
Maybe you have an issue with you bit masks?
Have you tried to log outside your if statement of the didBeginContact method?
I have created a new class with subclass of SKSpriteNode for the bottom and added it to my GameScene.

SKPhysicsContactDelegate Not Working

I have the following code:
in my scene:
static const uint32_t enermyCategory = 0x1 << 0;
static const uint32_t fatherCategory = 0x1 << 1;
self.physicsWorld.contactDelegate = self;
//init ship
Ship *ship = [Ship getFather];
ship.position = CGPointMake(CGRectGetMaxX(self.frame) - ship.frame.size.width , ship.frame.size.height);
[self addChild: ship];
//init enermy
Enermy *ene = [[Enermy alloc] initWithImageNamed:enermyName gameScene:self];
ene.position = ship.position;
[self addChild:ene];
#pragma mark - Physics Delegate Methods
- (void)didBeginContact:(SKPhysicsContact *)contact{
NSLog(#"contact detected");
}
As you can see, I set both ship and energy at the same location to start with, so they will always collide.
For both classes, I have the following code in their init method:
//setup physics body
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.size];
self.physicsBody.dynamic = NO;
self.physicsBody.categoryBitMask = enermyCategory; #shipCategory for ship
self.physicsBody.collisionBitMask = 0;
self.physicsBody.contactTestBitMask = shipCategory; #enermyCategory for ship
What I found is that the NSLog is never get called, so that the physical collision detection never works, I've read a lot from apple developer tutorials and it seems all the same of what they had, not sure why is not working. I can see the ship and energy images on screen collide each other.
self.physicsBody.dynamic = NO;
Static (non-dynamic) bodies do not generate contact events. Make them dynamic.
Also, I see you're passing gameScene to an instance of Enemy. If Enemy has a strong reference to game scene (an ivar) then you may be creating a retain cycle here (enemy retains scene, scene retains enemy).
In Sprite Kit you can simply use self.scene to access the scene and if you need the scene during init, move that code into a setup method and call [ene setup] right after adding it as child to perform setup steps involving the scene.
Having the physicsBody set to dynamic = NO doesn't affect contacts. The more likely case is that you are setting the same contactBitMask and categoryBitMask for both nodes. You should be setting the ship to have the following:
self.physicsBody.categoryBitMask = shipCategory;
self.physicsBody.collisionBitMask = 0;
self.physicsBody.contactTestBitMask = enermyCategory;
The enemy should have the following:
self.physicsBody.categoryBitMask = enermyCategory;
self.physicsBody.collisionBitMask = 0;
self.physicsBody.contactTestBitMask = shipCategory;
Bottom line: they should be swapped in the ship.

Detecting collisions in sprite kit

I'm trying to make a simple game with sprite kit.
The basic idea is that there is one player who can jump to avoid blocks.
But i have a problem I don't know how to make it that when the player hits the block the player disappears and the blood animation starts.
First of all i don't understand what this code does that I found on apples website.
static const uint32_t blockCategory = 0x1 <<0;
static const uint32_t playerCategory = 0x1 <<1;
Than i am calling the didBeginContact function and put a NSLOG("did call function") in it.
But i never receive the output in my debugger.
Here is my _player and _block code:
-(SKSpriteNode *)character {
_player = [SKSpriteNode spriteNodeWithImageNamed:#"soldier_run1"];
_player.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:_player.size.width /2 -5];
_player.physicsBody.dynamic = YES;
_player.physicsBody.usesPreciseCollisionDetection = YES;
_player.physicsBody.friction = 0;
_player.physicsBody.categoryBitMask = playerCategory;
_player.physicsBody.collisionBitMask = blokCategory;
_player.name = #"player";
SKAction *animAction = [SKAction animateWithTextures:playerTextures timePerFrame:0.1 resize:YES restore:YES];
My _player code:
[_player runAction:[SKAction repeatActionForever:animAction]];
return _player;
}
-(SKSpriteNode *)block {
_blok = [[SKSpriteNode alloc] initWithColor:[SKColor blackColor] size:CGSizeMake(15, 40)];
//physics
_blok.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:_blok.size];
_blok.physicsBody.dynamic = NO;
_blok.name = #"block";
_blok.physicsBody.categoryBitMask = blokCategory;
_blok.physicsBody.collisionBitMask = playerCategory;
SKAction *moveBlock = [SKAction sequence:#[
[SKAction moveToX:-20 duration:2] ]];
[_blok runAction:moveBlock ];
return _blok;
}
Also i don't really understand what the categoryBitMask and collisionBitMask do.
After i have that working i would like to make the character disappear from the screen and the blood animation to start, but I have no idea how to let that happen. I think you have to do something like:
if(_player && _block didcollide) {
}
But I don't know how to do it exactly.
The categoryBitMask sets the category that the sprite belongs to, whereas the collisionBitMask sets the category with which the sprite can collide with and not pass-through them.
For collision detection, you need to set the contactTestBitMask. Here, you set the categories of sprites with which you want the contact delegates to be called upon contact.
What you have already done is correct. Here are a few additions you need to do:
_player.physicsBody.contactTestBitMask = blockCategory;
_blok.physicsBody.contactTestBitMask = playerCategory;
Afterwards, implement the contact delegate as follows:
-(void)didBeginContact:(SKPhysicsContact *)contact`
{
NSLog(#"contact detected");
SKPhysicsBody *firstBody;
SKPhysicsBody *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
firstBody = contact.bodyA;
secondBody = contact.bodyB;
}
else
{
firstBody = contact.bodyB;
secondBody = contact.bodyA;
}
//Your first body is the block, secondbody is the player.
//Implement relevant code here.
}
For a good explanation on implementing collision detection, look at this tutorial.
Handling collisions is a bit messy. :) There are a number of approaches, but one place you should start with is this fairly simple example from Apple. The readme provides a good intro, and then you can start poking around with the code.
Another approach (which Apple mentions in their guide) is to use Double Dispatching (see wikipedia for a long desc). I wouldn't start by trying to grok that approach right off however. It is a somewhat advanced approach given it depends on dynamic selectors and similar techniques to enable the magic to take place. However, even with that caveat, you can find a simple example someone put together on how to do it, along with a lot of supporting description here.

Resources