位掩码的原因在于它使您/程序能够轻松快速地计算两个对象之间是否发生碰撞。因此:是的,它是某种优化。
假设我们有以下三个类别:
- 导弹
0x1 << 0
- 玩家
0x1 << 1
- 墙壁
0x1 << 2
现在有一个Player
实例,其类别设置为player
。 它的碰撞位掩码设置为missile | player | wall
(也可以使用+
而不是|
),因为我们希望能够与所有三种类型进行碰撞:其他玩家,关卡墙和正在飞行的子弹/导弹。
现在有一枚类别设置为missile
且碰撞位掩码设置为player | wall
的Missile
:它不会与其他导弹发生碰撞,但会击中玩家和墙壁。
现在,如果我们想要评估两个对象是否可以相互碰撞,我们获取第一个对象的类别位掩码和第二个对象的碰撞位掩码,然后简单地将它们进行&
运算:
上述设置在代码中的样子如下:
let player : UInt8 = 0b1 << 0 // 00000001 = 1
let missile : UInt8 = 0b1 << 1 // 00000010 = 2
let wall : UInt8 = 0b1 << 2 // 00000100 = 4
let playerCollision = player | missile | wall // 00000111 = 7
let missileCollision = player | wall // 00000101 = 5
后续推理基本上是:
if player & missileCollision != 0 {
print("potential collision between player and missile") // prints
}
if missile & missileCollision != 0 {
print("potential collision between two missiles") // does not print
}
我们在这里使用一些位运算,每个比特位代表一个类别。
你可以简单地枚举比特掩码1、2、3、4、5……但是那样你就不能对它们进行任何数学运算。因为你不知道一个作为类别比特掩码的5是否真的是一个类别5,还是既包括了类别1又包括了类别4。
然而只使用比特位,我们可以做到这一点:7的唯一表示方式是2的幂次方的和为4+2+1:因此无论什么对象具有碰撞比特掩码7都会与类别4、2和1发生碰撞。而具有比特掩码5的对象恰好且只是类别1和4的组合——没有其他方式。
现在,由于我们不是在枚举,每个类别使用一个比特位,而普通整数只有32(或64)位,所以我们只能有32(或64)个类别。
看一下以下更详细的代码,它演示了如何以更一般的术语使用掩码:
let playerCategory : UInt8 = 0b1 << 0
let missileCategory : UInt8 = 0b1 << 1
let wallCategory : UInt8 = 0b1 << 2
struct EntityStruct {
var categoryBitmask : UInt8
var collisionBitmask : UInt8
}
let player = EntityStruct(categoryBitmask: playerCategory, collisionBitmask: playerCategory | missileCategory | wallCategory)
let missileOne = EntityStruct(categoryBitmask: missileCategory, collisionBitmask: playerCategory | wallCategory)
let missileTwo = EntityStruct(categoryBitmask: missileCategory, collisionBitmask: playerCategory | wallCategory)
let wall = EntityStruct(categoryBitmask: wallCategory, collisionBitmask: playerCategory | missileCategory | wallCategory)
func canTwoObjectsCollide(first:EntityStruct, _ second:EntityStruct) -> Bool {
if first.categoryBitmask & second.collisionBitmask != 0 {
return true
}
return false
}
canTwoObjectsCollide(player, missileOne)
canTwoObjectsCollide(player, wall)
canTwoObjectsCollide(wall, missileOne)
canTwoObjectsCollide(missileTwo, missileOne)
这里重要的部分是方法canTwoObjectsCollide
并不关心对象的类型或有多少类别。只要你坚持使用位掩码,那么这就是你需要确定两个对象是否可以在理论上发生碰撞的全部内容(忽略它们的位置,这是另一天的任务)。