Using Scenekit in Swift Playground

Using SceneKit in Swift Playground

Since Swift doesn't have source compatibility between versions, the code in this answer might not work in either future or previous versions of Swift. Currently is has been updated to work in Xcode 7.0 Playgrounds with Swift 2.0.


The XCPlayground framework is what you need, and it is documented here.

Here is a very simple scene to get you started with Scene Kit in Swift:

import SceneKit
import QuartzCore // for the basic animation
import XCPlayground // for the live preview
import PlaygroundSupport

// create a scene view with an empty scene
var sceneView = SCNView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
var scene = SCNScene()
sceneView.scene = scene

// start a live preview of that view
PlaygroundPage.current.liveView = sceneView

// default lighting
sceneView.autoenablesDefaultLighting = true

// a camera
var cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.position = SCNVector3(x: 0, y: 0, z: 3)
scene.rootNode.addChildNode(cameraNode)

// a geometry object
var torus = SCNTorus(ringRadius: 1, pipeRadius: 0.35)
var torusNode = SCNNode(geometry: torus)
scene.rootNode.addChildNode(torusNode)

// configure the geometry object
torus.firstMaterial?.diffuse.contents = NSColor.red // (or UIColor on iOS)
torus.firstMaterial?.specular.contents = NSColor.white // (or UIColor on iOS)

// set a rotation axis (no angle) to be able to
// use a nicer keypath below and avoid needing
// to wrap it in an NSValue
torusNode.rotation = SCNVector4(x: 1.0, y: 1.0, z: 0.0, w: 0.0)

// animate the rotation of the torus
var spin = CABasicAnimation(keyPath: "rotation.w") // only animate the angle
spin.toValue = 2.0*Double.pi
spin.duration = 3
spin.repeatCount = HUGE // for infinity
torusNode.addAnimation(spin, forKey: "spin around")

When I run it, it looks like this:

Sample Image


Note that to run Scene Kit in an iOS playground, you need to check the "Run in Full Simulator" checkbox.

Sample Image

You find the Playground Setting in the Utilities Pane (0 to hide or show)

SceneKit Particle Systems in a Swift Playground

I think you're doing a mistake in filename extension. It is .scnp and not .sncp.

Either try without any extension -

var stars =  SCNParticleSystem(named: "Stars", inDirectory: nil)

or try with correct extension -

var stars =  SCNParticleSystem(named: "Stars.scnp", inDirectory: nil)

How do I address the “There was a problem running this page” message in Swift Playgrounds?

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.

iPad Swift Playgrounds won’t run ARKit code/crashes

I’ve figured out a way to make this work. All I had to do was assign the view controller to a variable and then present the variable. I’m not exactly sure why this works, I just know it does.

import ARKit
import SceneKit
import UIKit
import PlaygroundSupport

public class LiveVC: UIViewController, ARSessionDelegate, ARSCNViewDelegate {

let scene = SCNScene()
public var arscn = ARSCNView(frame: CGRect(x: 0,y: 0,width: 640,height: 360))

override public func viewDidLoad() {
super.viewDidLoad()
arscn.delegate = self
arscn.session.delegate = self
arscn.scene = scene
let config = ARWorldTrackingConfiguration()
config.planeDetection = [.horizontal]
arscn.session.run(config)
view.addSubview(arscn)
}
public func session(_ session: ARSession, didFailWithError error: Error) {}
public func sessionWasInterrupted(_ session: ARSession) {}
public func sessionInterruptionEnded(_ session: ARSession) {}
}
var vc = LiveVC()
PlaygroundPage.current.liveView = vc
PlaygroundPage.current.needsIndefiniteExecution = true


Related Topics



Leave a reply



Submit