碰撞时,我的玩家以外的物理实体不会调用 didBeginContact

Posted

技术标签:

【中文标题】碰撞时,我的玩家以外的物理实体不会调用 didBeginContact【英文标题】:Physics bodies other than my player won't call didBeginContact when colliding 【发布时间】:2014-04-02 00:06:11 【问题描述】:

在我的游戏中,我有 4 个位掩码,一切都已设置好,但 didBeginContact 仅在第一个位掩码(playerCategory)与某物碰撞时才被调用。如果 3 与 4 发生碰撞,则不会发生任何事情,即使我设置了 contactTestBitMask 让它们发生碰撞。

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 再次:

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);

最后一个 NSLog,只打印 1 - x,从不打印 3 - x。我也尝试过允许其他碰撞,但我无法让它们中的任何一个发生碰撞。

【问题讨论】:

【参考方案1】:

如果两个实体 AND 在一起的 categoryBitMask 为零,则它们不会生成接触事件。例如,玩家类别 (1) 和敌人类别 (4) 会发生这种情况:1 &amp; 4 = 0 contactBitMask 标志。

如果您希望两个实体生成接触事件,它们都必须设置相同的类别位掩码标志。

categoryBitMask 的默认值为0xFFFFFFFF,以便所有实体都可以相互联系。 collisionBitMask 也一样,因为通常您希望所有主体默认接触和碰撞。

在大多数情况下,您真正​​需要修改的唯一位掩码是contactBitMask,其默认值为 0,表示默认情况下不生成任何联系事件(以提高性能)。

仅更改 contactBitMask 不足以解决的其他位掩码。

例如,如果您想要接触事件,但没有碰撞反馈(= 碰撞物体接触时速度/位置的变化),则确保这些物体的 collisionBitMask 与在一起为 0。这允许物体产生接触事件,但相互传递。对创建触发器很有用,即当玩家进入某个区域时,您可以让他通过但也可以触发游戏事件。

或者当您想对两个不同类别的实体使用相同的接触位掩码时,它们应该只与共享同一类别的其他实体发生碰撞/接触 - 即应该与其他所有物体具有相同接触行为的常规和“幽灵”角色在游戏中,除非他们不应该与他们的实体(非幽灵)对手接触/碰撞。在这种情况下,对两个主体使用相同的contactBitMaskcollisionBitMask。然后为所有其他类别设置categoryBitMask 中的所有标志,但不要为代表实体和幽灵实体的两个类别设置标志。即如果实体类别为 2,幽灵类别为 8,categoryBitMask 应为:0xFFFFFFFF - 2 - 8 = 0xFFFFFFF5(低 8 位为:11110101)

长话短说:categoryBitMask 标志应从 0xFFFFFFFF 中清除(而不是设置为特定位),以便特定类别中的各个主体不相互联系,但仍与所有其他主体联系。

【讨论】:

这真的很有趣。感谢您清理 Steffen!

以上是关于碰撞时,我的玩家以外的物理实体不会调用 didBeginContact的主要内容,如果未能解决你的问题,请参考以下文章

HLSDK系列groupinfo的基本用法

玩家与地板碰撞时不会移动

使用固定的SKPhysicsJoint将平台检测命中框作为子项附加到玩家精灵更改玩家的碰撞和检测位掩码

如何在与另一个物理体碰撞时阻止物体失去位置

Swift SpriteKit 基本接触/碰撞

Sprite Kit 从未检测到我的碰撞