SpriteKit中的碰撞(Swift 4和iOS 11)

这篇文章的诞生是为了帮助读者理解SpriteKit中的冲突是如何工作的,因为我个人在理解该主题时遇到了一些困难。 在开始本文学习之前,建议您学习Swift,SpriteKit和二进制编码的十进制数字表示的基础知识,这是理解本主题所必需的。 无论如何,您可以在本文结尾处找到一些有用的链接。

SpriteKit中的碰撞基于蒙版的概念。 对于场景中的每个Sprite节点,您可以分配一个物理主体,以模拟其上的物理动力学。 该物理体自身具有与碰撞有关的三个属性: 类别蒙版碰撞蒙版接触蒙版

类别遮罩

类别掩码指示物理实体(与节点关联)所属的类别。 例如,您可以考虑存在两个精灵节点的场景:蝙蝠和石头。 如果将数字1分配给蝙蝠类别蒙版,将数字2分配给宝石类别蒙版,则表示蝙蝠和宝石属于不同的类别。 因此,类别遮罩用于对场景中的精灵节点进行分类。

防撞面罩

碰撞掩码指示节点可以与哪个类别碰撞。 SpriteKit中的碰撞意味着可以避免两个节点卡住。 例如,如果蝙蝠与石头碰撞,则意味着蝙蝠会受到物理碰撞效果的影响,因此它可能会在碰撞后返回。 现在,为了更好地了解碰撞的工作原理,请开始在Xcode上创建一个新项目,并在GameScene.sks中创建三个SKSpriteNode。 然后将这三个精灵节点重命名为玩家,蝙蝠和石头。

完成操作后,向场景中的每个精灵添加一个物理物体。 选择Alpha蒙版作为蝙蝠和石头的主体类型,而选择边界矩形作为玩家的主体类型:

现在,对于每个精灵,您将具有一些碰撞属性,即类别蒙版,碰撞蒙版和接触蒙版。 现在,分别为蝙蝠和石头设置这些属性,如下图所示:

当两个物体相互接触时,SpriteKit通过在物体A的碰撞蒙版和物体B的类别蒙版之间执行“与”运算来确定是否发生碰撞。如果此操作的结果为非零值,发生碰撞。 假设第一个图像与蝙蝠有关,第二个图像与石头有关。 当蝙蝠与石头接触时,SpriteKit在蝙蝠的碰撞蒙版与石头的类别蒙版之间执行“与”运算:

蝙蝠碰撞屏蔽 = 1 = 0001(基于4位的二进制表示)
岩石类别遮罩 = 0 = 0000
AND = 0000 = 0的结果

岩石碰撞遮罩 = 0 = 0000(基于4位的二进制表示)
蝙蝠类别遮罩 = 1 = 0001
AND = 0000 = 0的结果

结果为0,因此我们可以推断出在这种情况下,蝙蝠和岩石不会碰撞。 现在来看另一个示例:

蝙蝠防撞面罩 = 2 = 0010
摇滚类别蒙版 = 2 = 0010
AND = 0010 = 2的结果
=>蝙蝠会受到碰撞的影响

防撞面具 = 2 = 0010
蝙蝠类别遮罩 = 2 = 0001
AND = 0000 = 0的结果
=>岩石不会受到碰撞的影响

在这种情况下,蝙蝠和岩石会在它们之间碰撞,但是只有蝙蝠会受到碰撞的物理影响。 岩石不会受到影响,因此它将保持在原位。 另一个例子:

蝙蝠防撞面罩 = 2 = 0010
摇滚类别蒙版 = 2 = 0010
AND = 0010 = 2的结果
=>蝙蝠会受到碰撞的影响

防撞面具 = 3 = 0011
蝙蝠类别遮罩 = 1 = 0001
AND = 0001 = 1的结果
=>岩石会受到碰撞的影响

在这种情况下,蝙蝠和岩石会在它们之间发生碰撞,并且两者都将受到碰撞的物理影响。

简约面膜

联系人掩码指示节点导致相交通知的类别。 这意味着如果一个节点与另一个节点相交,则SpriteKit会通过调用SKPhysicsContactDelegate中定义的didEnter()函数来通知该节点。 关于碰撞,当主体A的接触掩模与主体B的类别掩模之间的与运算给出非零值时,通知两个节点之间的交点。

要检测场景中的联系人,您必须通过在类声明附近添加代表场景的类,使其符合SKPhysicsContactDelegate协议:

蝙蝠接触面罩 = 1 = 0001
摇滚类别蒙版 = 4 = 0100
AND = 0000 = 0的结果
=>蝙蝠不会通知路口

防岩面具 = 2 = 0010
蝙蝠类别遮罩 = 1 = 0001
AND = 0000 = 0的结果
=>岩石不会通知路口

在这种情况下,两个对象之间的交集不会被通知,因此didBegin()函数将不会被调用。 让我们看另一个例子:

蝙蝠接触面罩 = 1 = 0001
摇滚类别蒙版 = 4 = 0100
AND = 0000 = 0的结果
=>蝙蝠不会通知路口

防岩面具 = 3 = 0011
蝙蝠类别遮罩 = 1 = 0001
AND = 0001 = 1的结果
=>岩石会通知路口

在这种情况下,交集将由第二个对象通知,因此将调用didBegin()函数。 为了使物体参与碰撞,我们可以使用该函数的contact参数。 contact.bodyA是碰撞的第一个主体,而contact.bodyB是第二个碰撞。

在代码中实现冲突

既然您已经了解了所有前面的概念,那么该是时候了解如何在代码上实现它了。 处理代码冲突的好处是什么? 让我们做一个例子。 想想一个游戏,其中有许多不同种类的精灵,并且您只想检测其中一些精灵之间的碰撞。 如果我希望玩家与场景中的所有敌人发生碰撞,则必须计算各个类别蒙版之间的所有OR,并将获得的结果作为玩家的碰撞蒙版。 如果将来想添加另一种不同类型的敌人,则可能必须重新计算防撞面罩。 为了避免所有这些问题,您可以在代码中完成。

首先要做的是用枚举定义所有类别掩码。 因为在SpriteKit中,掩码以32位无符号整数表示,所以枚举的类型为UInt32:

标记0b用于以二进制表示形式表示值。 将此枚举放在Scene类声明之前。 我们希望玩家可以与蝙蝠和石头碰撞,但石头和蝙蝠之间不能碰撞。 此外,我们希望只有玩家才能响应碰撞。 为此,我们必须以正确的方式设置collisionBitMask和contactTestMask属性:

为了使玩家能够与蝙蝠和石头碰撞并能够响应接触事件,您必须将其碰撞和接触面具设置为等于蝙蝠的类别面具和石头的类别面具之间的“或”:

您可以像这样阅读该表达式: “玩家可以与蝙蝠和石头碰撞” 。 它的工作归功于OR和AND按位运算符的属性。 相反,为了使蝙蝠与石头之间不会发生碰撞并且无法响应接触事件,您必须将蝙蝠的碰撞蒙版设置为与蝙蝠的碰撞蒙版的“否”(否定)相等。蝙蝠和石头的类别面具。 石头碰撞和接触面罩的推理相同。

您可以像这样读这样的表达: “蝙蝠不能与玩家和石头碰撞” 。 在代码中进行碰撞,如果您插入其他类型的敌人,则只需更改碰撞蒙版的逻辑条件,并在其中添加新敌人的类别蒙版即可。

一些提示和技巧

由于碰撞和接触面罩适用于按位运算符,因此您可以推断出一些技巧。 第一个技巧是,如果位串不为零,则位串与其自身之间的AND总是给出一个非零值。 因此,从该规则可以推断出,如果将一个节点的冲突掩码与另一个节点的类别掩码相等,则AND将给出一个非零值,因此两个对象之间将发生冲突。 第二个技巧是将与任何位串相加的位串0000与AND一起得出结果0。因此,如果希望节点不与任何子画面碰撞,可以将其类别掩码设置为零。

参考书目

我是计算机工程专业的学生,​​因此不应将本文视为书籍或课程的替代品。 如果您想进一步了解SpriteKit中的碰撞,可以访问以下链接:

SKPhysics身体
将物理模拟添加到节点的对象。 软件开发工具包iOS 7.0+ macOS 10.9+ tvOS 9.0+ watchOS 3.0+将对象分配给… developer.apple.com SKPhysicsContactDelegate – SpriteKit | Apple开发人员文档
在以下情况下,您可以使用联系人委托来播放声音或执行游戏逻辑,例如提高玩家的分数: developer.apple.com didBegin(_ 🙂 – SKPhysicsContactDelegate | Apple开发人员文档
编辑描述 developer.apple.com

如果您想进一步了解按位运算符的工作方式,可以访问以下链接:

二进制编码的十进制–维基百科
在计算和电子系统中,二进制编码的十进制(BCD)是一类十进制数字的二进制编码… en.wikipedia.org 布尔代数–维基百科
在数学和数学逻辑中,布尔代数是代数的分支,其中变量的值… en.wikipedia.org Swift编程语言(Swift 4.1):高级运算符
Swift的权威指南,Apple用于构建iOS,macOS,watchOS和tvOS应用程序的编程语言。 developer.apple.com