Im trying to test for collision in my game, which is a Brick Breaker type game, between bullets and a specific type of brick. When the bullet hits the brick, the brick is then removed. I can test for collision between any brick and the bullets, and removed them. But I cannot get the collision to work between a specific type of brick, and then remove that brick.
Here's my Brick.h class
#import <SpriteKit/SpriteKit.h>
typedef enum : NSUInteger {
// Normal Bricks
Blue = 1,
LightBlue = 2,
DarkBlue = 3,
Green = 4,
LightGreen = 5,
DarkGreen = 6,
Grey = 7,
DarkGrey = 8,
Pink = 9,
LightPink = 10,
Purple = 11,
LightPurple = 12,
DarkPurple = 13,
Red = 14,
DarkRed = 15,
Orange = 16,
Gold = 17,
Yellow = 18,
Teal = 19,
Brown = 20,
} BrickType;
static const uint32_t BRICK_CATEGORY = 0x1 << 2;
@interface XSBrick : SKSpriteNode
@property (nonatomic) BrickType type;
@property (nonatomic) BOOL indestructible;
@property (nonatomic) BOOL spawnsExtraBall;
@property (nonatomic) BOOL givesExtraLife;
@property (nonatomic) BOOL enlargePaddle;
@property (nonatomic) BOOL spawnsTwoOrThreeExtraBalls;
@property (nonatomic) BOOL makesBallSlow;
@property (nonatomic) BOOL makesBallFast;
@property (nonatomic) BOOL bullets;
-(instancetype)initWithType:(BrickType)type;
-(void)hit;
@end
The Brick.m class
#import "XSBrick.h"
@implementation XSBrick
{
SKAction *_brickSmashSound;
}
#pragma mark - Load Bricks
-(instancetype)initWithType:(BrickType)type
{
switch (type) {
// Normal Bricks
case Blue:
self = [super initWithImageNamed:@"BrickBlue"];
break;
case LightBlue:
self = [super initWithImageNamed:@"BrickLightBlue"];
break;
case DarkBlue:
self = [super initWithImageNamed:@"BrickDarkBlue"];
break;
case Green:
self = [super initWithImageNamed:@"BrickGreen"];
break;
case DarkGreen:
self = [super initWithImageNamed:@"BrickDarkGreen"];
break;
case LightGreen:
self = [super initWithImageNamed:@"BrickLightGreen"];
break;
case Grey:
self = [super initWithImageNamed:@"BrickGrey"];
break;
case DarkGrey:
self = [super initWithImageNamed:@"BrickDarkGrey"];
break;
case Pink:
self = [super initWithImageNamed:@"BrickPink"];
break;
case LightPink:
self = [super initWithImageNamed:@"BrickLightPink"];
break;
case Purple:
self = [super initWithImageNamed:@"BrickPurple"];
break;
case LightPurple:
self = [super initWithImageNamed:@"BrickLightPurple"];
break;
case DarkPurple:
self = [super initWithImageNamed:@"BrickDarkPurple"];
break;
case Red:
self = [super initWithImageNamed:@"BrickRed"];
break;
case DarkRed:
self = [super initWithImageNamed:@"BrickDarkRed"];
break;
case Orange:
self = [super initWithImageNamed:@"BrickOrange"];
break;
case Gold:
self = [super initWithImageNamed:@"BrickGold"];
break;
case Yellow:
self = [super initWithImageNamed:@"BrickYellow"];
break;
case Teal:
self = [super initWithImageNamed:@"BrickTeal"];
break;
case Brown:
self = [super initWithImageNamed:@"BrickBrown"];
break;
default:
self = nil;
break;
}
return self;
}
And in MyScene.m, didBeginContact. This works but removes any type of brick that the bullet hits
// Collision between bullets and bricks
if (firstBody.categoryBitMask == BRICK_CATEGORY && secondBody.categoryBitMask == BULLET_CATEGORY) {
[firstBody.node removeFromParent];
[secondBody.node removeFromParent];
}
I've tried this but Xcode gives me a warning of a comparison between a SKNode and a NSUInteger, so it didn't work.
// Collision between bullets and bricks
if (firstBody.categoryBitMask == BRICK_CATEGORY && secondBody.categoryBitMask == BULLET_CATEGORY) {
if (firstBody.node == LightBlue) {
[firstBody.node removeFromParent];
}
[secondBody.node removeFromParent];
}
I've also tried creating an instance of of my Brick and then testing against that, but that didn't work either.
// Collision between bullets and bricks
if (firstBody.categoryBitMask == BRICK_CATEGORY && secondBody.categoryBitMask == BULLET_CATEGORY) {
XSBrick *LightBlueBrick = [[XSBrick alloc]initWithType:LightBlue];
if (firstBody.node == LightBlueBrick) {
[firstBody.node removeFromParent];
}
[secondBody.node removeFromParent];
}
I've been at this for awhile now trying to get it to work. Plus, I'm still pretty new to developing for iOS so any help would be greatly appreciated.
EDIT:
I tried your answer, rickster, but it threw an error, "2014-07-02 09:13:52.527 Brick Breaker[44129:60b] -[SKSpriteNode type]: unrecognized selector sent to instance 0x12145a2a0". But I did get it to work using some of the code you provided.
// Collision between bullets and bricks
if (firstBody.categoryBitMask == BRICK_CATEGORY && secondBody.categoryBitMask == BULLET_CATEGORY) {
XSBrick *brick = nil;
SKNode *bullets = nil;
if ([firstBody.node isKindOfClass:[XSBrick class]]) {
brick = firstBody.node;
bullets = secondBody.node;
if (brick.type == LightBlue) {
[firstBody.node removeFromParent];
}
else if (brick.type == Blue) {
[firstBody.node removeFromParent];
}
}
[secondBody.node removeFromParent];
}
This now works. It detects what type of brick is hit, and then removes that brick. If it isn't in my if statement, it wont get removed, which is just the way i want it. So thanks alot for your help!!
if (firstBody.node == LightBlue)
doesn't work because you're comparing a node (an object pointer) with an enum value (an integer). You need to compare values of the same type for an equality comparison to have any useful meaning.
if (firstBody.node == LightBlueBrick)
doesn't work because you're testing against an XSBrick
instance that you created right there. You're already in didBeginContact
, so there's a node that already exists that's involved in the collision — if you create a new node, that node is guaranteed not to be the one that already exists. You just need to inspect the properties of each node in the collision to find out which node, or which kind of node, it is.
You already have an if
statement that tests whether firstBody.categoryBitMask
matches the value you use for bricks. If you're sure you use that value only for bricks, you can safely assume that firstBody.node
points to an instance of XSBrick
. (The safe thing to do would be to use isKindOfClass
to be sure, though.)
For the compiler to let you call XSBrick
methods, you'll need to cast firstBody.node
to that class type. Then you can check the type
property you defined on XSBrick
.
Finally, SpriteKit doesn't guarantee the order of bodies in a contact handler, so it won't always be true that the first body in a collision between a brick and a bullet is the brick.
Those are the ingredients you need to solve this problem. Putting them all together, here's a sample implementation of your contact handler:
- (void)didBeginContact:(SKPhysicsContact *)contact {
// If we expect more than one kind of collision, check that first
if (contact.bodyA.categoryBitMask | contact.bodyA.categoryBitMask == BRICK_CATEGORY | BULLET_CATEGORY)
// We know it's a brick/bullet collision, sort out which body is which
XSBrick *brick = nil;
SKNode *bullet = nil;
if ([contact.bodyA.node isKindOfClass:[XSBrick class]]) {
brick = contact.bodyA.node;
bullet = contact.bodyB.node;
} else {
brick = contact.bodyB.node;
bullet = contact.bodyA.node;
}
// Now, check the type of brick
if (brick.type == LightBlue) {
// Do whatever with light blue bricks
} else {
// Do whatever with other kinds of bricks
}
}
}