I’m creating a 3D ball that when the user clicks on it should move. However, at my line that says ballNode = scene.rootNode.childNode(withName: "Ball", recursively: true)!. I get an error saying Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value. The issue is when I create the node for the ball. I have never gotten this error before in swift.
Here is my code
import UIKit
import SceneKit
class GameViewController: UIViewController {
let CategoryLamp = 2
var sceneView:SCNView!
var scene:SCNScene!
var ballNode:SCNNode!
var selfieStickNode:SCNNode!
var motion = MotionHelper()
var motionForce = SCNVector3(0, 0, 0)
var sounds:[String:SCNAudioSource] = [:]
override func viewDidLoad() {
setupScene()
setupNodes()
}
func setupScene(){
sceneView = self.view as! SCNView
sceneView.delegate = self
//sceneView.allowsCameraControl = true
scene = SCNScene(named: "art.scnassets/MainScene.scn")
sceneView.scene = scene
scene.physicsWorld.contactDelegate = self
let tapRecognizer = UITapGestureRecognizer()
tapRecognizer.numberOfTapsRequired = 1
tapRecognizer.numberOfTouchesRequired = 1
tapRecognizer.addTarget(self, action: #selector(GameViewController.sceneViewTapped(recognizer:)))
sceneView.addGestureRecognizer(tapRecognizer)
}
func setupNodes() {
ballNode = scene.rootNode.childNode(withName: "Ball", recursively: true)! // Where the error is occurring
ballNode.physicsBody?.contactTestBitMask = CategoryLamp
selfieStickNode = scene.rootNode.childNode(withName: "selfieNode", recursively: true)!
}
@objc func sceneViewTapped (recognizer:UITapGestureRecognizer) {
let location = recognizer.location(in: sceneView)
let hitResults = sceneView.hitTest(location, options: nil)
if hitResults.count > 0 {
let result = hitResults.first
if let node = result?.node {
if node.name == "Ball" {
ballNode.physicsBody?.applyForce(SCNVector3(x: 0, y:4, z: -2), asImpulse: true)
}
}
}
}
override var shouldAutorotate: Bool {
return false
}
override var prefersStatusBarHidden: Bool {
return true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
extension GameViewController : SCNSceneRendererDelegate {
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
let ball = ballNode.presentation
let ballPosition = ball.position
let targetPosition = SCNVector3(x: ballPosition.x, y: ballPosition.y + 5, z:ballPosition.z + 5)
var cameraPosition = selfieStickNode.position
let camDamping:Float = 0.3
let xComponent = cameraPosition.x * (1 - camDamping) + targetPosition.x * camDamping
let yComponent = cameraPosition.y * (1 - camDamping) + targetPosition.y * camDamping
let zComponent = cameraPosition.z * (1 - camDamping) + targetPosition.z * camDamping
cameraPosition = SCNVector3(x: xComponent, y: yComponent, z: zComponent)
selfieStickNode.position = cameraPosition
motion.getAccelerometerData { (x, y, z) in
self.motionForce = SCNVector3(x: x * 0.05, y:0, z: (y + 0.8) * -0.05)
}
ballNode.physicsBody?.velocity += motionForce
}
}
extension GameViewController : SCNPhysicsContactDelegate {
func physicsWorld(_ world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact) {
var contactNode:SCNNode!
if contact.nodeA.name == "Ball" {
contactNode = contact.nodeB
}else{
contactNode = contact.nodeA
}
if contactNode.physicsBody?.categoryBitMask == CategoryLamp {
contactNode.isHidden = true
let waitAction = SCNAction.wait(duration: 15)
let unhideAction = SCNAction.run { (node) in
node.isHidden = false
}
let actionSequence = SCNAction.sequence([waitAction, unhideAction])
contactNode.runAction(actionSequence)
}
}
}