How to run actions if 2 sprites collide in sprite kit? - ios

I want to make two sprites disappear when they hit each other in sprite kit. The example in apple's documentation doesn't really help me that much so if you could explain it it would be good. :)

OK, so you need to do several things.
I'm going to assume that you have two sprites...
SKSpriteNode *sprite1;
SKSpriteNode *sprite2;
These are currently flying around your scene and may or may not come into contact with each other.
So, you need to set everything up to enable hit testing and call backs.
First, add physics bodies to the sprites...
sprite1.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:10];
sprite2.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(10, 10)];
// this is a couple of examples, you'll have to set yours up to match the sprite size, shapes
Next you need to add categories to the sprites. This gives the physics engine some information about what type of body it is...
sprite1.physicsBody.categoryBitMask = 1 << 0;
sprite2.physicsBody.categoryBitMask = 1 << 1;
// again this is an example. You probably want to put these in an NS_OPTIONS typedef.
Then you need to tell the physics engine which contact you want to be told about. In the is case you want to be told when sprite1 (1 << 0) comes into contact with sprite2 (1 << 1).
So...
sprite1.physicsBody.contactTestBitMask = 1 << 1; // it will test for contact against sprite 2
sprite2.physicsBody.contactTestBitMask = 1 << 0; // test against sprite 1.
Now in you scene you need to adopt <SKPhysicsContactDelegate> and set the scene to be the physics delegate...
self.physicsWorld.contactDelegate = self;
And then create the method...
- (void)didBeginContact:(SKPhysicsContact *)contact
{
// these two physics bodies have come into contact!
SKSpriteNode *firstContactNode = contact.bodyA.node;
SKSpriteNode *secondContactNode = contact.bodyB.node;
// now remove them...
[firstContactNode removeFromParent];
[secondContactNode removeFromParent];
}

SKAction* _vanishAction =[SKAction fadeOutWithDuration: 1];
[sprite runAction:_vanishAction completion:^(void){
[sprite removeFromParent];
}];

Related

Sprite Kit - Objective c, move multiple SKNode at the same time

i have this method that makes the Lines
lineaGuida_Img = [SKTexture textureWithImageNamed:#"LineaGuida copia.jpeg"];
_Linea = [SKNode node];
_Linea.position = CGPointMake((self.frame.size.width / 7 ) * 2, self.frame.size.height / 2 );
_Linea.zPosition = -10;
SKSpriteNode * linea = [SKSpriteNode spriteNodeWithTexture:lineaGuida_Img];
linea.position = CGPointMake(0,0);
linea.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:linea.size];
linea.physicsBody.dynamic = FALSE;
linea.physicsBody.collisionBitMask = 0;
linea.physicsBody.categoryBitMask = CollisionEnemy;
[_Linea addChild:linea];
[self addChild:_Linea];
In the touchesBegan method when i touch the screen, the player is always on the center of screen, and those lines behind him have to move by -x.
[_Linea runAction:[SKAction moveByX: -(self.size.width/8) y:0 duration:0.1]];
[self spawn_Lines];
But this action is only executed by the last SKNode. I need to apply this to all Lines simultaneously. After that, when the line's position is less then the player's position, the line must be deleted.
SpriteKit uses a tree based model, so whatever the parent node does, the children fall suit. Create an SKNode to house all of your lines, add all of your lines to this SKNode, then apply the actions to the SKNode, not the lines.
Add them to NSMutableArray to keep the reference and use for each loop to make each one run the action. I would recommend you to use NSTimer to provide [SKAction moveByX: -1 y:0 duration:0]] with constant speed which will result in the same motion as you already used. Each time this NSTimer execute you will check all positions of object from your NSMutableArray and than delete it if it fits your conditions. Be careful when you want to lose the reference completely use [object removeFromParent]; and remove it also from your NSMutableArray to avoid lose of performance later on. For this I use rather forLoop with continue method

Check if SKShapeNode which is a Line contains Point

I'm programming a little Game. For this I need some Walls. Therefor I have used:
Wall[w] = [[SKShapeNode alloc] init];
Wall[w].path = WallPos[w];
Wall[w].lineWidth = 4;
Wall[w].strokeColor = [UIColor whiteColor];
Wall[w].zPosition = 3;
[self addChild: Wall[w]];
Wall is an Array of SKShapeNodes and is set in #interface, so I can use it in every method. WallPos contains CGMutablePathRefs.
In TouchesBegan and TouchesMoved I'm calling a method which should check if you have touched one of the walls.
I have also some SKShapeNodes which are Rectangles, and to check if they are touched, I have used
if ([SomeShape containsPoint: Position] {
//Do some stuff
}
But with a line it's not working. Sometimes I'm touching on the line and nothing happens. Then I've seen this: Detecting Touch on SKShapeNode that is a Line and I have tried to do it on that way:
for (int i = 0; i < NrWalls; i++) {
if (CGRectContainsPoint(Wall[i].frame, Position)) {
[self GameOver];
}
}
But now every Point I touch sets a "Game Over" to me!!
Has anyone an Idea, how could I check if the line is touched?
Thanks for your help!
DXXL
Not sure why you want to use SKShapeNodes for walls and rectangles. To answer your question, you can attach a physics body to your shape node and use the contact methods to check for possible contacts. However, assigning a physics body to a shape node could be a tricky undertaking due to the anchor points and getting a desired alignment.
Seeing that you are really only drawing rectangles for your walls, I suggest you use a SKSpriteNode with a physics body instead. Something like this:
SKSpriteNode *myNode = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(5, 100)];
myNode.position = CGPointMake(100, 100);
myNode.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:myNode.size];
myNode.physicsBody.dynamic = NO;
myNode.physicsBody.categoryBitMask = CategoryWall;
[self addObject:myNode];
If you need, you can read up on SKPhysicsBody here.

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;

Collision detection in Sprite Kit

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.

Issue with removing physics body from physics world (spritekit)

I have one sprite and my code is:
sprite.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:sprite.size.width/2];
sprite.physicsBody.dynamic = YES;
sprite.physicsBody.affectedByGravity = YES;
sprite.physicsBody.mass = 550.0f;
sprite.physicsBody.categoryBitMask = 1;
sprite.physicsBody.collisionBitMask = 1;
sprite.physicsBody.contactTestBitMask = 1;
I can detect collision in:
- (void)didBeginContact:(SKPhysicsContact *)contact {
// destroy contact.bodyA , contact.bodyB
}
But how can I destroy/remove these bodies from physics world?
I'm not sure if I completely understand, but if you're looking for a way of removing your sprites upon collision, you have to access each physics body's node property, and call its removeFromParent function.
[contact.bodyA.node removeFromParent];
[contact.bodyB.node removeFromParent];
Of course, you'll want to add additional logic to determine whether the bit masks of the colliding sprites are the same as the ones you wish to destroy during collision.

Resources