我有一个 SCNPlane,当检测到水平表面有足够的区域时,它会添加到场景中。根据放置的地板/ table ,飞机似乎被放置在正确的位置。问题是当我将一个 SCNNode(无论是盒子、金字塔、3D 模型等)放到飞机上时,它最终会找到一个着陆点,并且 99% 的人开始疯狂地摇晃。很少有它只是降落而根本没有移动。我也认为这可能是由于节点掉落并降落在平面表面下方。它不是“在上面”,也不是“在”平面下面。也许这个节点吓坏了,因为它在两个级别之间摇摇欲坠?

这是正在发生的事情的视频,您可以在开头看到盒子在飞机的下方和上方,橙色盒子在与深蓝色盒子碰撞时确实停止了,但是当绿色盒子碰撞时确实会回到摇晃的方式盒子最后与它碰撞:

代码在 github

我还将展示一些嵌入在代码中的相关部分:

我只是创建一个 Plane 类以在需要时添加到场景中

class Plane: SCNNode {
var anchor :ARPlaneAnchor
var planeGeometry :SCNPlane!

init(anchor :ARPlaneAnchor) {
    self.anchor = anchor
    super.init()
    setup()
}

func update(anchor: ARPlaneAnchor) {
    self.planeGeometry.width = CGFloat(anchor.extent.x)
    self.planeGeometry.height = CGFloat(anchor.extent.z)
    self.position = SCNVector3Make(anchor.center.x, 0, anchor.center.z)
    let planeNode = self.childNodes.first!
    planeNode.physicsBody = SCNPhysicsBody(type: .static, shape: SCNPhysicsShape(geometry: self.planeGeometry, options: nil))
}

private func setup() {
    //plane dimensions
    self.planeGeometry = SCNPlane(width: CGFloat(self.anchor.extent.x), height: CGFloat(self.anchor.extent.z))
    //plane material
    let material = SCNMaterial()
    material.diffuse.contents = UIImage(named: "tronGrid.png")
    self.planeGeometry.materials = [material]
    //plane geometry and physics
    let planeNode = SCNNode(geometry: self.planeGeometry)
    planeNode.physicsBody = SCNPhysicsBody(type: .static, shape: SCNPhysicsShape(geometry: self.planeGeometry, options: nil))
    planeNode.physicsBody?.categoryBitMask = BodyType.plane.rawValue
    planeNode.position = SCNVector3Make(anchor.center.x, 0, anchor.center.z)
    planeNode.transform = SCNMatrix4MakeRotation(Float(-Double.pi / 2.0), 1, 0, 0)
    //add plane node
    self.addChildNode(planeNode)
}

这是 View Controller
enum BodyType: Int {
    case box = 1
    case pyramid = 2
    case plane = 3
}
class ViewController: UIViewController, ARSCNViewDelegate, SCNPhysicsContactDelegate {
    //outlets
    @IBOutlet var sceneView: ARSCNView!
    //globals
    var planes = [Plane]()
    var boxes = [SCNNode]()
    //life cycle
    override func viewDidLoad() {
        super.viewDidLoad()
        //set sceneView's frame
        self.sceneView = ARSCNView(frame: self.view.frame)
        //add debugging option for sceneView (show x, y , z coords)
        self.sceneView.debugOptions = [ARSCNDebugOptions.showFeaturePoints, ARSCNDebugOptions.showWorldOrigin]
        //give lighting to the scene
        self.sceneView.autoenablesDefaultLighting = true
        //add subview to scene
        self.view.addSubview(self.sceneView)
        // Set the view's delegate
        sceneView.delegate = self
        //subscribe to physics contact delegate
        self.sceneView.scene.physicsWorld.contactDelegate = self
        //show statistics such as fps and timing information
        sceneView.showsStatistics = true
        //create new scene
        let scene = SCNScene()
        //set scene to view
        sceneView.scene = scene
        //setup recognizer to add scooter to scene
        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapped))
        sceneView.addGestureRecognizer(tapGestureRecognizer)
    }
    //MARK: helper funcs
    @objc func tapped(recognizer: UIGestureRecognizer) {
        let scnView = recognizer.view as! ARSCNView
        let touchLocation = recognizer.location(in: scnView)
        let touch = scnView.hitTest(touchLocation, types: .existingPlaneUsingExtent)
        //take action if user touches box
        if !touch.isEmpty {
            guard let hitResult = touch.first else { return }
            addBox(hitResult: hitResult)
        }
    }

    private func addBox(hitResult: ARHitTestResult) {
        let boxGeometry = SCNBox(width:  0.1,
                                 height: 0.1,
                                 length: 0.1,
                                 chamferRadius: 0)
        let material = SCNMaterial()
        material.diffuse.contents = UIColor(red:    .random(),
                                            green:  .random(),
                                            blue:   .random(),
                                            alpha:  1.0)
        boxGeometry.materials = [material]
        let boxNode = SCNNode(geometry: boxGeometry)
        //adding physics body, a box already has a shape, so nil is fine
        boxNode.physicsBody = SCNPhysicsBody(type: .dynamic, shape: nil)
        //set bitMask on boxNode, enabling objects with diff categoryBitMasks to collide w/ each other
        boxNode.physicsBody?.categoryBitMask = BodyType.plane.rawValue | BodyType.box.rawValue
        boxNode.position = SCNVector3(hitResult.worldTransform.columns.3.x,
                                      hitResult.worldTransform.columns.3.y + 0.3,
                                      hitResult.worldTransform.columns.3.z)
        self.sceneView.scene.rootNode.addChildNode(boxNode)
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        let configuration = ARWorldTrackingConfiguration()
        configuration.planeDetection = .horizontal
        //track objects in ARWorld and start session
        sceneView.session.run(configuration)
    }
    //MARK: - ARSCNViewDelegate
    func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
        //if no anchor found, don't render anything!
        if !(anchor is ARPlaneAnchor) {
            return
        }
        DispatchQueue.main.async {
            //add plane to scene
            let plane = Plane(anchor: anchor as! ARPlaneAnchor)
            self.planes.append(plane)
            node.addChildNode(plane)
            //add initial scene object
            let pyramidGeometry = SCNPyramid(width: CGFloat(plane.planeGeometry.width / 8), height: plane.planeGeometry.height / 8, length: plane.planeGeometry.height / 8)
            pyramidGeometry.firstMaterial?.diffuse.contents = UIColor.white
            let pyramidNode = SCNNode(geometry: pyramidGeometry)
            pyramidNode.name = "pyramid"
            pyramidNode.physicsBody = SCNPhysicsBody(type: .dynamic, shape: nil)
            pyramidNode.physicsBody?.categoryBitMask = BodyType.pyramid.rawValue | BodyType.plane.rawValue
            pyramidNode.physicsBody?.contactTestBitMask = BodyType.box.rawValue
            pyramidNode.position = SCNVector3(-(plane.planeGeometry.width) / 3, 0, plane.planeGeometry.height / 3)
            node.addChildNode(pyramidNode)
        }
    }
    func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
        let plane = self.planes.filter {
            plane in return plane.anchor.identifier == anchor.identifier
        }.first

        if plane == nil {
            return
        }

        plane?.update(anchor: anchor as! ARPlaneAnchor)
    }
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        //pause session
        sceneView.session.pause()
    }
}

最佳答案

我想我遵循了相同的教程。我也有同样的结果。原因是因为当立方体从更高的地方落下时,它会加速并且没有准确地击中平面而是通过了。如果将立方体缩小到“1 毫米”,您可以看到盒子完全穿过平面并继续落在平面下方。您可以尝试从更靠近平面的地方放下立方体,盒子会下降得更慢,并且不会发生这种“摇晃”。或者您可以尝试使用高度较小的盒子而不是平面。

10-08 05:57