精灵节点数组之间的精灵碰撞



我正在开发一款涉及多个精灵阵列的游戏,我想检测它们之间的冲突,并根据不同等指定函数。

假设我有一个由16个球组成的数组ballArray[I]和16个块blockArray[I],我可以使用索引号I轻松地对其进行迭代。

我给这些球一个物理类别——球,类似于方块。然后我有16个ID物理类别,比如ID1、ID2、ID3、ID4

所以我可以检测到碰撞,知道那是一个球击中了一个盖帽,但我需要知道哪个球和哪个盖帽。

最好或最简单的方法是什么?我正在阅读有关enumerateChildNodes(withName)函数的文章,但还没有使用它。或者我可以创建PhysicsCategories的数组,我可以与SpriteArray一起迭代以进行比较和识别。

编辑:

谢谢大家的帮助。我终于破解了它。令人惊讶的是,最终代码比我最初想象的要简单得多。仍然没有完全理解这些部分在我的类别中的位置,但让它发挥作用。

我会试着发布我的最终工作代码——你可能有改进的建议。再次感谢并为我糟糕的StackFlow礼仪道歉-我是新来的:-)

所以我的物理分类被定义了。

struct PhysicsCategories {
static let BoxCategoryMask = UInt32(1<<7)
static let BallCategoryMask = UInt32(1<<8)
}

然后在我的函数中构建一个Sprites 阵列

boxBloqArray[i].physicsBody?.categoryBitMask = PhysicsCategories.BoxCategoryMask | UInt32(i)              
boxBloqArray[i].physicsBody!.contactTestBitMask = PhysicsCategories.BallCategoryMask

球阵列也是如此,但只是类别BitMask

ballBloqArray[i].physicsBody?.categoryBitMask = PhysicsCategories.BallCategoryMask | UInt32(i)

我仍然不确定为什么必须这样,但这是今晚的最后一个问题,我在&amp;最终工作检测代码中的比较:

var body1 = SKPhysicsBody()
var body2 = SKPhysicsBody()
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
body1 = contact.bodyA
body2 = contact.bodyB
}
else {
body1 = contact.bodyB
body2 = contact.bodyA
}

// Check node collisions
for n in 0...15 {
for i in 0...15 {
if body2.categoryBitMask == PhysicsCategories.BallCategoryMask | UInt32(n) && body1.categoryBitMask == PhysicsCategories.BoxCategoryMask | UInt32(i) {
//if body1.node != nil {
print("Ball(n) hit Box(i)")
//}
}
}
}

现在正在打印正确的碰撞。。。。美丽的从开始到下一步。。。再次感谢

正如@Luca Angeletti的回答中所讨论的那样,一旦有两个节点参与了冲突,您就可以通过各种方式将它们转化为索引。

  1. 如果您已经将每种类型的节点都作为一个专门的子类,并且将适当的索引存储为类成员,那么您可以转换到适当的类并查看索引字段,例如
if let block = nodeA as? BlockNode, let ball = nodeB as? BallNode {
print("block (block.blockIndex) hit ball (ball.ballIndex)")
}
  1. 节点是可哈希的,因此您可以使用字典将它们映射回索引:
if let blockIndex = blockIndexes[nodeA], let ballIndex = ballIndexes[nodeB] {
print("block (blockIndex) hit ball (ballIndex)")
}
  1. 您可以使用节点的userData属性来存储您喜欢的任何内容,包括索引。不过,在NS的事情上混得有点难看。https://developer.apple.com/documentation/spritekit/sknode/1483121-userdata

  2. 您可以对每个阵列进行线性扫描。

if let blockIndex = blocks.firstIndex(of: nodeA), let ballIndex = balls.firstIndex(of: nodeB) {
print("block (blockIndex) hit ball (ballIndex)")
}
  1. 从你的问题中听起来,你可能为每个单独的盖帽和每个单独的球都有一个单独的类别位掩码。或者如果你不这样做,如果每个最多有16个,那也是可能的。无论如何,如果是这样的话,那么你可以做一些比特轻弹,从物理体中获取类别BitMask,将球/块一个移位16比特(使用高位的那个移位),然后获取比特掩码的log2来获取索引。您可以在这里找到log2的各种位轻弹技术:https://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious

给定每种类型的16件事,我会说只做第4件。如果您已经有子类节点,#1就可以了。第二个是状态的传播,所以我不太喜欢。第三,我不会真的推荐,因为NS的东西。5号太可爱了。

编辑:现在我又读了一遍,听起来你可能有1…16类的单独ID,所以你的区块类别比特掩码是这样的:blockCategoryMask | ID1blockCategoryMask | ID2等。这也可以工作(基本上是#5的变体)。不过,如果你正沿着这条路走下去,你也可以直接将索引粘贴到类别掩码中:

let blockCategoryMask = UInt32(1<<4)
let ballCategoryMask = UInt32(1<<5)

然后,块的物理体得到掩码blockCategoryMask | UInt32(index),类似地,球也是如此。在这种情况下,索引提取仅为categoryBitMask & UInt32(0xf)。或者,如果你把块和球类别放在第0位和第1位,把索引放在第2-5位,那么右移2就可以得到索引。

编辑以回应评论:好的,让我们以6个不同类别的对象为例,每个对象都可以分为16个不同的子类别之一。为了能够控制报告的联系人,您可以为6个主要类别中的每一个分配一个位掩码:

enum Category: UInt32 {
// Basic categories
case block =    0b000001
case ball =     0b000010
case shot =     0b000100
case obstacle = 0b001000
case wizard =   0b010000
case food =     0b100000
}

由于您已将6位用于主要类别,因此您还有26位。要对16个子类别进行编码,需要4位。您可以将这些放在类别位掩码中的主6位之上。示例操作:

func encodeObject(category: Category, subcategory: Int) -> UInt32 {
return category.rawValue | (UInt32(subcategory) << 6)
}
func nodeIsA(node: SKNode, category: Category) -> Bool {
guard let body = node.physicsBody else { return false }
return (body.categoryBitMask & category.rawValue) != 0
}
func subcategory(node: SKNode) -> Int {
guard let body = node.physicsBody else { fatalError("missing physicsbody") }
return Int(body.categoryBitMask >> 6)
}

请注意,这些子类别只是在某种程度上标记游乐设施;您的所有联系人BitMask都只涉及主要类别。

本质上,你是在利用物理身体类别比特掩码中有一些额外的比特来存储随机信息。我以前用过简单的精灵。但是,如果所需的信息比简单的数字或索引更复杂,我建议创建节点的子类,而不是试图在未使用的部分中堆积东西。

使用contact.bodyA.nodecontact.bodyB.node可以获得联系人中涉及的SKNode(s)

extension GameScene: SKPhysicsContactDelegate {
func didBegin(_ contact: SKPhysicsContact) {
switch (contact.bodyA.node, contact.bodyB.node) {
case (let ball as Ball, let block as Block):
didBeginContactBetween(ball: ball, andBlock: block)
case (let block as Block, let ball as Ball):
didBeginContactBetween(ball: ball, andBlock: block)
default:
break
}
}
func didBeginContactBetween(ball: Ball, andBlock block: Block) {
// TODO: put your code here
}
}

最新更新