Sprite Kit collisions for multiple collisions - ios

I have looked, and have found answers for single collisions, but I am looking for a way to detect more than one type of collision. I am making a game where there are 3 collisions I would like. The user plane colliding with enemy bullets, the user's bullet colliding with the enemy plane (which i have working already), and the enemy bullet and user bullet colliding. I have all the categoryBitMask and contactTestBitMask set up and correct. Here is my delegate method.
- (void) didBeginContact:(SKPhysicsContact *)contact {
SKPhysicsBody *firstBody, *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
firstBody = contact.bodyA;
secondBody = contact.bodyB;
}
else
{
firstBody = contact.bodyB;
secondBody = contact.bodyA;
}
// if user plane hits enemy bullet
if ((firstBody.categoryBitMask == playerShipCategory) &&
(secondBody.categoryBitMask == enemyBulletCategory)) {
[self takeDamageByAmount:POINT_INCREMENTER];
[_enemyBullet removeFromParent];
SKAction *bounce = [SKAction sequence:#[
[SKAction fadeAlphaTo:.5 duration:.2],
[SKAction fadeAlphaTo:1.0 duration:.2],
[SKAction fadeAlphaTo:.5 duration:.2],
[SKAction fadeAlphaTo:1.0 duration:.2]
]];
[_playerPlane runAction:bounce];
}
// if the user bullet hits the enemy bullet
else if ((firstBody.categoryBitMask == bulletCategory) &&
(secondBody.categoryBitMask == enemyBulletCategory)) {
[_enemyBullet removeFromParent];
[_bullet removeFromParent];
}
// if bullet hits enemy ship - THIS ONE WORKS, but none of the others work for some reason
else if ((firstBody.categoryBitMask == bulletCategory) &&
(secondBody.categoryBitMask == enemyShipCategory)) {
[self gainPointsByAmoint:POINT_INCREMENTER];
[self projectile:(SKSpriteNode *)firstBody.node didCollideWithMonster:(SKSpriteNode *)secondBody.node];
}
}

This should work it's already tested and working
//Define the collider Category
typedef NS_ENUM(uint32_t, CollisionType) {
enemyShipCategory = 0x1 << 0,
enemyBulletCategory = 0x1 << 1,
playerShipCategory = 0x1 << 2,
bulletCategory = 0x1 << 3,
};
// Set the category that this physics body belongs to
// and specify which categories of bodies cause intersection
// notifications with this physics body
ship.physicsBody.categoryBitMask = playerShipCategory;
ship.physicsBody.contactTestBitMask = enemyBulletCategory;
shipBullet.physicsBody.categoryBitMask = bulletCategory;
shipBullet.physicsBody.contactTestBitMask = enemyShipCategory | enemyBulletCategory;
enemy.physicsBody.categoryBitMask = enemyShipCategory;
enemy.physicsBody.contactTestBitMask = bulletCategory;
enemyBullet.PhysicsBody.categoryBitMask = enemyBulletCategory;
enemyBullet.physicsBody.contactTestBitMask = bulletCategory;
// And handle Collisions
- (void)didBeginContact:(SKPhysicsContact *)contact {
uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
if (collision == (playerShipCategory | enemyBulletCategory)) {
SKNode *Ship, *bullet;
if (contact.bodyA.categoryBitMask == playerShipCategory) {
Ship = contact.bodyA.node;
bullet = contact.bodyB.node;
} else {
Ship = contact.bodyB.node;
bullet = contact.bodyA.node;
}
[self takeDamageByAmount:POINT_INCREMENTER];
[bullet removeFromParent];
SKAction *bounce = [SKAction sequence:#[
[SKAction fadeAlphaTo:.5 duration:.2],
[SKAction fadeAlphaTo:1.0 duration:.2],
[SKAction fadeAlphaTo:.5 duration:.2],
[SKAction fadeAlphaTo:1.0 duration:.2]
]];
[Ship runAction:bounce];
}
else if (collision == (bulletCategory | enemyBulletCategory)) {
[contact.bodyA.node removeFromParent];
[contact.bodyB.node removeFromParent];
}
else if (collision == (bulletCategory | enemyShipCategory)) {
SKNode *shipBullet, *enemyShip;
if (contact.bodyA.categoryBitMask == shipBullet) {
shipBullet = contact.bodyA.node;
enemyShip = contact.bodyB.node;
} else {
shipBullet = contact.bodyB.node;
enemyShip = contact.bodyA.node;
}
[self gainPointsByAmoint:POINT_INCREMENTER];
[self projectile:shipBullet didCollideWithMonster:enemyShip];
}
}
Good Luck!!

Related

sprite kit can't receive contact message nodes

so i made the code below to receive contact:
- (void) didBeginContact:(SKPhysicsContact *)contact{
SKPhysicsBody *firstBody,*secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask) {
//player<enemy<abilities
firstBody =contact.bodyA; // player
secondBody = contact.bodyB; // enemy or abilities
} else {
firstBody = contact.bodyB;//player
secondBody = contact.bodyA;//enemy or projectile
}
if (firstBody.categoryBitMask == collisionCatergoryPlayer &&
secondBody.categoryBitMask == collisionCatergoryEnemy) {
NSLog(#"test");
playerBall *ball = (playerBall *)firstBody.node;
[ball removeFromParent];
enemyBall *enemyBall1 = (enemyBall *)secondBody.node;
[enemyBall1 removeFromParent];
} else if (firstBody.categoryBitMask == collisionCatergoryPlayer &&
secondBody.categoryBitMask == collisionCatergoryAbilities){
NSLog(#"upgrade");
abilities *abilities1 = (abilities *)secondBody.node;
//if theres abilities2 and 3,make a catergory bit mask for it and remove it from parent
[abilities1 removeFromParent];
}
}
but i can't receive the nslog messages from the code above,please help!!!!
also the remove nodes are not doing anything,my bit mask is player

How to blink SpritekitNode?

i want to blink SpritekitNode when two player and monster collapse . im using code in didBeginContact:
-(void)didBeginContact:(SKPhysicsContact *)contact
{
SKPhysicsBody *firstBody, *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
firstBody = contact.bodyA;
secondBody = contact.bodyB;
}
else
{
firstBody = contact.bodyB;
secondBody = contact.bodyA;
}
if ([firstBody.node.name isEqual: OBSTACLE_KEY] && ( [secondBody.node.name isEqual: PLAYER_KEY] ))
{
if ([kPref integerForKey:BUY_LIFES] > 0)
{
SKAction *blink = [SKAction sequence:#[[SKAction fadeOutWithDuration:0.25],
[SKAction fadeInWithDuration:0.25]]];
SKAction *blinkForever = [SKAction repeatAction:blink count:3];
[secondBody.node runAction: blinkForever];
int life = [kPref integerForKey:BUY_LIFES] - 1;
[kPref setInteger:life forKey:BUY_LIFES];
}
else
{
[kPref setInteger:sec forKey:HIGH_SCORE];
self.paused =YES;
}
}
}
it working but it continuously call didBeginContact when monster touches player node till it connected to it, i want to set in way that if monster node touch player than it blinks 3 times and that period of time none monster can touch player node.
Please help me out.!

Game increases node count drastically and freezes

I have created two nodes, a projectile node and a target node, each with their respective categories and when they both contact each other, both are removed and depending on the projectile, even more projectiles appear.
The projectiles also are removed when they touch the edge of the screen.
Everything works fine until after a "wave" of targets (I created a method to occasionally spawn 5 targets in a set design) appear and scroll across the screen and projectiles are all over the place contacting more and more targets, now I would understand the node count increasing, but the amount is no where near the amount, the game then lags to the point of the FPS being 2 FPS and the game eventually freezing and the node count stuck to 700.
didBeginContact Method
static inline Enemies *nodeFromBody(SKPhysicsBody *body1, SKPhysicsBody *body2, uint32_t category) {
Enemies *node = nil;
if (body1.categoryBitMask & category) {
node = (Enemies *)body1.node;
}
else if (body2.categoryBitMask & category) {
node = (Enemies *)body2.node;
}
return node;
}
-(void)didBeginContact:(SKPhysicsContact *)contact {
SKPhysicsBody *firstBody, *secondBody;
SKSpriteNode *projectile = nil;
SKSpriteNode *offScreen = nil;
Enemies *target = nil;
firstBody = contact.bodyA;
secondBody = contact.bodyB;
projectile = nodeFromBody(firstBody, secondBody, projectileCategory);
offScreen = nodeFromBody(firstBody, secondBody, offScreenCategory);
target = nodeFromBody(firstBody, secondBody, targetsCategory);
if (projectile && target) {
if ([projectile.name isEqualToString:#"firstShot"]) {
NSLog(#"firstShot");
[projectile removeAllActions];
[projectile removeFromParent];
[ProjectilePatterns pattern1:self spawnPoint:target.position];
[target removeAllActions];
[target removeFromParent];
[Enemies wave1:self spawnPoint:CGPointMake(CGRectGetMidX(self.frame) - 525, CGRectGetMidY(self.frame) - 50)];
}
}
enemyClassMethod
// These are the regular targets, the other method that spawns the wave is essentially creating new enemies and returns them positioned in certain positions.
+(Enemies *)wave1:(SKScene *)scene spawnPoint:(CGPoint)location {
SKAction *delay = [SKAction waitForDuration:5];
SKAction *remove = [SKAction removeFromParent];
SKAction *delayAndRemove = [SKAction sequence:#[delay, remove]];
Enemies *enemy1 = [[Enemies alloc] init];
enemy1 = [Enemies spriteNodeWithColor:[SKColor blackColor] size:CGSizeMake(30, 30)];
enemy1.position = location;
enemy1.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:enemy1.size];
enemy1.physicsBody.categoryBitMask = targetsCategory;
enemy1.physicsBody.contactTestBitMask = projectileCategory;
enemy1.physicsBody.collisionBitMask = 0;
[scene addChild:enemy1];
[enemy1 runAction:delayAndRemove];
[enemy1.physicsBody applyImpulse:CGVectorMake(12, 0)];
return enemy1;
}
Projectile Pattern
+(ProjectilePatterns *)pattern1:(SKScene *)scene spawnPoint:(CGPoint)location {
SKAction *rotate = [SKAction rotateByAngle:1 duration:0.1];
SKAction *rotateForever = [SKAction repeatActionForever:rotate];
ProjectilePatterns *patternBomb = [[ProjectilePatterns alloc] init];
patternBomb = [ProjectilePatterns spriteNodeWithImageNamed:#"contactBomb"];
patternBomb.position = CGPointMake(location.x + 10, location.y);
patternBomb.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:patternBomb.size];
patternBomb.physicsBody.categoryBitMask = projectileCategory;
patternBomb.physicsBody.contactTestBitMask = targetsCategory | offScreenCategory;
patternBomb.physicsBody.collisionBitMask = 0;
patternBomb.physicsBody.friction = 0;
patternBomb.physicsBody.linearDamping = 0;
patternBomb.name = #"p1";
ProjectilePatterns *patternBomb2 = [[ProjectilePatterns alloc] init];
patternBomb2 = [ProjectilePatterns spriteNodeWithImageNamed:#"contactBomb"];
patternBomb2.position = CGPointMake(location.x - 10, location.y);
patternBomb2.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:patternBomb2.size];
patternBomb2.physicsBody.categoryBitMask = projectileCategory;
patternBomb2.physicsBody.contactTestBitMask = targetsCategory | offScreenCategory;
patternBomb2.physicsBody.collisionBitMask = 0;
patternBomb2.physicsBody.friction = 0;
patternBomb2.physicsBody.linearDamping = 0;
patternBomb2.name = #"p1";
[scene addChild:patternBomb];
[patternBomb runAction:rotateForever];
[patternBomb.physicsBody applyImpulse:CGVectorMake(5, 0)];
[scene addChild:patternBomb2];
[patternBomb2 runAction:rotateForever];
[patternBomb2.physicsBody applyImpulse:CGVectorMake(-5, 0)];
return patternBomb;
return patternBomb2;
}
I should also note that the scene's physicsWorld gravity is (0,0). I did this so the impulses movement would be constant.

Adding a start and end screen in ios game

I am having trouble having my game begin paused so that the player has to press a button to start the game. I also have collision detection working, so when the two objects collide they just fall off of the screen. Here is my current code:
- (instancetype)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
self.physicsWorld.contactDelegate = self;
[self buildBackground];
[self startScrolling];
_firstPosition = CGPointMake(self.frame.size.width * 0.817f, self.frame.size.height * .40f);
_squirrelSprite = [SKSpriteNode spriteNodeWithImageNamed:#"squirrel"];
_squirrelSprite.position = _firstPosition;
_atFirstPosition = YES;
_squirrelSprite.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:10];
_squirrelSprite.physicsBody.categoryBitMask = squirrelHitCategory;
_squirrelSprite.physicsBody.contactTestBitMask = nutHitCategory;
_squirrelSprite.physicsBody.collisionBitMask = nutHitCategory;
_squirrelSprite.physicsBody.affectedByGravity = NO;
self.physicsWorld.contactDelegate = self;
[self addChild:_squirrelSprite];
// Declare SKAction that waits 2 seconds
SKAction *wait = [SKAction waitForDuration:3.0];
// Declare SKAction block to generate the sprites
SKAction *createSpriteBlock = [SKAction runBlock:^{
const BOOL isHeads = arc4random_uniform(100) < 50;
NSString* spriteName = isHeads ? #"lightnut.png" : #"darknut.png";
SKSpriteNode *nut = [SKSpriteNode spriteNodeWithImageNamed:spriteName];
BOOL heads = arc4random_uniform(100) < 50;
nut.position = (heads)? CGPointMake(257,600) : CGPointMake(50,600);
nut.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(200,160)];
nut.physicsBody.categoryBitMask = nutHitCategory;
nut.physicsBody.contactTestBitMask = squirrelHitCategory;
nut.physicsBody.collisionBitMask = squirrelHitCategory;
nut.physicsBody.affectedByGravity = NO;
self.physicsWorld.contactDelegate = self;
[self addChild: nut];
SKAction *moveNodeUp = [SKAction moveByX:0.0 y:-700.0 duration:1.3];
[nut runAction: moveNodeUp];
}];
// Combine the actions
SKAction *waitThenRunBlock = [SKAction sequence:#[wait,createSpriteBlock]];
// Lather, rinse, repeat
[self runAction:[SKAction repeatActionForever:waitThenRunBlock]];
}
return self;
}
My touchesBegan:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if (_atFirstPosition)
{
SKAction *moveNodeLeft = [SKAction moveByX:-207.8 y:0.0 duration:0.85];
[_squirrelSprite runAction: moveNodeLeft withKey:#"moveleft"];
} else {
SKAction *moveNodeRight = [SKAction moveByX:207.8 y:0.0 duration:0.85];
[_squirrelSprite runAction: moveNodeRight withKey:#"moveright"];
}
_atFirstPosition = !_atFirstPosition;
_squirrelSprite.xScale *= -1.0;
}
Finally, my didBeginContact:
-(void)didBeginContact:(SKPhysicsContact *)contact
{
SKPhysicsBody *firstBody, *secondBody;
firstBody = contact.bodyA;
secondBody = contact.bodyB;
if(firstBody.categoryBitMask == squirrelHitCategory || secondBody.categoryBitMask == nutHitCategory)
{
}
}
I've been trying to figure this out for the last 2 weeks and I have read many tutorials and code to start the game with a simple start button and end screen, but nothing has worked. Any help is greatly appreciated!
I would use a navigation controller and have your start button on the root view and have that button push a viewcontroller that presents your game.

Sprite Kit, Remove Sprite for collision

Im making a game in sprite kit and I'm fairly new to iOS programming and i have been working on getting it so when 2 images collide that one is deleted or made invisible. I have been very unsuccessful with this and was wondering if anyone knew how to do it?
Below is the ship (which always stays) and one of the objects to be deleted.
-(void)addShip
{
//initalizing spaceship node
ship = [SKSpriteNode spriteNodeWithImageNamed:#"Spaceship"];
[ship setScale:0.5];
ship.zRotation = - M_PI / 2;
//Adding SpriteKit physicsBody for collision detection
ship.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:ship.size];
ship.physicsBody.categoryBitMask = shipCategory;
ship.physicsBody.dynamic = YES;
ship.physicsBody.contactTestBitMask = DonutCategory | PizzaCategory | ChocolateCategory | SoftCategory | AppleCategory | GrapeCategory | OrangeCategory | BananaCategory;
ship.physicsBody.collisionBitMask = 0;
ship.physicsBody.usesPreciseCollisionDetection = YES;
ship.name = #"ship";
ship.position = CGPointMake(260,30);
actionMoveRight = [SKAction moveByX:-30 y:0 duration:.2];
actionMoveLeft = [SKAction moveByX:30 y:0 duration:.2];
[self addChild:ship];
}
- (void)shoot1 //donut
{
// Sprite Kit knows that we are working with images so we don't need to pass the image’s extension
Donut = [SKSpriteNode spriteNodeWithImageNamed:#"1"];
[Donut setScale:0.15];
// Position the Donut outside the top
int r = arc4random() % 300;
Donut.position = CGPointMake(20 + r, self.size.height + Donut.size.height/2);
Donut.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:Donut.size];
Donut.physicsBody.categoryBitMask = DonutCategory;
Donut.physicsBody.dynamic = YES;
Donut.physicsBody.contactTestBitMask = shipCategory;
Donut.physicsBody.collisionBitMask = 0;
Donut.physicsBody.usesPreciseCollisionDetection = YES;
// Add the Dount to the scene
[self addChild:Donut];
// Here is the Magic
// Run a sequence
[Donut runAction:[SKAction sequence:#[
// Move the Dount and Specify the animation time
[SKAction moveByX:0 y:-(self.size.height + Donut.size.height) duration:5],
// When the Dount is outside the bottom
// The Dount will disappear
[SKAction removeFromParent]]]];
}
You have to set the delegate of your physics world:
self.physicsWorld.contactDelegate = self;
Then, you have a delegate that is called when two objects contact :
- (void)didBeginContact:(SKPhysicsContact *)contact {
SKPhysicsBody *firstBody, *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
firstBody = contact.bodyA;
secondBody = contact.bodyB;
}
else
{
firstBody = contact.bodyB;
secondBody = contact.bodyA;
}
if ((firstBody.categoryBitMask & projectileCategory) != 0 &&
(secondBody.categoryBitMask & monsterCategory) != 0)
{
//remove the donut and the target
SKSpriteNode *firstNode = (SKSpriteNode *) firstBody.node;
SKSpriteNode *secondNode = (SKSpriteNode *) secondBody.node;
[firstNode removeFromParent];
[secondNode removeFromParent];
}
}
For more information you can jump to the collision part in this tutorial.

Resources