【问题标题】:Spritekit collisions between arrays of SpriteNodesSpriteNode 数组之间的 Spritekit 冲突
【发布时间】:2020-01-11 10:24:01
【问题描述】:

我正在开发一个涉及多个 Sprite 数组的游戏,我想检测它们之间的碰撞并根据哪些等指定函数。

假设我有一个由 16 个球 ballArray[I] 和 16 个块 blockaArray[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

球阵列也一样,只是 categoryBitMask

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

我仍然不确定为什么一定要这样,但这是今晚的最后一个问题,我在最终工作检测代码中的 && 比较中将两个物体的方向弄错了:

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)")
                    //}
                }
            }
        }

现在正在打印正确的碰撞......可爱!......向前 下一步...再次感谢

【问题讨论】:

  • 您不需要循环遍历in,只需循环遍历let n = Int(body2.categoryBitMask &amp; 0xf) 和类似的i,但无论如何很高兴它正在工作。
  • 好吧,这很聪明。所以 & 0xf 只是删除了类别的相关位并留下剩余的整数?惊人的。谢谢

标签: xcode sprite-kit


【解决方案1】:

一旦@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 个,则这是可能的。无论如何,如果是这种情况,那么您可以做一些轻弹以从物理体中获取 categoryBitMask,将球/块移动 16 位(无论使用高位哪个都被移动),然后获取该位的 log2掩码来获取您的索引。您可以在此处找到 log2 的各种位闪烁技术: https://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious

考虑到每种类型的 16 件事,我会说只做 #4。如果您已经有子类节点,#1 很好。数字 2 有点分散状态,所以我不是这样的粉丝。 3 号我不会真的推荐,因为 NS 的东西。 5 号太可爱了。

编辑:现在我再次阅读,听起来您可能有类别 1...16 的单独 ID,因此您的块类别位掩码如下: blockCategoryMask | ID1blockCategoryMask | ID2 等也可以工作(基本上是#5 的变体)。但是,如果您要走那条路,不妨将索引直接粘贴到类别掩码中:

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

然后方块的物理体得到掩码blockCategoryMask | UInt32(index),球也同样如此。在这种情况下,索引提取只是categoryBitMask &amp; 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)
}

请注意,子类别只是为了顺路而标记;您所有的contactBitMasks 将只处理主要类别。

本质上,您使用的是物理体类别位掩码中有一些额外位来存储随机信息的事实。我以前用简单的精灵做过。但是,如果所需的信息比简单的数字或索引变得更复杂,我建议创建节点的子类,而不是尝试将未使用的信息隐藏在未使用的位中。

【讨论】:

  • 哇好。认为你在那里有正确的答案,但我只需要理解它!确实有 6 个主要类别,然后是多达 26 个子类别。所以理论上我可以检测到 156 种碰撞类型之间的碰撞。您最后的解释看起来我应该怎么做,但我真的不明白该代码如何将索引直接放入 Cat Bit 掩码中。?我想尝试的另一条路线是命名SpriteNodes-即name =“block(i)”。但是不确定 id 是如何作为物理类别进行迭代的
  • 再次感谢 bg2b。用 Buffalo Springfield 的话来说,这里发生了一些事情还不是很清楚......我现在只用 Swift 编码大约 6-7 周,所以这个概念正在伤害我的大脑 - 很多。所以我想我只是插入你建议的代码并尝试理解它。令人惊讶的是,它实际上几乎按照我的意愿做了一些事情,但远非完美。我想在这里发布我拥有的代码和更多解释,但没有空间......并且在有空间的答案部分发布似乎让每个人都感到不安...... StackOverflow 的新手......
  • 您可以编辑原始问题以在最后粘贴更新的代码。
【解决方案2】:

使用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
    }

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-05-31
    • 1970-01-01
    • 2018-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-03
    • 1970-01-01
    相关资源
    最近更新 更多