为什么SCNNode会“跳动"?何时放到SCNPlane上? [英] Why is SCNNode "jiggling" when dropped onto SCNPlane?

查看:146
本文介绍了为什么SCNNode会“跳动"?何时放到SCNPlane上?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a SCNPlane that is added to the scene when a sufficient area is detected for a horizontal surface. The plane appears to be placed in a correct spot, according to the floor/table it's being placed on. The problem is when I drop a SCNNode(this has been consistent whether it was a box, pyramid, 3D-model, etc.) onto the plane, it will eventually find a spot to land and 99% start jiggling all crazy. Very few times has it just landed and not moved at all. I also think this may be cause by the node being dropped and landing slightly below the plane surface. It is not "on top" neither "below" the plane. Maybe the node is freaking out because it's kind of teetering between both levels?

这是正在发生的事情的视频,您可以从一开始就看到该框位于飞机的下方和上方,而橙色框在与深蓝色框碰撞时确实停止了,但确实回到了它的摆动状态当绿色框最后与之碰撞时:

Here is a video of what's going on, you can see at the beginning that the box is below and above the plane and the orange box does stop when it collides with the dark blue box, but does go back to its jiggling ways when the green box collides with it at the end:

代码在 github

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

I will also show some of the relevant parts embedded in code:

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

I just create a Plane class to add to the scene when I need to

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

这是ViewController

This is the ViewController

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毫米",您会看到盒子完全穿过平面,并继续下降到平面以下.
您可以尝试将立方体从更近的地方放到飞机上,盒子放慢些,并且不会发生这种跳动".或者,您可以尝试使用高度较小的盒子而不是飞机.

I think i followed the same tutorial. I also had same result. Reason is because when the cube drops from higher place, it accelerates and doesnot exactly hit on the plane but passes through. If you scale down the cube to '1 mm' you can see box completely passes through plane and continue falling below plane.
You can try droping cube from nearer to the plane, box drops slower and this 'jiggling' will not occur. Or you can try with box with small height instead of plane.

这篇关于为什么SCNNode会“跳动"?何时放到SCNPlane上?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆