Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
266 views
in Technique[技术] by (71.8m points)

objective c - Physics bodies other than my player won't call didBeginContact when colliding

In my game I have 4 bitmasks and everything is setup, yet didBeginContact only gets called when the first bitmask (playerCategory) collides with something. if 3 collides with 4, nothing happens, even though I have the contactTestBitMask set for them to collide.

myscene.h

    self.physicsWorld.gravity = CGVectorMake(0.0, -2.45);
    self.physicsWorld.contactDelegate = self;

    self.player = [SKSpriteNode spriteNodeWithColor:[SKColor blueColor] size: CGSizeMake(10.0, 20.0)];
    self.player.position = CGPointMake(20.0, self.frame.size.height);
    self.player.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.player.size];
    self.player.physicsBody.affectedByGravity = YES;
    self.player.physicsBody.dynamic = YES;
    self.player.physicsBody.categoryBitMask = playerCategory;
    self.player.physicsBody.contactTestBitMask = enemyCategory;
    self.player.physicsBody.collisionBitMask = tileCategory;
    [self.gameNode addChild:self.player];

    [...]

- (void) swipeRightHandler:(UISwipeGestureRecognizer *) recognizer {
    NSLog(@"swipe right");
    SKSpriteNode *attackRect = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(40, 5)];
    attackRect.position = CGPointMake(self.player.position.x + 10, self.player.position.y);
    attackRect.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:attackRect.size];
    attackRect.physicsBody.categoryBitMask = attackCategory;
    attackRect.physicsBody.contactTestBitMask = 255;
    attackRect.physicsBody.collisionBitMask = enemyCategory;
    attackRect.physicsBody.affectedByGravity = NO;
    attackRect.physicsBody.dynamic = NO;
    [self.gameNode addChild:attackRect];
    [attackRect runAction:[SKAction moveBy:CGVectorMake(250, 0) duration:1.0] completion:^{
        [attackRect removeFromParent];
    }];
}

RandomLevelGenerator.m:

        SKSpriteNode *enemy = [SKSpriteNode spriteNodeWithColor:[SKColor greenColor] size:CGSizeMake(20, 20)];
        enemy.position = CGPointMake(visual_x, FLOOR_X + arc4random_uniform(MAX_Y - FLOOR_X));
        enemy.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:enemy.size];
        enemy.physicsBody.categoryBitMask = enemyCategory;
        enemy.physicsBody.contactTestBitMask = playerCategory | attackCategory;
        enemy.physicsBody.collisionBitMask = attackCategory;
        enemy.physicsBody.affectedByGravity = NO;
        enemy.physicsBody.dynamic = NO;
        [self.scene.gameNode addChild:enemy];

LevelGenerator.h:

static const uint32_t playerCategory    =   0x1 << 0;
static const uint32_t tileCategory      =   0x1 << 1;
static const uint32_t enemyCategory     =   0x1 << 2;
static const uint32_t attackCategory    =   0x1 << 3;

MyScene.m again:

SKPhysicsBody *firstBody, *secondBody;

if(contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
    firstBody = contact.bodyA;
    secondBody = contact.bodyB;
} else {
    firstBody = contact.bodyB;
    secondBody = contact.bodyA;
}
NSLog(@"%d - %d", firstBody.categoryBitMask, secondBody.categoryBitMask);

That last NSLog, only ever prints 1 - x, never 3 - x. I've also tried allowing other collisions and I can't get any of them to collide.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

If the categoryBitMask for two bodies ANDed together is zero, they will not generate contact events. For instance this happens with playerCategory (1) and enemyCategory (4): 1 & 4 = 0 <== no contact events are generated, ever, between players and enemies. In that case it won't even get to check the contactBitMask flags.

If you want two bodies to generate contact events, they both have to have the same category bitmask flag set.

The default for categoryBitMask is 0xFFFFFFFF so that all bodies can contact with each other. Same for collisionBitMask because generally you want all bodies to contact and collide by default.

The only bit mask you really need to modify in most cases is the contactBitMask, whose default is 0, meaning no contact events are generated by default (to improve performance).

Only change the other bitmasks where changing contactBitMask alone does not suffice.

For example when you want contact events, but no collision feedback (= change in velocity/position of colliding bodies when they come in contact), then make sure that those body's collisionBitMask ANDed together is 0. This allows bodies to generate contact events, but pass through each other. Useful to create triggers, ie when the player enters an area you can let him pass but also trigger a game event.

Or when you want to use the same contact bitmasks for two different categories of bodies who should only collide/contact with other bodies sharing the same category - ie regular and "ghost" characters who should have the same contact behavior with everything else in the game, except they shouldn't contact/collide with their corporeal (non-ghost) counterparts. In that case, use the same contactBitMask and collisionBitMask for both bodies. Then set all flags in categoryBitMask for all other categories, but don't set the flags for the two categories representing the corporeal and ghost entities. Ie if corporeal category is 2 and ghost category is 8, the categoryBitMask should be: 0xFFFFFFFF - 2 - 8 = 0xFFFFFFF5 (lower 8 bits would be: 11110101)

Long story short: categoryBitMask flags should be cleared from 0xFFFFFFFF (rather than set to a specific bit) so that individual bodies in specific categories don't contact with each other but still contact with all other bodies.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...