【问题标题】:ARKit – Collision with Real world objectsARKit - 与现实世界物体的碰撞
【发布时间】:2022-01-23 20:49:11
【问题描述】:

我需要使用 ARKit 检测虚拟对象何时与现实世界对象接触。

有什么办法可以查到吗?

【问题讨论】:

    标签: swift augmented-reality scenekit arkit


    【解决方案1】:

    首先你需要创建一个符合OptionSet协议并且具有bitset类型的属性的碰撞类别结构体:

    import ARKit
    
    struct Category: OptionSet {
    
        let rawValue: Int
        
        static let sphereCategory = Category(rawValue: 1 << 0)
        static let targetCategory = Category(rawValue: 1 << 1)
    }
    

    然后在生命周期方法中设置一个physics delegateSCNPhysicsContactDelegate 协议:

    class ViewController: UIViewController, SCNPhysicsContactDelegate {
    
        @IBOutlet var sceneView: ARSCNView!
    
        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
    
            sceneView.scene = SCNScene()
            sceneView.scene.physicsWorld.contactDelegate = self
    
            let config = ARWorldTrackingConfiguration()
            config.planeDetection = [.horizontal]
            sceneView.session.run(config)
        }
    }
    

    SCNPhysicsContactDelegate 包含 3 个可选的physicsWorld() 方法(稍后我们将使用第一个):

    public protocol SCNPhysicsContactDelegate: NSObjectProtocol {
            
        optional func physicsWorld(_ world: SCNPhysicsWorld, 
                          didBegin contact: SCNPhysicsContact)
    
        optional func physicsWorld(_ world: SCNPhysicsWorld, 
                         didUpdate contact: SCNPhysicsContact)
    
        optional func physicsWorld(_ world: SCNPhysicsWorld, 
                            didEnd contact: SCNPhysicsContact)
    }
    

    在此之后为球体对撞机定义categoryBitMaskcollisionBitMask

    fileprivate func createSphere() -> SCNNode {
    
        var sphere = SCNNode()
        sphere.geometry = SCNSphere(radius: 0.1)
    
        sphere.physicsBody =  .init(type: .kinematic, 
                                   shape: .init(geometry: sphere.geometry!, 
                                                 options: nil))
    
        sphere.physicsBody?.isAffectedByGravity = true
    
        sphere.physicsBody?.categoryBitMask = Category.sphereCategory.rawValue
        sphere.physicsBody?.collisionBitMask = Category.targetCategory.rawValue
        sphere.physicsBody?.contactTestBitMask = Category.targetCategory.rawValue
    
        return sphere
    }
    

    ...并以相反的顺序为现实世界检测到的平面定义位掩码:

    fileprivate func visualizeDetectedPlane() -> SCNNode {
    
        var plane = SCNNode()
        plane.geometry = SCNPlane(width: 0.7, height: 0.7)
    
        plane.physicsBody =  .init(type: .kinematic, 
                                  shape: .init(geometry: plane.geometry!, 
                                                options: nil))
    
        plane.physicsBody?.isAffectedByGravity = false
    
        plane.physicsBody?.categoryBitMask = Category.targetCategory.rawValue
        plane.physicsBody?.collisionBitMask = Category.sphereCategory.rawValue
        plane.physicsBody?.contactTestBitMask = Category.sphereCategory.rawValue
    
        return plane
    }
    

    只有当您将 SCNPlanes 添加到实际检测到的平面并将 SCNSphere 添加到您的 SCNScene 时,您才能使用 physicsWorld(_:didBegin:) 实例方法来检测碰撞:

    func physicsWorld(_ world: SCNPhysicsWorld, 
             didBegin contact: SCNPhysicsContact) {
    
        if contact.nodeA.physicsBody?.categoryBitMask == 
                                              Category.targetCategory.rawValue |
           contact.nodeB.physicsBody?.categoryBitMask == 
                                              Category.targetCategory.rawValue {
    
            print("BOOM!")
        }
    }
    

    【讨论】:

    • @AndyFedoroff 非常感谢。我检查了两个答案,但我无法得到想要的结果。我正在加载一个实体 (.usdz) 并尝试添加碰撞,以便它与墙壁或沙发等现实世界的对象发生碰撞。
    • 我检查过了,但它现在可以工作了,模型可以穿过墙壁或其他物体
    • 不明白:workingnot working?
    • 当然,您可以将其作为问题发布,但我目前没有时间回答任何问题。我的日程安排非常繁忙。
    • 好的。感谢您的宝贵时间。
    猜你喜欢
    • 2017-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-07
    • 1970-01-01
    • 2012-04-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多