我最近问了question,答案非常明显。我还在研究同一个项目并遇到另一个问题。我需要实现每帧逻辑,并且SCNSceneRendererDelegate
协议在iOS上运行得非常好,但在OSX上,renderer
函数没有触发。我创建了一个示例项目来说明我的问题。它由故事板中的Scene Kit视图和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修复了描述的问题
答案 0 :(得分:9)
我不明白是什么导致了这个问题,但我能够复制它。不过,我通过添加无意义的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
(正如您已经在做的那样)足以触发渲染回调。
我上面的代码不是解决方案。解决你的问题是一个讨厌的黑客,让你继续生活。
答案 1 :(得分:8)
对于仍有问题的人,设置委托和播放变量都可以。
sceneView.delegate = self
sceneView.isPlaying = true
答案 2 :(得分:5)
我怀疑答案取决于Querent所指的每一帧"每一帧"。 Querent应该澄清这一点,但无论如何我都会尝试回答,因为我就是这样。
最简单的解释可能是"无论如何都会呈现的每一帧#34;但是这似乎不太可能是所期望的,除非该多维数据集旨在作为渲染器的一种活动监视器,而不是#39看起来很可能;有更好的方法。
Querent可能想要的是在视图的playing属性为YES时重复渲染。如果是这种情况,那么答案就像将视图的loops属性设置为YES一样简单。这最近为我解决了一个问题,我希望在键盘按键被按下时进行渲染。 (我注意到将播放设置为YES会导致对我的代表进行一次调用。)
答案 3 :(得分:1)
除了这个链中的几个有用的提示之外,最后一个让我调用委托的方法如下:如果你对SCNSceneRendererDelegate
类使用pre Swift 4方法,它编译得很好,没有错误或者警告,但从不调用委托。
因此过时的Swift 4前定义:
func renderer(aRenderer:SCNSceneRenderer, updateAtTime time:TimeInterval) {...}
(我从网上的一个片段中获得)编译得很好并且从未被调用过,而正确的定义
func renderer(_ renderer:SCNSceneRenderer, updateAtTimet time:TimeInterval) {...}
编译并被调用!
由于SCNSceneRendererDelegate
是一个协议,因此覆盖提供的正常Swift保护是不合适的。由于SCNSceneRendererDelegate
将其方法定义为可选(我喜欢),因此它也不会被捕获。
答案 4 :(得分:0)
我一直在努力解决使用 SCNRenderer
而不是 SCNView
的一些类似错误,这是 Google 上的第一次成功,所以我只想在我的解决方案中添加一个注释,以防万一任何人。
我正在使用
.render(withViewport:commandBuffer:passDescriptor:)
但这不会调用委托渲染方法。相反,使用
.render(atTime:viewport:commandBuffer:passDescriptor:)
即使您没有使用时间间隔参数。然后委托方法 renderer(_:updateAtTime:)
将被正确调用。
答案 5 :(得分:-2)
试试这个......把代码放在一个场景类中 - 保持视图控制器清洁。
final class MySCNScene:SCNScene, SCNSceneRendererDelegate
{
@objc func renderer(aRenderer:SCNSceneRenderer, updateAtTime time:NSTimeInterval)
{
}
}
同时将视图的委托设置为场景:
mySCNView!.delegate = mySCNScene