【问题标题】:What is causing node to slow down despite velocity remaining constant?尽管速度保持不变,是什么导致节点变慢?
【发布时间】:2019-03-02 16:11:18
【问题描述】:

我有一个 SCNSphere 正在爬 45 度的山坡。

节点保持一致的速度,直到每个级别的同一点,在该点它意外地下降速度,这是一个 10 秒的问题片段。

速度下降发生在this clip 中的 8 秒。

当节点到达 -240 的 z 位置时,似乎整个游戏速度减半。

我通过以下方式对此进行了测试,但始终没有成功。

  • 尝试在没有重力的情况下进行测试。
  • 在不与山丘相撞的情况下进行了测试。
  • 尝试在没有阻尼或摩擦的情况下进行测试。
  • 尝试打印节点速度以注意到任何变化,尽管速度在 z 轴上保持在 -5.0 的持续时间 尽管速度显着下降,但仍保持水平。
  • 尝试打印物理世界速度以注意任何变化,尽管速度在持续时间保持在 1.0 尽管速度显着下降,但仍保持水平。
  • 检查了帧速率下降,尽管它保持 60 fps 的帧速率,总共只进行了 14 次绘制调用,多边形计数低于 15k。

使用以下函数在渲染器中的每一帧更新球体的速度。

func updatePositions() {

        if let playerPhysicsBod = playerNode.physicsBody {
            playerPhysicsBod.velocity.x = (lastXPosition - playerNode.position.x) * 8
            playerPhysicsBod.velocity.z = -5
            print("player velocity is \(playerNode.physicsBody!.velocity)")
        }
    }

func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
        updatePositions()
        updateSegments()
    }

这个项目是一个新项目,所以行数不多,我将整个代码库包含在下面。

class GameViewController: UIViewController {


    let scene = SCNScene(named: "art.scnassets/gameScene.scn")!
    let jump = SCNScene(named: "art.scnassets/jump.scn")!.rootNode.childNode(withName: "jump", recursively: true)!
    let box = SCNScene(named: "art.scnassets/box.scn")!.rootNode.childNode(withName: "box", recursively: true)!
    var playerNode = SCNNode()
    var cameraNode = SCNNode()
    var lastXPosition = Float()
    var floorSegments = [SCNNode]()


    override func viewDidLoad() {
        super.viewDidLoad()

        // retrieve the SCNView
        let scnView = self.view as! SCNView
//        scnView.isJitteringEnabled = true
        scnView.scene = scene
        scnView.delegate = self
        scnView.showsStatistics = true

        setupCamera()
        setupPlayer()
        setupSegments()
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {

        guard let tLocationX = touches.first?.location(in: self.view).x else { return }
        let ratio = tLocationX / self.view.frame.maxX
        lastXPosition = Float((5 * ratio) - 2.5)

    }

    func setupCamera() {
        if let camera = scene.rootNode.childNode(withName: "camera", recursively: true) {
            cameraNode = camera
            cameraNode.name = "camera"
        }
    }

    func setupPlayer() {
        if let player = scene.rootNode.childNode(withName: "player", recursively: true) {
            playerNode = player
            playerNode.name = "player"
        }
    }

    func setupSegments() {
        if let segment = scene.rootNode.childNode(withName: "segment", recursively: true) {
            floorSegments.append(segment)
        }
    }




}

extension GameViewController: SCNSceneRendererDelegate {


    func updateSegments() {

        playerNode.position = playerNode.presentation.position
        if let lastSegmentClone = floorSegments.last?.clone() {

            lastSegmentClone.childNodes.forEach { (node) in
                node.removeFromParentNode()
            }


            if abs(playerNode.position.z - lastSegmentClone.position.z) < 30 {
                // set up next segment
                lastSegmentClone.position = SCNVector3(lastSegmentClone.position.x, lastSegmentClone.position.y + 4, lastSegmentClone.position.z - 4)
                floorSegments.append(lastSegmentClone)


                // Add falling blocks to the segment
                for _ in 0...2 {

                    let boxClone = box.clone()
                    let randomX = Int.random(in: -2...2)
                    let randomY = Int.random(in: 1...3)
                    boxClone.eulerAngles.z = Float(GLKMathDegreesToRadians(-45))
                    boxClone.position = SCNVector3(randomX, randomY, -randomY)
                    lastSegmentClone.addChildNode(boxClone)

                }

                // Add falling blocks to the segment
                for (index,segment) in floorSegments.enumerated().reversed() {
                    if segment.position.z > playerNode.position.z + 5 {
                        floorSegments.remove(at: index)
                        segment.childNodes.forEach { (node) in
                            node.removeFromParentNode()
                        }
                        segment.removeFromParentNode()
                    }
                }

                scene.rootNode.addChildNode(lastSegmentClone)
            }
        }
    }

    func updatePositions() {

        if let playerPhysicsBod = playerNode.physicsBody {
            playerPhysicsBod.velocity.x = (lastXPosition - playerNode.position.x) * 8
            playerPhysicsBod.velocity.z = -5
            print("player velocity is \(playerNode.physicsBody!.velocity)")
        }
    }

    func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
        updatePositions()
        updateSegments()
    }

}

【问题讨论】:

  • 不要删除并添加更多的clone(),这很慢。只需隐藏它们,然后在正确的位置取消隐藏。
  • 还有 playerPhysicsBod.allowsResting = false 可能会有所帮助。
  • @E.Coms 感谢您的帮助,不幸的是,我已经在关闭 allowResting 的情况下进行了测试,并且根本没有添加任何克隆。很奇怪的东西
  • 一种可能性是该位置远离原点。在您的场景中,您可以模拟另一种方式并使活动场景围绕原点发生。

标签: ios swift scenekit


【解决方案1】:

不看 scn 文件很难判断。

我希望玩家和地板节点之间没有碰撞......

玩家的位置是一个浮点数,它可能是不精确的相等比较。我认为你应该避免玩家的位置调整,因为它应该几乎相同:

playerNode.position = playerNode.presentation.position

为什么不在空的段节点上保留引用?

setupSegments 方法中克隆段并在删除子节点后保留它。没有必要总是删除子节点:

 lastSegmentClone.childNodes.forEach { (node) in
            node.removeFromParentNode()
        }

另外,我认为您应该“颠倒” updateSegments 中的顺序:

if let lastSegment = floorSegments.last { // no cloning here....

            if abs(playerNode.position.z - lastSegment.position.z) < 30 {

               let lastSegmentClone = lastSegment.clone() // better to use emptySegmentNode

                 /* If empty segment then it's not needed to remove children nodes...
                  lastSegmentClone.childNodes.forEach { (node) in
                   node.removeFromParentNode() */

此外,如果您要删除父节点,子节点将被自动删除....

// Avoid commented code
/*segment.childNodes.forEach { (node) in
                            node.removeFromParentNode()
                        }*/
                        segment.removeFromParentNode()

不确定,但也许对于楼板段,最好使用自定义操作进行移除:

// calculate somehow wait duration, based on the player's position, velocity or use pure constant..
   let removeAction = SCNAction.sequence([SCNAction.waitForDuration(2.0), SCNAction.removeFromParent()])

   lastSegmentClone.run(removeAction)

在这种情况下,您只需要最后一层节点上的引用,以及空的楼层节点。

【讨论】:

  • 原来你答案的第一部分是问题的根源。 playerNode.position = playerNode.presentation.position
猜你喜欢
  • 2012-11-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-20
  • 2021-12-14
  • 1970-01-01
  • 2012-11-19
  • 1970-01-01
相关资源
最近更新 更多