我正在学习使用SpriteKit
,我正在学习colllisions的教程。我正在努力理解以下代码:
struct PhysicsCategory {
static let None : UInt32 = 0
static let All : UInt32 = UInt32.max
static let Monster : UInt32 = 0b1 // 1
static let Projectile: UInt32 = 0b10 // 2
}
为什么我们要分配这些名为bitMaps
的内容,以及它们如何在以下代码中使用?:
func didBegin(_ contact: SKPhysicsContact) {
// 1
var firstBody: SKPhysicsBody
var secondBody: SKPhysicsBody
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
firstBody = contact.bodyA
secondBody = contact.bodyB
} else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
// 2
if ((firstBody.categoryBitMask & PhysicsCategory.Monster != 0) &&
(secondBody.categoryBitMask & PhysicsCategory.Projectile != 0)) {
if let monster = firstBody.node as? SKSpriteNode, let
projectile = secondBody.node as? SKSpriteNode {
projectileDidCollideWithMonster(projectile: projectile, monster: monster)
谢谢!
答案 0 :(得分:5)
BitMasks是用于描述二进制格式项目的标志
所以想象一下你有8种方法可以形容。 (在Spritekit中你有32个)
我们可以将这8个东西整合到一个字节中,因为8位在一个字节中,这样我们就可以节省空间并更快地执行操作。
以下是8个描述的示例
Attackable 1 << 0
Ranged 1 << 1
Undead 1 << 2
Magic 1 << 3
Regenerate 1 << 4
Burning 1 << 5
Frozen 1 << 6
Poison 1 << 7
现在我有一个射手,想要归类他。我想说他是一个生活友好的单位,其中包括
我会使用categoryBitmask
对他进行分类:
archer.categoryBitmask = Ranged
这将以1字节表示为
00000010
||||||||_ Attackable
|||||||_ Ranged
||||||_ Undead
|||||_ Magic
||||_ Regenerate
|||_ Burning
||_ Frozen
|_ Poison
现在让我们说他的箭是火箭,我会把它归类为:
arrow.categoryBitmask = Burning
00100000
||||||||_ Attackable
|||||||_ Ranged
||||||_ Undead
|||||_ Magic
||||_ Regenerate
|||_ Burning
||_ Frozen
|_ Poison
最后,我们有一个可以被击中并随时间再生的僵尸
zombie.categoryBitmask = Attackable + Undead + Regenerate
00010101
||||||||_ Attackable
|||||||_ Ranged
||||||_ Undead
|||||_ Magic
||||_ Regenerate
|||_ Burning
||_ Frozen
|_ Poison
现在我希望我的箭头只能点击Attackable
精灵(在这种情况下是僵尸)
我会设置contactTestBitmask
告诉箭头他能打什么
arrow.contactTestBitmask = Attackable 00000001
现在我们需要检查箭头何时击中僵尸,这是didBeginContact
进来的地方
didBeginContact
会做什么,是通过使用AND操作查找匹配项来检查移动项目的contactTestBitmask
到它所点击的categoryBitmask
在我们的案例中
arrow.contactTestBitmask = 00000001
zombie.categoryMask = 00010101 AND
--------
00000001
因为我们的价值是&gt; 0,联系成功。
这意味着didBegins被解雇了。
现在我们在didBegins,我们需要确定哪个物理体是我们的箭头,哪个物理体是我们的僵尸
这是下一个声明的来源
func didBegin(_ contact: SKPhysicsContact) {
// 1
var firstBody: SKPhysicsBody
var secondBody: SKPhysicsBody
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
firstBody = contact.bodyA
secondBody = contact.bodyB
} else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
由于arrow = 00100000和zombie = 00010101,我们知道僵尸的值低于箭头,所以在这种情况下,zombie是&lt;箭头。
我们将firstBody
分配给僵尸,将secondBody
分配给箭头
现在我们需要提供一个条件。
我们想说一个不死生物是否被可燃物体击中,就可以做点什么了。
所以在代码中这将是
if (firstBody & Undead > 0) && (secondBody & Burning > 0)
{
//burn zombie
}
但是如果箭头是冰箭怎么办?我们不想进入if语句。
现在我们可以添加第二个条件,让我们冻结僵尸。
if (firstBody & Undead > 0) && (secondBody & Frozen > 0)
{
//freeze zombie
}
这些ifs正在做什么,是确保身体已打开某些功能,然后执行一些操作以响应它们。
要了解有关位掩码如何工作的更多信息,我将研究如何制作真值表。这基本上就是这个。我们只是创建一些真值表,并试图弄清楚一个陈述是否为真,如果是真的,则执行一个动作。
答案 1 :(得分:1)
操纵contactTest和collison位掩码以启用/禁用特定的接触和碰撞。
对于这个例子,我们将使用4个主体,为简单起见,仅显示位掩码的最后8位。这4个实体是3个SKSpriteNodes(每个都有一个物理体)和一个边界:
let edge = frame.insetBy(dx: 0, dy: 0)
physicsBody = SKPhysicsBody(edgeLoopFrom: edge)
请注意,'edge'物理主体是场景的物理主体,而不是节点。
我们定义了4个独特的类别
let purpleSquareCategory: UInt32 = 1 << 0 // bitmask is ...00000001
let redCircleCategory: UInt32 = 1 << 1 // bitmask is ...00000010
let blueSquareCategory: UInt32 = 1 << 2 // bitmask is ...00000100
let edgeCategory: UInt32 = 1 << 31 // bitmask is 10000...00000000
为每个物理主体分配它所属的类别:
//Assign our category bit masks to our physics bodies
purpleSquare.physicsBody?.categoryBitMask = purpleSquareCategory
redCircle.physicsBody?.categoryBitMask = redCircleCategory
blueSquare.physicsBody?.categoryBitMask = blueSquareCategory
physicsBody?.categoryBitMask = edgeCategory // This is the edge for the scene itself
如果正文的collisionBitMask中的某个位设置为1,那么它会碰撞(弹回)任何在其categoryBitMask中位于相同位置的“1”的主体。同样适用于contactTestBitMask。
除非您另行指定,否则所有内容都会与其他所有内容发生冲突,并且不会生成任何联系人(当任何内容与其他内容联系时,您的代码都不会收到通知):
purpleSquare.physicsBody.collisonBitMask = 11111111111111111111111111111111 // 32 '1's.
每个位置的每一位都是'1',所以当与任何其他categoryBitMask相比时,Sprite Kit会发现'1',因此会发生碰撞。如果您不希望此主体与某个类别发生冲突,则必须将collisonBitMask中的正确位设置为“0”
并将其contactTestbitMask设置为全0:
redCircle.physicsBody.contactTestBitMask = 00000000000000000000000000000000 // 32 '0's
与collisionBitMask相同,除了反转。
可以使用以下方法关闭实体之间的接触或碰撞(保持现有接触或碰撞不变):
nodeA.physicsBody?.collisionBitMask &= ~nodeB.category
我们逻辑地将nodeA的碰撞位掩码与nodeB的类别位掩码的反(逻辑NOT,〜运算符)“关闭”该位nodeA的bitMask。例如,阻止红色圆圈与紫色方块碰撞:
redCircle.physicsBody?.collisionBitMask = redCircle.physicsBody?.collisionBitMask & ~purpleSquareCategory
可以缩短为:
redCircle.physicsBody?.collisionBitMask &= ~purpleSquareCategory
说明:
redCircle.physicsBody.collisonBitMask = 11111111111111111111111111111111
purpleSquareCategory = 00000000000000000000000000000001
~purpleSquareCategory = 11111111111111111111111111111110
11111111111111111111111111111111 & 11111111111111111111111111111110 = 11111111111111111111111111111110
redCircle.physicsBody.collisonBitMask现在等于11111111111111111111111111111110 redCircle不再与类别为...... 0001(purpleSquare)
的物体发生碰撞您可以直接设置它,而不是关闭collsionsbitMask中的各个位:
blueSquare.physicsBody?.collisionBitMask = (redCircleCategory | purpleSquareCategory)
i.e. blueSquare.physicsBody?.collisionBitMask = (....00000010 OR ....00000001)
等于blueSquare.physicsBody?.collisionBitMask = ....00000011
blueSquare只会与类别或..01或..10
的实体发生碰撞可以使用以下任意点打开2个实体之间的接触或碰撞(不影响任何现有的接触或碰撞):
redCircle.physicsBody?.contactTestBitMask |= purpleSquareCategory
我们逻辑上将redCircle的bitMask与purpleSquare的类别位掩码一起“打开”redcircle的bitMask中的那个位。这使得redCircel的bitMas中的任何其他位都不受影响。
您可以确保每个形状“弹回”屏幕边缘,如下所示:
// Make sure everything collides with the screen edge
enumerateChildNodes(withName: "//*") { node, _ in
node.physicsBody?.collisionBitMask |= self.edgeCategory //Add edgeCategory to the collision bit mask
}
注意:
碰撞可以是片面的,即物体A可以碰撞(反弹)物体B,而物体B继续进行,好像什么也没发生过一样。如果你想让2个物体相互反弹,它们必须被告知要与另一个物体相撞:
blueSquare.physicsBody?.collisionBitMask = redCircleCategory
redcircle.physicsBody?.collisionBitMask = blueSquareCategory
然而,联系人不是片面的;如果您想知道对象A何时触摸(接触)对象B,则就对象B设置对象A的接触检测就足够了。您不必在对象B上为对象A设置接触检测。
blueSquare.physicsBody?.contactTestBitMask = redCircleCategory
我们不需要redcircle.physicsBody?.contactTestBitMask= blueSquareCategory