ios - 为什么 SCNNode "jiggling"掉落到 SCNPlane 上?

标签 ios swift scenekit arkit scnnode

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

这是一段视频,您可以在开始时看到盒子在平面下方和上方,橙色盒子在与深蓝色盒子碰撞时确实停止了,但又回到了摇晃的方式当绿色盒子最后与它碰撞时:

box jiggle

代码在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 毫米”,您可以看到盒子完全穿过平面并继续下降到平面以下。
您可以尝试将立方体从离平面较近的地方放下,盒子下降得更慢,并且不会发生这种“抖动”。或者您可以尝试使用高度较小的盒子而不是平面。

关于ios - 为什么 SCNNode "jiggling"掉落到 SCNPlane 上?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48329115/

相关文章:

ios - Firebase 功能电子邮件发送问题

ios - 呈现一个背景昏暗的 View Controller

swift - 高质量渲染– RealityKit vs SceneKit vs Metal

ios - 如何在 SceneKit 中加载多个对象?

iOS 画笔硬度像 Photoshop

ios - 我可以在 IOS 中获取下载的 M4A、MP3 文件吗?

iphone - 在我的设备上使用 Xcode (iPhone 6+) 进行编译时出错

ios - SCNNode 边界大小和缩放

iphone - 24小时模拟时钟

iphone - 如何添加JSON框架