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