As a new Xcode Project, let's select Game, name, next and create!
When beginning, you will find a GameViewController.swift
file. Starting from scratch, remove everything such that it looks like the following:
import UIKit import SpriteKit import GameplayKit class GameViewController: UIViewController { }
Then, we begin by creating the scene:
import UIKit import QuartzCore import SceneKit class GameViewController: UIViewController { let scene = SCNScene() // where the camera is kept essentially let cameraNode = SCNNode() let firstBox = SCNNode() override func viewDidLoad() { self.createScene() } func createScene() { // adding objects onto this view that's on the storyboard let sceneView = self.view as! SCNView sceneView.scene = scene // Create Camera cameraNode.camera = SCNCamera() cameraNode.camera?.usesOrthographicProjection = true cameraNode.camera?.orthographicScale = 3 cameraNode.position = SCNVector3Make(20, 20, 20) cameraNode.eulerAngles = SCNVector3Make(-45, 45, 0) let constraint = SCNLookAtConstraint(target: firstBox) constraint.isGimbalLockEnabled = true self.cameraNode.constraints = [constraint] scene.rootNode.addChildNode(cameraNode) // Create Box // This will be the first box that is created // and every box create later will be due to this box // chamferRadius is for the edge pointiness let firstBoxGeo = SCNBox(width: 1, height: 1.5, length: 1, chamferRadius: 0) firstBox.geometry = firstBoxGeo firstBox.position = SCNVector3Make(0, 0, 0) scene.rootNode.addChildNode(firstBox) // createLight // this will be used so that we can see our box let light = SCNNode() light.light = SCNLight() light.light?.type = SCNLight.LightType.directional light.eulerAngles = SCNVector3Make(-45, 45, 0) scene.rootNode.addChildNode(light) } }
To explore how the camera works, feel free to head to art.scnassets > ship.scn
and throw in a camera to see how it works.
From this, you can head to position after adding a camera and chang the Position
and Euler
to see the changes that this makes. Euler
essentially rotates it clockwise around the axis.
After changing this, you can select camera
from the bottom just to see how it looks.
Create a global node: var person = SCNNode()
Then, in createScene()
we can add
// Create Person let personGeo = SCNSphere(radius: 0.2) person = SCNNode(geometry: personGeo) let personMat = SCNMaterial() personMat.diffuse.contents = UIColor.red personGeo.materials = [personMat] person.position = SCNVector3Make(0, 1.1, 0) scene.rootNode.addChildNode(person)
For the actions, we can override the touchesBegan()
function and apply some logic. Ensure that you create the appropriate global Booleans.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { if goingLeft == false { person.removeAllActions() person.runAction(SCNAction.repeatForever(SCNAction.move(by: SCNVector3Make(-100, 0, 0), duration: 20))) goingLeft = true } else { person.removeAllActions() person.runAction(SCNAction.repeatForever(SCNAction.move(by: SCNVector3Make(0, 0, -100), duration: 20))) goingLeft = false } }
After adjusting the constaint of what we want the camera to look at etc. we can now start using the camera to look our "person". The code up to the end of this section looks as follows:
import UIKit import QuartzCore import SceneKit class GameViewController: UIViewController { let scene = SCNScene() // where the camera is kept essentially let cameraNode = SCNNode() let firstBox = SCNNode() var person = SCNNode() var goingLeft = Bool() override func viewDidLoad() { self.createScene() } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { if goingLeft == false { person.removeAllActions() person.runAction(SCNAction.repeatForever(SCNAction.move(by: SCNVector3Make(-100, 0, 0), duration: 20))) goingLeft = true } else { person.removeAllActions() person.runAction(SCNAction.repeatForever(SCNAction.move(by: SCNVector3Make(0, 0, -100), duration: 20))) goingLeft = false } } func createScene() { self.view.backgroundColor = UIColor.white // adding objects onto this view that's on the storyboard let sceneView = self.view as! SCNView sceneView.scene = scene // Create Person let personGeo = SCNSphere(radius: 0.2) person = SCNNode(geometry: personGeo) let personMat = SCNMaterial() personMat.diffuse.contents = UIColor.red personGeo.materials = [personMat] person.position = SCNVector3Make(0, 1.1, 0) scene.rootNode.addChildNode(person) // Create Camera cameraNode.camera = SCNCamera() cameraNode.camera?.usesOrthographicProjection = true cameraNode.camera?.orthographicScale = 3 cameraNode.position = SCNVector3Make(20, 20, 20) cameraNode.eulerAngles = SCNVector3Make(-45, 45, 0) let constraint = SCNLookAtConstraint(target: person) constraint.isGimbalLockEnabled = true self.cameraNode.constraints = [constraint] scene.rootNode.addChildNode(cameraNode) person.addChildNode(cameraNode) // Create Box // This will be the first box that is created // and every box create later will be due to this box // chamferRadius is for the edge pointiness let firstBoxGeo = SCNBox(width: 1, height: 1.5, length: 1, chamferRadius: 0) firstBox.geometry = firstBoxGeo let boxMaterial = SCNMaterial() boxMaterial.diffuse.contents = UIColor(red: 0.2, green: 0.8, blue: 0.9, alpha: 1.0) firstBoxGeo.materials = [boxMaterial] firstBox.position = SCNVector3Make(0, 0, 0) scene.rootNode.addChildNode(firstBox) // Create Light // this will be used so that we can see our box let light = SCNNode() light.light = SCNLight() light.light?.type = SCNLight.LightType.directional light.eulerAngles = SCNVector3Make(-45, 45, 0) scene.rootNode.addChildNode(light) let light2 = SCNNode() light2.light = SCNLight() light2.light?.type = SCNLight.LightType.directional light2.eulerAngles = SCNVector3Make(45, 45, 0) scene.rootNode.addChildNode(light2) } }
Creating the function createBox(), we can use a new SCNNode dynamically generated along with a switch on arc4random
in order to create new boxes.
func createBox() { tempBox = SCNNode(geometry: firstBox.geometry) let prevBox = scene.rootNode.childNode(withName: "\(boxNumber)", recursively: true) boxNumber += 1 tempBox.name = "\(boxNumber)" let randomNumber = arc4random() % 2 switch randomNumber { case 0: tempBox.position = SCNVector3Make((prevBox?.position.x)! - firstBox.scale.x, (prevBox?.position.y)!, (prevBox?.position.z)!) break case 1: tempBox.position = SCNVector3Make((prevBox?.position.x)!, (prevBox?.position.y)!, (prevBox?.position.z)! - firstBox.scale.z) break default: break } self.scene.rootNode.addChildNode(tempBox) }
By the end of this stage, you will end up having a path to follow that has 6 boxes ahead for you to see, but it will not decide whether or not you are on the box.
import UIKit import QuartzCore import SceneKit class GameViewController: UIViewController, SCNSceneRendererDelegate { let scene = SCNScene() // where the camera is kept essentially let cameraNode = SCNNode() let firstBox = SCNNode() var person = SCNNode() var goingLeft = Bool() var tempBox = SCNNode() var prevBoxNumber = Int() var boxNumber = Int() override func viewDidLoad() { self.createScene() } // used to ensure ball is on the path func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) { let deleteBox = self.scene.rootNode.childNode(withName: "\(prevBoxNumber)", recursively: true) if (deleteBox?.position.x)! > person.position.x + 1 || (deleteBox?.position.z)! > person.position.z + 1 { prevBoxNumber+=1 deleteBox?.removeFromParentNode() createBox() } } func createBox() { tempBox = SCNNode(geometry: firstBox.geometry) let prevBox = scene.rootNode.childNode(withName: "\(boxNumber)", recursively: true) boxNumber += 1 tempBox.name = "\(boxNumber)" let randomNumber = arc4random() % 2 switch randomNumber { case 0: tempBox.position = SCNVector3Make((prevBox?.position.x)! - firstBox.scale.x, (prevBox?.position.y)!, (prevBox?.position.z)!) break case 1: tempBox.position = SCNVector3Make((prevBox?.position.x)!, (prevBox?.position.y)!, (prevBox?.position.z)! - firstBox.scale.z) break default: break } self.scene.rootNode.addChildNode(tempBox) } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { if goingLeft == false { person.removeAllActions() person.runAction(SCNAction.repeatForever(SCNAction.move(by: SCNVector3Make(-100, 0, 0), duration: 20))) goingLeft = true } else { person.removeAllActions() person.runAction(SCNAction.repeatForever(SCNAction.move(by: SCNVector3Make(0, 0, -100), duration: 20))) goingLeft = false } } func createScene() { prevBoxNumber = 0 boxNumber = 0 self.view.backgroundColor = UIColor.white // adding objects onto this view that's on the storyboard let sceneView = self.view as! SCNView sceneView.delegate = self sceneView.scene = scene // Create Person let personGeo = SCNSphere(radius: 0.2) person = SCNNode(geometry: personGeo) let personMat = SCNMaterial() personMat.diffuse.contents = UIColor.red personGeo.materials = [personMat] person.position = SCNVector3Make(0, 1.1, 0) scene.rootNode.addChildNode(person) // Create Camera cameraNode.camera = SCNCamera() cameraNode.camera?.usesOrthographicProjection = true cameraNode.camera?.orthographicScale = 3 cameraNode.position = SCNVector3Make(20, 20, 20) cameraNode.eulerAngles = SCNVector3Make(-45, 45, 0) let constraint = SCNLookAtConstraint(target: person) constraint.isGimbalLockEnabled = true self.cameraNode.constraints = [constraint] scene.rootNode.addChildNode(cameraNode) person.addChildNode(cameraNode) // Create Box // This will be the first box that is created // and every box create later will be due to this box // chamferRadius is for the edge pointiness let firstBoxGeo = SCNBox(width: 1, height: 1.5, length: 1, chamferRadius: 0) firstBox.geometry = firstBoxGeo let boxMaterial = SCNMaterial() boxMaterial.diffuse.contents = UIColor(red: 0.2, green: 0.8, blue: 0.9, alpha: 1.0) firstBoxGeo.materials = [boxMaterial] firstBox.position = SCNVector3Make(0, 0, 0) scene.rootNode.addChildNode(firstBox) firstBox.name = "\(boxNumber)" for i in 0...6 { createBox() } // Create Light // this will be used so that we can see our box let light = SCNNode() light.light = SCNLight() light.light?.type = SCNLight.LightType.directional light.eulerAngles = SCNVector3Make(-45, 45, 0) scene.rootNode.addChildNode(light) let light2 = SCNNode() light2.light = SCNLight() light2.light?.type = SCNLight.LightType.directional light2.eulerAngles = SCNVector3Make(45, 45, 0) scene.rootNode.addChildNode(light2) } }