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



在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的子节点,一切都不好。

我是做错了什么,还是这是碰撞检测的限制?

除了我已经实现的变通方法之外,我还能做些什么来实现这一点吗?

谢谢!

关于您的气缸定位:

与其在时间上使用渲染更新,不如使用圆柱体节点的位置约束来随视点移动。结果将是相同的,就好像它是视点的子节点一样,但会检测到冲突,因为您将其添加到了主rootnode场景图中。

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%,位置将立即跟随。

最新更新