5

I am trying to make something with SceneKit for the WWDC Student Challenge, which requires my project be in a Swift playground. I keep getting the message that says “There was a problem running this page.” It offers no error messages. It just suggests that check my code or start over. I have tried removing pieces of code individually, but I cannot locate the source of this issue. I have also tried running it in man Xcode playground, which offered the warning of, and I quote, “The playground could not continue running because the playground source did”. I am stuck. What is wrong with my code.

import UIKit
import SceneKit
import QuartzCore 
import PlaygroundSupport

class GameScene: UIViewController, SCNSceneRendererDelegate {
    var primaryView: SCNView!
    var primaryScene: SCNScene!
    var cameraNode: SCNNode!
    
    override func viewDidLoad() {
        sceneAndViewInit()
        cameraInit()
        createGround()
        moonInit()
    }
    
    func createGround() {
        var ground = SCNBox(width: 200, height: 1, length: 200, chamferRadius: 0)
        var groundPhysicsShape = SCNPhysicsShape(geometry: ground, options: nil) 
        var groundNode = SCNNode(geometry: ground)
        ground.firstMaterial?.diffuse.contents = UIColor.orange
        ground.firstMaterial?.specular.contents = UIColor.white
        groundNode.position = SCNVector3(0, -6, 0)
        groundNode.physicsBody = SCNPhysicsBody(type: .static, shape: groundPhysicsShape)
        primaryScene.rootNode.addChildNode(groundNode)
    }
    
    func sceneAndViewInit() {
        primaryView = SCNView(frame: CGRect(x: 0, y: 0, width: 500, height: 300))
        primaryView.allowsCameraControl = true
        primaryView.autoenablesDefaultLighting = true
        
        primaryScene = SCNScene()
        
        primaryView.scene = primaryScene
        primaryView.isPlaying = true
    }
    
    func cameraInit() {
        var cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        cameraNode.position = SCNVector3(x: 0, y: 0, z: 2)
        cameraNode.camera?.fieldOfView = 65
        cameraNode.camera?.wantsHDR = false
        primaryScene.rootNode.addChildNode(cameraNode)
    }
    
    func moonInit() {
        let moonScene = SCNScene(named: "Moon.scn")
        var moonNode = moonScene?.rootNode.childNode(withName: "Sphere", recursively: true)
        var moonPhysicsShape = SCNPhysicsShape(node: moonNode!, options: nil)
        moonNode?.position = SCNVector3(0, 0, 0)
        moonNode?.scale = SCNVector3(1, 1, 1)
        moonNode?.name = "Moon"
        moonNode?.physicsBody = SCNPhysicsBody(type: .static, shape: moonPhysicsShape)
        primaryScene.rootNode.addChildNode(moonNode!)
    }
    
    func renderer(_ aRenderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
        print(time)
    }
}

let gameScene = GameScene()
PlaygroundPage.current.liveView = gameScene

(“Moon.scn” is a file that I have in the resources/files section along with its texture, and I still have the issue without any of the moon related code.)

aheze
  • 24,434
  • 8
  • 68
  • 125
42nd
  • 101
  • 8

2 Answers2

5

To view the full error message, go to Settings app -> Playgrounds -> turn on Authoring Debug Mode. This doesn't directly solve the issue but it's a lot more helpful for locating errors.

Settings app -> Playgrounds -> turn on Authoring Debug Mode

aheze
  • 24,434
  • 8
  • 68
  • 125
jp21
  • 296
  • 2
  • 8
  • 1
    Thank you so much! – aheze Apr 13 '21 at 14:41
  • 1
    Does Authoring Debug Mode also show (or is there a way to get it to show) full errors on code or just errors with loading Playgrounds? The only [resource](https://www.thecodehub.ie/news/2018/07/05/authoring-swift-playgrounds-subscription-feeds/) I could find talking about Authoring Debug Mode does so in the context of making a playground for distribution, but it is by no means comprehensive. It would amazing if Authoring Debug Mode also works on the code itself. – 42nd Apr 13 '21 at 16:42
  • 1
    I think authoring debug mode only shows compile errors, although I didn't try it myself. [link](https://stackoverflow.com/questions/51247539/how-do-you-debug-swift-playgroundbook) shows a detailed error message for error compiling source but not for the runtime errors, if that's what you were asking. – jp21 Apr 13 '21 at 18:02
  • 1
    Understood. Thank you for the clarification. This is very useful. – 42nd Apr 14 '21 at 00:23
1

I have found a way to address this issue. However, I can only offer you a round-ish idea of what the issue is and how to fix it.
The problem seems to be with the variable declarations at the top of the class.

var primaryView: SCNView!
var primaryScene: SCNScene!
var cameraNode: SCNNode!

The exclamation mark following the type indicates that the variable is an implicitly-unwrapped optional (I am quoting from this Stack Overflow response that explains what this is in more depth), but more-or-less the variable will automatically force unwrap whenever it is used. I have a more of a working knowledge of Swift than a technical one, so my guess is that when a GameScene object is created a synthetic initializer tries to set up these variables, it, for some reason, needs to read from the variables, gets nil, and cannot continue.

The solution appears to be putting the values of the variables in their initial declaration (There might be a better solution, but this at least works in my testing.), so that they are not nil for the initializer. Now the reason that I cannot fully explain the issue here is that this only seems to be necessary for the SCNScene variable. I don't really know why this is the case.

Code (*** mark important comments):

import UIKit
import SceneKit
import QuartzCore 
import PlaygroundSupport

class GameScene: UIViewController, SCNSceneRendererDelegate {
    var primaryView: SCNView!// = SCNView(frame: CGRect(x: 0, y: 0, width: 500, height: 300)) //***This seems to work for reasons beyond me with the implicitly-unwrapped optional***
    var primaryScene = SCNScene() //***The implicitly unwrapped optional is removed here***
    var cameraNode = SCNNode() //***The implicitly unwrapped optional is removed here (I am not sure if that is necessary but it probably good practice not to use it anywhere intros code.  I use it earlier to demonstrate its weird complexities.)***
    
    override func viewDidLoad() {
        sceneAndViewInit()
        cameraInit()
        createGround()
        //moonInit() ***I removed this for simplicity in the answer***
    }
    
    func createGround() {
        var ground = SCNBox(width: 200, height: 1, length: 200, chamferRadius: 0)
        var groundPhysicsShape = SCNPhysicsShape(geometry: ground, options: nil) 
        var groundNode = SCNNode(geometry: ground)
        ground.firstMaterial?.diffuse.contents = UIColor.orange
        ground.firstMaterial?.specular.contents = UIColor.white
        groundNode.position = SCNVector3(0, -6, 0)
        groundNode.physicsBody = SCNPhysicsBody(type: .static, shape: groundPhysicsShape)
        primaryScene.rootNode.addChildNode(groundNode)
    }
    
    func sceneAndViewInit() {
        primaryView = SCNView(frame: CGRect(x: 0, y: 0, width: 500, height: 300))
        primaryView.allowsCameraControl = true
        primaryView.autoenablesDefaultLighting = true
        
        //primaryScene = SCNScene() ***This is not necessary when it is declared above***
        
        self.primaryView.scene = primaryScene
        //primaryView.isPlaying = true
        
        self.view = primaryView //***I realized later you need this bit of code or the program will only display a blank screen***
    }
    
    func cameraInit() {
        //var cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        cameraNode.position = SCNVector3(x: 0, y: 0, z: 2)
        cameraNode.camera?.fieldOfView = 65
        cameraNode.camera?.wantsHDR = false
        primaryScene.rootNode.addChildNode(cameraNode)
    }
    
    func moonInit() {
        let moonScene = SCNScene(named: "Moon.scn")
        var moonNode = moonScene?.rootNode.childNode(withName: "Sphere", recursively: true)
        var moonPhysicsShape = SCNPhysicsShape(node: moonNode!, options: nil)
        moonNode?.position = SCNVector3(0, 0, 0)
        moonNode?.scale = SCNVector3(1, 1, 1)
        moonNode?.name = "Moon"
        moonNode?.physicsBody = SCNPhysicsBody(type: .static, shape: moonPhysicsShape)
        primaryScene.rootNode.addChildNode(moonNode!)
    }
    
    func renderer(_ aRenderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
        print(time)
    }
}

let gameScene = GameScene()
PlaygroundPage.current.liveView = gameScene

One final note: The self.view = primaryView code is really important. I make a note about in the full code, but I have found this way too hard to locate on the internet. I think this has to do with UIViewController (I don't know much about UIKit at all.), but either SceneKit or UIKit just displays a blank screen if you do now have that bit of code. If you don't figure out that these are separate issues they are really hard to decipher.

42nd
  • 101
  • 8