首页 文章

使用 Scenekit sceneTime 遍历 iOS 动画

提问于
浏览
2

我试图修改 Xcode 的默认游戏设置,以便能够:将动画编程到几何图形中,通过动画进行擦洗,然后让用户自动播放动画。

通过基于清理器的值设置视图的场景时间,我设法使动画的清理工作了。但是,当我将 SCNSceneRenderer 上的 isPlaying 布尔值设置为 true 时,它将每帧的时间重置为 0,而我无法使它移出第一帧。

从文档中,我假设这意味着它不会检测到我的动画,并认为所有动画的持续时间均为 0。

这是我的 GameViewController 中的 viewDidLoad 函数:

override func viewDidLoad() {
    super.viewDidLoad()

    // create a new scene
    let scene = SCNScene(named: "art.scnassets/ship.scn")!

    // create and add a camera to the scene
    let cameraNode = SCNNode()
    cameraNode.camera = SCNCamera()
    scene.rootNode.addChildNode(cameraNode)

    // place the camera
    cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)

    // create and add a light to the scene
    let lightNode = SCNNode()
    lightNode.light = SCNLight()
    lightNode.light!.type = .omni
    lightNode.position = SCNVector3(x: 0, y: 10, z: 10)
    scene.rootNode.addChildNode(lightNode)

    // create and add an ambient light to the scene
    let ambientLightNode = SCNNode()
    ambientLightNode.light = SCNLight()
    ambientLightNode.light!.type = .ambient
    ambientLightNode.light!.color = UIColor.darkGray
    scene.rootNode.addChildNode(ambientLightNode)

    // retrieve the ship node
    let ship = scene.rootNode.childNode(withName: "ship", recursively: true)!

    // define the animation
    //ship.runAction(SCNAction.repeatForever(SCNAction.rotateBy(x: 0, y: 2, z: 0, duration: 1)))
    let positionAnimation = CAKeyframeAnimation(keyPath: "position.y")
    positionAnimation.values = [0, 2, -2, 0]
    positionAnimation.keyTimes = [0, 1, 3, 4]
    positionAnimation.duration = 5
    positionAnimation.usesSceneTimeBase = true

    // retrieve the SCNView
    let scnView = self.view as! SCNView
    scnView.delegate = self

    // add the animation
    ship.addAnimation(positionAnimation, forKey: "position.y")

    // set the scene to the view
    scnView.scene = scene

    // allows the user to manipulate the camera
    scnView.allowsCameraControl = true

    // show statistics such as fps and timing information
    scnView.showsStatistics = true

    // configure the view
    scnView.backgroundColor = UIColor.black

    // add a tap gesture recognizer
    let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
    scnView.addGestureRecognizer(tapGesture)

    // play the scene
    scnView.isPlaying = true
    //scnView.loops = true
}

任何帮助表示赞赏! :)

参考文献:

sceneTime:

https://developer.apple.com/documentation/scenekit/scnscenerenderer/1522680-scenetime

正在玩:

https://developer.apple.com/documentation/scenekit/scnscenerenderer/1523401-isplaying

相关问题:

SceneKit SCNSceneRendererDelegate-未调用渲染器功能

2 回答

  • 0

    我无法使其以一种优雅的方式工作,但我通过添加以下 Timer 调用对其进行了修复:

    Timer.scheduledTimer(timeInterval: timeIncrement, target: self,   selector: (#selector(updateTimer)), userInfo: nil, repeats: true)
    

    timeIncrement 是设置为 0.01 的 Double,而 updateTimer 是以下函数:

    // helper function updateTimer
    @objc func updateTimer() {
        let scnView = self.view.subviews[0] as! SCNView
        scnView.sceneTime += Double(timeIncrement)
    }
    

    我敢肯定有更好的解决方案,但是可以。

  • 0

    在每个帧上运行动作和动画后,sceneTime 将自动设置为 0.0.

    在 SceneKit 运行动作和动画之前,可以使用 renderer(_:updateAtTime:)委托方法将 sceneTime 设置为所需的值。

    使 GameViewController 符合 SCNSceneRendererDelegate:

    class GameViewController: UIViewController, SCNSceneRendererDelegate {
        // ...
    }
    

    确保将scnView.delegate = self保留在 viewDidLoad()中。

    现在在您的 GameViewController 类中实现 renderer(_:updateAtTime:):

    // need to remember scene start time in order to calculate current scene time
    var sceneStartTime: TimeInterval? = nil
    
    func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
        // if startTime is nil assign time to it
        sceneStartTime = sceneStartTime ?? time
    
        // make scene time equal to the current time
        let scnView = self.view as! SCNView
        scnView.sceneTime = time - sceneStartTime!
    }
    

相关问题