didAddNode 与 SceneKit 碰撞检测

didAddNode vs SceneKit Collision Detection

我正在构建一个小型演示,其中两个对象可以相互碰撞。基本上一个物体将被放置在一个平面上。我有以下用于将物理体添加到平面的代码。

 func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {

        if anchor is ARPlaneAnchor {


            let plane = SCNPlane(width: 0.5, height: 0.5)
            let material = SCNMaterial()
            material.isDoubleSided = true
            material.diffuse.contents = UIImage(named: "overlay_grid")
            plane.firstMaterial = material

            let planeNode = SCNNode(geometry: plane)
            planeNode.physicsBody = SCNPhysicsBody(type: .static, shape: nil)
            planeNode.physicsBody?.categoryBitMask = BodyType.plane.rawValue

            planeNode.eulerAngles.x = .pi/2

            node.addChildNode(planeNode)

}

即使添加了飞机,它也不会参与任何物理碰撞。如果我试图在上面放置物体,它会直接穿过它。但是,如果我将最后一行更改为以下行:

// node.addChildNode(planeNode) // INSTEAD OF THIS
   planeNode.position = SCNVector3(anchor.transform.columns.3.x, anchor.transform.columns.3.y, anchor.transform.columns.3.z)
 self.sceneView.scene.rootNode.addChildNode(planeNode) // THIS WORKS

我的理解是,所有与碰撞相关的东西都由 SceneView 维护,为了参与碰撞,我需要将它添加到 SceneView 层次结构而不是 ARSCNView 层次结构。

问题:

// node.addChildNode(planeNode) // WHY THIS DOES NOT WORK
   planeNode.position = SCNVector3(anchor.transform.columns.3.x, anchor.transform.columns.3.y, anchor.transform.columns.3.z)

 self.sceneView.scene.rootNode.addChildNode(planeNode) // WHY THIS WORKS

static 物理体之所以如此命名是因为它们不应该移动(相对于 global/world/scene 坐标 space)。物理引擎内部工作的许多优化都依赖于此,因此更改具有附加静态物理体的节点的位置可能会导致不正确的行为。

ARKit 不断移动由平面检测产生的 ARPlaneAnchors——它从更多不同的角度观察真实世界平面的时间越长,它就越了解该平面的位置和大小。

当您将子节点添加到 renderer(_:didAdd:for:) 中的 ARSCNView-managed 节点时,子节点的 position 可能不会更改...但该位置是相对于其父节点的节点,ARKit 会自动移动父节点以匹配它所在的 ARPlaneAnchor。因此,只要 ARKit 更新平面锚点,子节点就会相对于世界移动。如果你在那个节点上有一个静态物理体,你会变得很奇怪。

当您直接添加一个节点作为场景 rootNode 的子节点并设置其位置 基于 初始对于平面锚,该节点保持静止 — 您是唯一设置其世界-space 位置的节点,而且您只这样做了一次。所以给它一个静态物理体是安全的。

(请注意,如果您希望“静态”物理体行为随时间变化,可以删除物理体并在新位置重新创建它。只是不要这样做经常或者您可能正在击败物理引擎中的其他优化。)