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

发布于 2025-01-13 01:58:09 字数 4285 浏览 0 评论 0原文

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

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

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

如果节点是不同根节点的子节点,是否不可能检测到冲突? 或者也许我的代码中遗漏了一些东西? contactDelegate 设置如下: sceneView.scene.physicalsWorld.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 的子节点,则一切正常。代码>.

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

除了我已经实施的解决方法之外,我还可以采取其他措施来完成这项工作吗?

谢谢!

In an AR app, I want to detect collisions between the user walking around and the walls of an AR node that I construct. In order to do that, I create an invisible cylinder right in front of the user and set it all up to detect collisions.

The walls are all part of a node which is a child of sceneView.scene.rootNode.
The cylinder, I want it to be a child of sceneView.pointOfView so that it would always follow the camera.
However, when I do so, no collisions are detected.

I know that I set it all up correctly, because if instead I set the cylinder node as a child of sceneView.scene.rootNode as well, I do get collisions correctly. In that case, I continuously move that cylinder node to always be in front of the camera in a renderer(updateAtTime ...) function. So I do have a workaround, but I'd prefer it to be a child of pointOfView.

Is it impossible to detect collisions if nodes are children of different root nodes?
Or maybe I'm missing something in my code?
The contactDelegate is set like that:
sceneView.scene.physicsWorld.contactDelegate = self so maybe this only includes sceneView.scene, but will exclude sceneView.pointOfView???
Is that the issue?

Here's what I do:

I have a separate file to create and configure my cylinder node which I call 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
    
}

Now in my viewController.swift file I call this function and set is as a child of either root nodes:

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

(Don't worry right now about not checking and unwrapping).
The above does not detect collisions.
But if instead I add it like so:

sceneView.scene.rootNode.addChildNode(pov!)

then collisions are detected just fine.

But then I need to always move this cylinder to be in front of the camera and I do it like that:

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
        }
}

For completeness, here's the code I use to configure the node of walls in ViewController (they're built elsewhere in another function):

        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

And here's my physycsWorld(didBegin contact) code:

    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
        }
    }

So I print something to the console and I also turn on a label on the view so I'll see that collision was detected (and the walls indeed move as a whole when it works).

So Again, it all works fine when the pov node is a child of sceneView.scene.rootNode, but not if it's a child of sceneView.pointOfView.

Am I doing something wrong or is this a limitation of collision detection?

Is there something else I can do to make this work, besides the workaround I already implemented?

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

好听的两个字的网名 2025-01-20 01:58:10

关于圆柱体的定位:

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

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%,排名会立即跟随。

Regarding the positioning of your cyliner:

instead to use the render update at time, you better use a position constraint for your cylinder node to move with the point of view. the result will be the same, as if it were a child of the point of view, but collisions will be detected, because you add it to the main rootnode scenegraph.

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]

There is also an influence factor you can configure. By default the influence is 100%, the position will immediatly follow.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文