2016-02-14 20 views
6

Ostatnio zapytałem o numer question, który miał dość oczywistą odpowiedź. Nadal pracuję nad tym samym projektem i napotykam inny problem. Muszę zaimplementować jedną logikę ramek, a protokół SCNSceneRendererDelegate działał idealnie dobrze na iOS, ale w systemie OSX funkcja renderer nie uruchamia się. Stworzyłem mały przykładowy projekt ilustrujący mój problem. Składa się on z zestawu sceny w widoku serii ujęć i następującego kodu w klasie ViewController:SceneKit SCNSceneRendererDelegate - funkcja renderer nie nazywa się

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

Wszystko czego chcę to po prostu przesunąć sześcian z każdej klatki. Ale nic się nie dzieje. Dziwne jest to, że po ustawieniu sceneView.allowsCameraControl na true, funkcja renderer jest wywoływana za każdym razem, gdy klikam lub przeciągam na ekranie (co ma sens, ponieważ wymaga aktualizacji widoku na podstawie kątów kamery). Ale chciałbym, żeby nazywał się każdy kadr.

Czy wystąpił błąd, którego nie widzę lub czy jest to błąd w moim Xcode?

Edit: Próbowałem postępując zgodnie z instrukcjami w odpowiedzi poniżej i teraz mają następujący kod dla 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 

    } 

} 

oraz klasę 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 
    } 
} 

Jednak jest wciąż nie działa. Co ja robię źle?

EDIT: ustawienie sceneView.loops = true poprawki opisanego problemu

Odpowiedz

3

Podejrzewam, że odpowiedź zależy od tego, co oznacza "każda klatka". Querent powinien to wyjaśnić, ale i tak postaram się odpowiedzieć, ponieważ jestem taki.

Najprostsza interpretacja to prawdopodobnie "każda klatka, która zresztą renderowałaby się", ale wydaje się mało prawdopodobne, aby była tym, co jest pożądane, chyba że kostka jest przeznaczona do monitorowania aktywności renderera, co również nie wydaje się prawdopodobne; istnieje o wiele lepsze podejście do tego.

To, co może chcieć, to renderować wielokrotnie, podczas gdy właściwość widoku tojest TAK. Jeśli tak jest, to być może odpowiedź jest tak prosta, jak ustawienie właściwości widoku dla TAK na loops. To ostatnio rozwiązało problem, w którym chciałem renderować się podczas przytrzymywania klawisza na klawiaturze. (Zauważyłem, że ustawienie opcji TAK spowoduje pojedyncze połączenie z moim uczestnikiem).

+0

Dokładnie! Też to wymyśliłem, ale zapomniałem zaktualizować moją odpowiedź. – Nico

+0

W takim przypadku wydaje się, że jest to odpowiedź, która powinna zostać przyjęta. –

-2

... Spróbuj umieścić swój kod w klasie zamiast sceny - zachować kontroler widoku czyste.

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

ustawić także delegata widoku do sceny:

mySCNView .delegate = mySCNScene

+0

Dzięki za sugestię, ale nie jestem pewien, jak połączyć to z ViewController i Storyboard ... Czy mógłbyś wyjaśnić trochę więcej? – Nico

+0

Scenariusz i kontroler widoku nie ma w tym żadnego udziału, chodzi tylko o SCNView i SCNScene. SCNView ma właściwość scene, wystarczy ustawić SCNScene na to. – StackUnderflow

+0

Zmieniłem moje pytanie z tym, jak myślę, że twoje rozwiązanie wygląda, ale nadal nie działa. Czy źle cię zrozumiałem? – Nico

7

ja nie rozumiem, co jest przyczyną problemu, ale udało mi się go powielać. Dostałem go do pracy, chociaż, dodając sensu SCNAction:

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

Masę tynkarską pożarów pętli tylko wtedy, gdy scena jest „gry” (patrz SKScene becomes unresponsive while being idle). Spodziewam się, że ustawienie (tak jak już to robisz) wystarczy, aby wyzwolić wywołania zwrotne renderowania.

Kod, który mam powyżej, nie jest rozwiązaniem. Jest to paskudny hack do obejścia twojego problemu i umożliwienia ci życia.

+1

Dzięki, zagram raport! – Nico

+1

Nie widzę, jak to może być "rozwiązanie", dodając bezsensowną SCNAction ... poważnie? Ludzie będą się mylić szukając odpowiedzi. – StackUnderflow

+1

@Nico Jeśli zgłaszasz raport, ważne jest, aby zaktualizować ten wątek. Ale wątpię, by zaakceptowali twój raport jako błąd. – StackUnderflow

3

Dla każdego, kto wciąż ma problemy, ustawienie delegata i odtwarzanie zmiennych będzie działało.

sceneView.delegate = self 
sceneView.isPlaying = true 
+0

Już to zrobiłem, ale jak wskazał Integer Poet, scenaView.loops = true rozwiązuje problem. – Nico

Powiązane problemy