-1

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)
        }
        
    }
    
    
}
Lacy9265
  • 9
  • 1

1 Answers1

0

scene.rootNode.childNode(withName: "Ball", recursively: true) returning a nil value while running. You should debug and identify why you are getting this nil value.

You can provide a default value if it founds a nil value as-

func setupNodes() {
        ballNode = scene.rootNode.childNode(withName: "Ball", recursively: true) ?? "provide a default value"
        ballNode.physicsBody?.contactTestBitMask = CategoryLamp
        selfieStickNode = scene.rootNode.childNode(withName: "selfieNode", recursively: true)!
}

OR You can provide a guard statement as

func setupNodes() {
        guard let tmpballNode = scene.rootNode.childNode(withName: "Ball", recursively: true) else {return}
        ballNode = tmpballNode
        ballNode.physicsBody?.contactTestBitMask = CategoryLamp
        selfieStickNode = scene.rootNode.childNode(withName: "selfieNode", recursively: true)!
}
Imran0001
  • 580
  • 4
  • 11