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.
Related
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];
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];
}];
I'm creating an ongoing learning project (basically I code while learning to use the framework in hope it will be useful for me and for someone else) for Sprite Kit (you can find it here if you are interested) but I'm facing some performance problems with my code.
The project puts cubes on the screen and make them falling. Here's the class that creates the piece
// The phisics for our falling piece:
// It will be a square, subject to gravity of 5: check common.h (9.8 is way too much for us)
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(self.size.width, self.size.height)];
self.physicsBody.dynamic = YES;
self.physicsBody.mass = 100;
self.physicsBody.collisionBitMask = piecesCollisionBitmask;
self.physicsBody.allowsRotation = NO;
and here is the scene file with the loop.
//schedule pieces
SKAction *wait = [SKAction waitForDuration:1];
SKAction *pieceIsFalling = [SKAction runBlock:^{
FLPPiece *piece = [[FLPPiece alloc] init];
[self addChild:piece];
}];
SKAction *fallingPieces = [SKAction sequence:#[wait,pieceIsFalling]];
[self runAction:[SKAction repeatActionForever:fallingPieces]];
I suspect that physics is behind my frame rate drop. I would like to stop physics execution on node while keeping it on the screen as soon as it collides with something else.
Is that possible? How can I do that?
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.
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.