无法检测 SceneKit / ARKit 中 rootNode 和 pointOfView 子节点之间的碰撞

Can't detect collision between rootNode and pointOfView child nodes in SceneKit / ARKit

在 AR 应用程序中,我想检测四处走动的用户与我构建的 AR 节点的墙壁之间的碰撞。为此,我在用户面前创建了一个不可见的圆柱体,并将其全部设置为检测碰撞。

墙都是 sceneView.scene.rootNode 的子节点的一部分。 圆柱体,我希望它成为 sceneView.pointOfView 的子对象,以便它始终跟随相机。 但是,当我这样做时,没有检测到碰撞。

我知道我的设置是正确的,因为如果我将圆柱节点也设置为 sceneView.scene.rootNode 的子节点,我确实会正确地得到碰撞。在那种情况下,我不断移动该圆柱体节点,使其始终在 renderer(updateAtTime ...) 函数中的相机前面。所以我确实有一个解决方法,但我更希望它成为 pointOfView 的子项。

如果节点是不同根节点的子节点,是否无法检测到冲突? 或者我的代码中遗漏了什么? contactDelegate 是这样设置的: sceneView.scene.physicsWorld.contactDelegate = self 所以也许这只包括 sceneView.scene,但会排除 sceneView.pointOfView??? 是这个问题吗?

这是我的做法:

我有一个单独的文件来创建和配置我的圆柱节点,我称之为 pov:

import Foundation
import SceneKit


func createPOV() -> SCNNode {
    
    let pov = SCNNode()
    pov.geometry = SCNCylinder(radius: 0.1, height: 4)
    pov.geometry?.firstMaterial?.diffuse.contents = UIColor.blue
    pov.opacity = 0.3    // will be set to 0 when it'll work correctly
    
    pov.physicsBody = SCNPhysicsBody(type: .kinematic, shape: nil)
    pov.physicsBody?.isAffectedByGravity = false
    pov.physicsBody?.mass = 1
    
    pov.physicsBody?.categoryBitMask = BodyType.cameraCategory.rawValue
    pov.physicsBody?.collisionBitMask = BodyType.wallsCategory.rawValue
    pov.physicsBody?.contactTestBitMask = BodyType.wallsCategory.rawValue
    
    pov.simdPosition = simd_float3(0, -1.5, -0.3)   // this position only makes sense when setting as a child of pointOfView, otherwise the position will always be changed by renderer
    
    return pov
    
}

现在在我的 viewController.swift 文件中调用此函数并将其设置为任一根节点的子节点:

pov = createPOV()
sceneView.pointOfView?.addChildNode(pov!)

(现在不用担心不检查和展开)。 以上 检测碰撞。 但是如果我这样添加它:

sceneView.scene.rootNode.addChildNode(pov!)

然后就可以很好地检测到碰撞了。

但是我需要始终将这个圆柱体移动到相机前面,然后我就这样做了:

func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {

        guard let pointOfView = sceneView.pointOfView else {return}
        let currentPosition = pointOfView.simdPosition
        let currentTransform = pointOfView.simdTransform
        let orientation = SCNVector3(-currentTransform.columns.2.x, -currentTransform.columns.2.y, -currentTransform.columns.2.z)
        let currentPositionOfCamera = orientation + SCNVector3(currentPosition)
        
        DispatchQueue.main.async {
            self.pov?.position = currentPositionOfCamera
        }
}

为了完整起见,这里是我用来在 ViewController 中配置墙节点的代码(它们是在其他地方用另一个函数构建的):

        node?.physicsBody = SCNPhysicsBody(type: .dynamic, shape: SCNPhysicsShape(node: node!, options: nil))
        node?.physicsBody?.isAffectedByGravity = false
        node?.physicsBody?.mass = 1
        node?.physicsBody?.damping = 1.0            // remove linear velocity, needed to stop moving after collision
        node?.physicsBody?.angularDamping = 1.0     // remove angular velocity, needed to stop rotating after collision
        node?.physicsBody?.velocityFactor = SCNVector3(1.0, 0.0, 1.0)           // will allow movement only in X and Z coordinates
        node?.physicsBody?.angularVelocityFactor = SCNVector3(0.0, 1.0, 0.0)    // will allow rotation only around Y axis

        node?.physicsBody?.categoryBitMask = BodyType.wallsCategory.rawValue
        node?.physicsBody?.collisionBitMask = BodyType.cameraCategory.rawValue
        node?.physicsBody?.contactTestBitMask = BodyType.cameraCategory.rawValue

这是我的 physycsWorld(didBegin contact) 代码:

    func physicsWorld(_ world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact) {
        
        if contact.nodeA.physicsBody?.categoryBitMask == BodyType.wallsCategory.rawValue || contact.nodeB.physicsBody?.categoryBitMask == BodyType.wallsCategory.rawValue {
            print("Begin COLLISION")           
            contactBeginLabel.isHidden = false
        }
    }

所以我在控制台上打印了一些东西,我还在视图上打开了一个标签,所以我会看到检测到碰撞(并且墙壁在它工作时确实作为一个整体移动)。

所以同样,当 pov 节点是 sceneView.scene.rootNode 的子节点时一切正常,但如果它是 sceneView.pointOfView.

的子节点则不行

我是不是做错了什么或者这是碰撞检测的局限性?

除了我已经实施的解决方法之外,我还能做些什么来使这项工作正常进行吗?

谢谢!

关于你的汽缸的定位:

而不是使用渲染更新,你最好使用一个位置约束来让你的圆柱节点随着视角移动。结果是一样的,就好像它是视点的子节点一样,但是会检测到碰撞,因为您将它添加到主根节点场景图中。

let constraint = SCNReplicatorConstraint(target: pointOfView) // must be a node
constraint.positionOffset           = positionOffset // some SCNVector3
constraint.replicatesOrientation    = false
constraint.replicatesScale          = false
constraint.replicatesPosition       = true

cylinder.constraints = [constraint]

您还可以配置一个影响因素。默认影响力为100%,位置会立即跟随。