collisionBitMask是如何工作的?Swift/SpriteKit

28
据我所知,物理体默认情况下在相互碰撞时会彼此反弹,直到你将它们的collisionBitMask设置为相等的数字。
然而,由于我认为是由于collisionBitmasks,我正在遇到一个似乎非常简单的巨大问题。
let RedBallCategory  : UInt32 = 0x1 << 1
let GreenBallCategory: UInt32 = 0x1 << 2
let RedBarCategory : UInt32 = 0x1 << 3
let GreenBarCategory : UInt32 = 0x1 << 4
let WallCategory : UInt32 = 0x1 << 5


greenBall.physicsBody?.categoryBitMask = GreenBallCategory
greenBall.physicsBody?.contactTestBitMask = RedBarCategory
greenBall.physicsBody?.collisionBitMask = GreenHealthCategory

redBall.physicsBody?.categoryBitMask = RedBallCategory
redBall.physicsBody?.contactTestBitMask = GreenBarCategory
redBall.physicsBody?.collisionBitMask = RedHealthCategory

let borderBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
self.physicsBody = borderBody
self.physicsBody?.friction = 0
borderBody.contactTestBitMask = RedBallCategory | GreenBallCategory
borderBody.categoryBitMask = WallCategory

这里我有两个球和一个边框。我可以实现碰撞检测,但当我添加了边框的类别位掩码时,它允许球同时穿过屏幕,这不是我想要的。

我还希望球彼此弹开,但只有当我注释掉一个球的categoryBitMasks时它们才会弹开。否则它们会穿过彼此。

对我来说这毫无意义,因为每个物品都有不同的collisionbitmask。有时我设置所有数字都等于5会导致一切都互相穿过,但将其全部设置为6会允许一切彼此碰撞。

碰撞位掩码到底如何工作?是否有一种适当的方式来管理大量交错的碰撞规则?

2个回答

66

由于您尚未正确设置分类、联系人和碰撞位掩码,因此无法获得所期望的行为。以下是一个示例,说明如何设置才能正常工作:

greenBall.physicsBody?.categoryBitMask = GreenBallCategory //Category is GreenBall
greenBall.physicsBody?.contactTestBitMask = RedBarCategory | WallCategory //Contact will be detected when GreenBall make a contact with RedBar or a Wall (assuming that redBar's masks are already properly set)
greenBall.physicsBody?.collisionBitMask = GreenBallCategory | RedBallCategory | WallCategory //Collision will occur when GreenBall hits GreenBall, RedBall or hits a Wall

redBall.physicsBody?.categoryBitMask = RedBallCategory //Category is RedBall
redBall.physicsBody?.contactTestBitMask = GreenBarCategory | GreenBallCategory | WallCategory //Contact will be detected when RedBall make a contact with GreenBar , GreenBall or a Wall
redBall.physicsBody?.collisionBitMask = RedBallCategory | GreenBallCategory | WallCategory //Collision will occur when RedBall meets RedBall, GreenBall or hits a Wall

let borderBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
self.physicsBody = borderBody
self.physicsBody?.friction = 0
borderBody.contactTestBitMask = RedBallCategory | GreenBallCategory //Contact will be detected when red or green ball hit the wall
borderBody.categoryBitMask = WallCategory
borderBody.collisionBitMask = RedBallCategory | GreenBallCategory // Collisions between RedBall GreenBall and a Wall will be detected

我建议您阅读有关categoryBitMask的文档,这是一个定义物理体所属类别的掩码:

场景中的每个物理体最多可以分配到32个不同的类别,每个类别对应掩码中的一位。您可以在游戏中定义使用的掩码值。结合collisionBitMask和contactTestBitMask属性,您可以定义哪些物理体彼此交互以及何时通知游戏这些交互。

contactTestBitMask - 定义与当前物理体发生交集通知的物体类别掩码。

当两个物体占用相同空间时,将通过执行逻辑AND操作来测试每个物体的类别掩码是否与另一个物体的接触掩码相匹配。如果任何一个比较结果为非零值,则创建一个SKPhysicsContact对象并传递给物理世界的委托。为获得最佳性能,请仅设置联系掩码中感兴趣的交互位。

collisionBitmask - 定义可以与此物理体发生碰撞的物理体类别掩码。

当两个物理体彼此接触时,可能会发生碰撞。通过执行逻辑AND操作来比较该物体的碰撞掩码与其他物体的类别掩码。如果结果为非零值,则该物体会受到碰撞的影响。每个物体都独立选择是否希望受到其他物体的影响。例如,您可以使用它来避免对速度变化微不足道的物体进行碰撞计算。

因此,基本上要设置所有这些内容,您应该问自己类似以下问题:

好的,我在场景中有一个绿球、一个红球和一些墙壁对象。我希望哪些物体之间发生碰撞,或者何时注册联系?我想让绿色和红色的球互相碰撞,并与墙壁发生碰撞。没有问题。我将首先适当地设置类别,然后将设置碰撞位掩码如下:

greenBall.collisionBitMask = GreenBallCategory | RedBallCategory | WallCategory; //greenBall will collide with greenBall, redBall and a wall
redBall.collisionBitMask = GreenBallCategory | RedBallCategory | WallCategory

wall.collisionBitMask = GreenBall | RedBall

现在,我想检测某些接触事件(在didBeginContact方法中)... 但我不想收到所有可能的接触事件通知,而是只想收到有关球之间的接触事件通知(球与墙之间的接触事件将被忽略)。 因此,让我们设置contactTestBitMasks以实现此目的:

greenBall.contactTestBitMask = GreenBallCategory | RedBallCategory;
redBall.contactTestBitMask = GreenBallCategory | RedBallCategory;

就是这样。重要的是,当你不使用接触检测时,不应该设置contactTestBitMask,因为这会影响性能。如果你不需要碰撞检测,并且只对检测接触感兴趣,可以将collisionBitMask = 0

重要提示:

确保已经设置了物理世界的接触代理以便使用didBeginContactdidEndContact方法:

self.physicsWorld.contactDelegate = self; //where self is a current scene

希望这有点帮助。


2
惊人的答案,非常感谢你抽出时间写下所有的内容。它完美地运行了。“问自己这个问题”的部分确实澄清了我困惑的地方。非常感谢你! - Barkley
如果你有超过32个怎么办? - cnzac
@Abcdefg 你如何在didEnd函数中检测碰撞的结束? - Marin
非常棒的答案 - 非常感谢!我已经苦苦挣扎了几天,一直在研究这个主题。你的“问自己”的部分是我见过的最好的解释。 - ixany

3

在此需要补充一点,如果接触或碰撞无法正常工作,您需要确保先分配物理体形状,然后再应用位掩码。

不起作用的位掩码示例:

        SKSpriteNode *bird = [SKSpriteNode spriteNodeWithImageNamed:@"bird1.png"];
        bird.name = @"bird";
        bird.physicsBody.categoryBitMask = birdCategory;
        bird.physicsBody.contactTestBitMask = wallCategory | scoreCategory;
        bird.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:bird.size.width/2.7];
        bird.position = CGPointMake(_mySize.width/3.0, 2*_mySize.height/3.0);
        [self addChild:bird];

还有一种工作方式:

    SKSpriteNode *bird = [SKSpriteNode spriteNodeWithImageNamed:@"bird1.png"];
    bird.name = @"bird";
    bird.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:bird.size.width/2.7];
    bird.physicsBody.categoryBitMask = birdCategory;
    bird.physicsBody.contactTestBitMask = wallCategory | scoreCategory;
    bird.position = CGPointMake(_mySize.width/3.0, 2*_mySize.height/3.0);
    [self addChild:bird];

无论如何,这可能只是新手错误,但我想在这里分享,因为这是导致我的问题无法正常工作的原因。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接