无法检测 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%,位置会立即跟随。
在 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%,位置会立即跟随。