【问题标题】:SceneKit SCNSceneRendererDelegate - renderer function not calledSceneKit SCNSceneRendererDelegate - 未调用渲染器函数
【发布时间】:2016-05-25 06:38:19
【问题描述】:

我最近问了一个question,答案很明显。我仍在从事同一个项目并遇到另一个问题。我需要实现每帧逻辑,SCNSceneRendererDelegate 协议在 iOS 上运行良好,但在 OSX 上,renderer 函数没有触发。我创建了一个小示例项目来说明我的问题。它由情节提要中的场景工具包视图和ViewController 类中的以下代码组成:

import Cocoa
import SceneKit

class ViewController: NSViewController, SCNSceneRendererDelegate {

    @IBOutlet weak var sceneView: SCNView!

    let cubeNode = SCNNode()

    override func viewDidLoad() {

        super.viewDidLoad()

        let scene = SCNScene()

        let sphere = SCNSphere(radius: 0.1)
        sphere.firstMaterial!.diffuse.contents = NSColor.yellowColor()
        let sphereNode = SCNNode(geometry: sphere)
        scene.rootNode.addChildNode(sphereNode)

        let cube = SCNBox(width: 0.2, height: 0.2, length: 0.2, chamferRadius: 0)
        cube.firstMaterial!.diffuse.contents = NSColor.greenColor()
        cubeNode.geometry = cube
        cubeNode.position = SCNVector3(1,0,0)
        scene.rootNode.addChildNode(cubeNode)

        let cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        cameraNode.position = SCNVector3(2, 1, 2)
        let constraint = SCNLookAtConstraint(target: cubeNode)
        cameraNode.constraints = [constraint]
        scene.rootNode.addChildNode(cameraNode)

        sceneView.scene = scene
        sceneView.backgroundColor = NSColor(red: 0.5, green: 0, blue: 0.3, alpha: 1)
        sceneView.allowsCameraControl = true

        sceneView.delegate = self
        sceneView.playing = true

    }

    func renderer(renderer: SCNSceneRenderer, updateAtTime time: NSTimeInterval) {
        cubeNode.position.x += 0.1
    }
}

我想要的只是在每一帧中移动立方体。但什么也没有发生。奇怪的是,当我将sceneView.allowsCameraControl 设置为true 时,每当我在屏幕上单击或拖动时都会调用renderer 函数(这是有道理的,因为它需要根据摄像机角度更新视图)。但我希望它在每一帧都被调用。

是否有我看不到的错误,或者这是我的 Xcode 中的错误?

编辑: 我已尝试按照以下答案中的说明进行操作,现在 ViewController 的代码如下:

import Cocoa
import SceneKit

class ViewController: NSViewController {

    @IBOutlet weak var sceneView: SCNView!
    let scene = MyScene(create: true)

    override func viewDidLoad() {

        super.viewDidLoad()

        sceneView.scene = scene
        sceneView.backgroundColor = NSColor(red: 0.5, green: 0, blue: 0.3, alpha: 1)
        sceneView.allowsCameraControl = true

        sceneView.delegate = scene
        sceneView.playing = true

    }

}

还有一个 MyScene 类:

import Foundation
import SceneKit

final class MyScene: SCNScene, SCNSceneRendererDelegate {

    let cubeNode = SCNNode()

    convenience init(create: Bool) {
        self.init()

        let sphere = SCNSphere(radius: 0.1)
        sphere.firstMaterial!.diffuse.contents = NSColor.yellowColor()
        let sphereNode = SCNNode(geometry: sphere)
        rootNode.addChildNode(sphereNode)

        let cube = SCNBox(width: 0.2, height: 0.2, length: 0.2, chamferRadius: 0)
        cube.firstMaterial!.diffuse.contents = NSColor.greenColor()
        cubeNode.geometry = cube
        cubeNode.position = SCNVector3(1,0,0)
        rootNode.addChildNode(cubeNode)

        let cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        cameraNode.position = SCNVector3(2, 1, 2)
        let constraint = SCNLookAtConstraint(target: cubeNode)
        cameraNode.constraints = [constraint]

        rootNode.addChildNode(cameraNode)
    }

    @objc func renderer(aRenderer: SCNSceneRenderer, updateAtTime time: NSTimeInterval) {
        cubeNode.position.x += 0.01
    }
}

但是,它仍然无法正常工作。我做错了什么?

编辑:设置 sceneView.loops = true 修复了描述的问题

【问题讨论】:

    标签: swift macos scenekit


    【解决方案1】:

    我不明白是什么导致了问题,但我能够复制它。不过,我通过添加一个毫无意义的SCNAction

    let dummyAction = SCNAction.scaleBy(1.0, duration: 1.0)
    let repeatAction = SCNAction.repeatActionForever(dummyAction)
    cubeNode.runAction(repeatAction)
    

    仅当场景“正在播放”时才会触发渲染循环(请参阅SKScene becomes unresponsive while being idle)。我希望这样的设置

        sceneView.isPlaying = true
    

    (正如您已经在做的那样)足以触发渲染回调。

    我上面的代码不是解决方案。解决您的问题并让您继续生活是一种令人讨厌的技巧。

    【讨论】:

    • 谢谢,我会提交报告的!
    • 我不明白这怎么可能是通过添加一个毫无意义的 SCNAction 的“解决方案”……真的吗?人们在寻找答案时会感到困惑。
    • @Nico 如果您提交报告,请务必更新此线程。但我怀疑他们会接受你的报告作为错误。
    【解决方案2】:

    对于仍有问题的任何人,设置委托和播放变量都可以。

    sceneView.delegate = self
    sceneView.isPlaying = true
    

    【讨论】:

    • 我已经这样做了,但正如 Integer Poet 指出的那样,sceneView.loops = true 解决了这个问题。
    【解决方案3】:

    我怀疑答案取决于 Querent 所说的“每一帧”是什么意思。提问者可能应该澄清这一点,但无论如何我都会尝试回答,因为我就是这样。

    最简单的解释可能是“无论如何都会渲染的每一帧”,但这似乎不太可能是我们想要的,除非多维数据集旨在作为渲染器的一种活动监视器,这似乎也不太可能;有更好的方法。

    当视图的playing 属性为YES 时,Querent 可能想要重复渲染。如果是这种情况,那么答案可能就像将视图的loops 属性设置为YES 一样简单。这最近为我解决了一个问题,我希望在按住键盘键时进行渲染。 (我注意到将播放设置为 YES 会导致对我的委托进行一次调用。)

    【讨论】:

    • 没错!我也想通了,但忘了更新我的答案。
    • 在这种情况下,这似乎是应该接受的答案。
    【解决方案4】:

    除了这个链中的几个有用的提示之外,我要调用委托的最后一个提示如下:如果您对 SCNSceneRendererDelegate 类使用 Swift 4 之前的方法,它可以正常编译,没有错误或警告,但代表永远不会被调用。

    因此过时的 Swift 4 之前的定义:

    func renderer(aRenderer:SCNSceneRenderer, updateAtTime time:TimeInterval) {...}
    

    (我从网络上的 sn-p 获得)编译得很好,从未被调用,而正确的定义

    func renderer(_ renderer:SCNSceneRenderer, updateAtTimet time:TimeInterval) {...}
    

    编译并被调用!

    由于SCNSceneRendererDelegate 是一个协议,覆盖提供的正常 Swift 保护是不合适的。由于SCNSceneRendererDelegate 将其方法定义为可选(我喜欢),因此它也不会以这种方式被捕获。

    【讨论】:

      【解决方案5】:

      我一直在努力解决使用 SCNRenderer 而不是 SCNView 的一些类似错误,这是在 Google 上的第一次点击,所以我只想在我的解决方案中添加一个注释,以防它帮助任何人。

      我正在使用

      .render(withViewport:commandBuffer:passDescriptor:)
      

      但这不会调用委托渲染方法。相反,使用

      .render(atTime:viewport:commandBuffer:passDescriptor:)
      

      即使您没有使用时间间隔参数。那么委托方法renderer(_:updateAtTime:)就会被正确调用。

      【讨论】:

        【解决方案6】:

        试试这个……把你的代码放在场景类中——保持视图控制器干净。

        final class MySCNScene:SCNScene, SCNSceneRendererDelegate
        {
            @objc func renderer(aRenderer:SCNSceneRenderer, updateAtTime time:NSTimeInterval)
            {
            }
        }
        

        还将视图的委托设置为您的场景:

        mySCNView!.delegate = mySCNScene

        【讨论】:

        • 情节提要和视图控制器实际上与此无关,这完全是关于 SCNView 和您的 SCNScene。 SCNView 有一个场景属性,只需将您的 SCNScene 设置为它。
        • 子类化SCNScene 不是一个好主意。当您尝试使用NSKeyedArchiver 进行序列化和反序列化时,您正在为自己设置问题。 stackoverflow.com/questions/34534743/… 对此进行了深入讨论,并在答案中提供了几种替代方案。
        • OP 已经在原始代码示例中将场景的委托设置为 VC。那没有帮助。至于 SCNScene 子类化,请阅读@rickster 对我引用的帖子的回答。我曾经自己一直继承 SCNScene。但是反复出现的归档和初始化问题、Apple 的示例代码以及 Xcode 场景编辑器与 SCNScene 交互的方式都反对子类化。子类 SCNNode,或者有一个单独的 Game 类来保存你的游戏逻辑。
        • 查看这个带有工作渲染器和子类的示例项目。继承 SCNScene 是非常好的,只需加载并向其添加场景文件内容。简单干净! dl.dropboxusercontent.com/u/6979623/ExampleSceneKitTester.zip
        • 可以为 SceneKit 应用程序子类化 SCNScene,就像可以为文本编辑器子类化 NSString 一样。两者都不会立即引起问题。然而,Cocoa 及其相关框架鼓励组合而不是继承——在某种程度上,作为基本数据模型对象的子类类型可能会在以后给您带来其他问题。
        猜你喜欢
        • 2020-09-02
        • 2021-05-31
        • 2019-10-24
        • 2021-05-10
        • 1970-01-01
        • 2016-06-13
        • 2021-07-22
        • 2016-05-29
        • 2022-06-15
        相关资源
        最近更新 更多