Thread 1: Swift runtime failure: Unexpectedly found nil while unwrapping an Optional value

In the fps im making, I am now working on mapping the levels. I have already made the walls and provided texture on them, and now I'm doing the same to the floor. I am able to code a red floor, however whenever I try to code texture on it I get the following error:

"Thread 1: Swift runtime failure: Unexpectedly found nil while unwrapping an Optional value"

The code appears at this line in ViewController.swift

return Textures(loader: { name in Bitmap(image: UIImage(named: name)!)!})

Here's the full code for ViewController.swift:

import Engine

private let joystickRadius: Double = 40
private let maximumTimeStep: Double = 1 / 20
private let worldTimeStep: Double = 1 / 120

private func loadTextures() -> Textures
{
  return Textures(loader: { name in Bitmap(image: UIImage(named: name)!)!})
}

private func loadMap() -> Tilemap {
  let jsonURL = Bundle.main.url(forResource: "Map", withExtension: "json")!
  let jsonData = try! Data(contentsOf: jsonURL)
  return try! JSONDecoder().decode(Tilemap.self, from: jsonData)
}

class ViewController: UIViewController {
  private let imageView = UIImageView()
  private let panGesture = UIPanGestureRecognizer()
  private var world = World(map: loadMap())
  private var lastFrameTime = CACurrentMediaTime()
  private let textures = loadTextures()

  override func viewDidLoad() {
    super.viewDidLoad()
    setUpImageView()

    let displayLink = CADisplayLink(target: self, selector: #selector(update))
    displayLink.add(to: .main, forMode: .common)

    view.addGestureRecognizer(panGesture)
  }

  private var inputVector: Vector {
    switch panGesture.state {
    case .began, .changed:
      let translation = panGesture.translation(in: view)
      var vector = Vector(x: Double(translation.x), y: Double(translation.y))
      vector /= max(joystickRadius, vector.length)
      panGesture.setTranslation(CGPoint(
        x: vector.x * joystickRadius,
        y: vector.y * joystickRadius
      ), in: view)
      return vector
    default:
      return Vector(x: 0, y: 0)
    }
  }

  @objc func update(_ displayLink: CADisplayLink) {
    let timeStep = min(maximumTimeStep, displayLink.timestamp - lastFrameTime)
    let inputVector = self.inputVector
    let rotation = inputVector.x * world.player.turningSpeed * worldTimeStep
    let input = Input(speed: -inputVector.y, rotation: Rotation(sine: sin(rotation), cosine: cos(rotation)))
    let worldSteps = (timeStep / worldTimeStep).rounded(.up)
    for _ in 0 ..< Int(worldSteps) {
      world.update(timeStep: timeStep / worldSteps, input: input)
    }
    lastFrameTime = displayLink.timestamp

    let width = Int(imageView.bounds.width), height = Int(imageView.bounds.height)
    var renderer = Renderer(width: width, height: height, textures: textures)
    renderer.draw(world)

    imageView.image = UIImage(bitmap: renderer.bitmap)
  }

  func setUpImageView() {
    view.addSubview(imageView)
    imageView.translatesAutoresizingMaskIntoConstraints = false
    imageView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
    imageView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
    imageView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
    imageView.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true
    imageView.contentMode = .scaleAspectFit
    imageView.backgroundColor = .black
    imageView.layer.magnificationFilter = .nearest
  }
}

Here's the code for other files that deal with the texturing of the floor and the level in general:

Textures.swift

{
  case wall, wall2
  case floor, ceiling
}

public struct Textures
{
  private let textures: [Texture: Bitmap]
}

public extension Textures
{
  init(loader: (String) -> Bitmap)
  {
    var textures = [Texture: Bitmap]()
    for texture in Texture.allCases
    {
      textures[texture] = loader(texture.rawValue)
    }
    self.init(textures: textures)
  }
  subscript(_ texture: Texture) -> Bitmap
  {
    return textures[texture]!
  }
}

Renderer.swift:

  public private(set) var bitmap: Bitmap
  private let textures: Textures

  public init(width: Int, height: Int, textures: Textures) {
    self.bitmap = Bitmap(width: width, height: height, color: .black)
    self.textures = textures
  }
}

public extension Renderer {
  mutating func draw(_ world: World) {
    let focalLength = 1.0
    let viewWidth = Double(bitmap.width) / Double(bitmap.height)
    let viewPlane = world.player.direction.orthogonal * viewWidth
    let viewCenter = world.player.position + world.player.direction * focalLength
    let viewStart = viewCenter - viewPlane / 2

    // Cast rays
    let columns = bitmap.width
    let step = viewPlane / Double(columns)
    var columnPosition = viewStart
    for x in 0 ..< columns {
      let rayDirection = columnPosition - world.player.position
      let viewPlaneDistance = rayDirection.length
      let ray = Ray(
        origin: world.player.position,
        direction: rayDirection / viewPlaneDistance
      )
      let end = world.map.hitTest(ray)
      let wallDistance = (end - ray.origin).length
       
      // Draw wall
      let wallHeight = 1.0
      let distanceRatio = viewPlaneDistance / focalLength
      let perpendicular = wallDistance / distanceRatio
      let height = wallHeight * focalLength / perpendicular * Double(bitmap.height)
      let wallTexture: Bitmap
      let wallX: Double
      if end.x.rounded(.down) == end.x
      {
        wallTexture = textures[.wall]
        wallX = end.y - end.y.rounded(.down)
      }
      else
      {
        wallTexture = textures[.wall2]
        wallX = end.x - end.x.rounded(.down)
      }
      let textureX = Int(wallX * Double(wallTexture.width))
      let wallStart = Vector(x: Double(x), y: (Double(bitmap.height) - height) / 2 - 0.001)
      bitmap.drawColumn(textureX, of: wallTexture, at: wallStart, height: height)
       
      // Draw floor
      let floorTexture = textures[.floor]
      let floorStart = Int(wallStart.y + height) + 1
      for y in min(floorStart, bitmap.height) ..< bitmap.height {
        let normalizedY = (Double(y) / Double(bitmap.height)) * 2 - 1
        let perpendicular = wallHeight * focalLength / normalizedY
        let distance = perpendicular * distanceRatio
        let mapPosition = ray.origin + ray.direction * distance
        let tileX = mapPosition.x.rounded(.down), tileY = mapPosition.y.rounded(.down)
        let textureX = Int((mapPosition.x - tileX) * Double(floorTexture.width))
        let textureY = Int((mapPosition.y - tileY) * Double(floorTexture.height))
        bitmap[x, y] = floorTexture[textureX, textureY]
      }
      columnPosition += step
    }
  }
}

The rest of the code can be found in the link below: https://github.com/KingDecorpel12/RampageFPS/tree/main/RetroRampage/Source

Any and all help will be greatly appreciated!

Answered by OOPer in 679667022

look at the Debug navigator

Debug Navigator is quite useless when finding which is being nil and you may need to modify your code:

private func loadTextures() -> Textures {
    return Textures(loader: { name in
        guard let image = UIImage(named: name) else {
            fatalError("UIImage for \(name) cannot be created")
        }
        guard let bitmap = Bitmap(image: image) else {
            fatalError("Bitmap for \(name) (image: \(image)) cannot be created")
        }
        return bitmap
    })
}

Generally, you use toooo.... many forced unwrappings (!) in your code. I recommend you to learn safe ways to work with Optionals.

If the line is causing Unexpectedly found nil, UIImage(named: name) is nil or Bitmap(image: UIImage(named: name)!) is nil. Have you checked which is returning nil?

I haven't been able to find out which is returning nil. Whenever I run the code and look at the Debug navigator it says the following for the line that the error appears in:

"closure #1 in loadTextures()" "loadTextures() [inlined]"

I haven't been able to find out which is returning nil. Whenever I run the code and look at the Debug navigator it says the following for the line that the error appears in:

"closure #1 in loadTextures()" "loadTextures() [inlined]"

Accepted Answer

look at the Debug navigator

Debug Navigator is quite useless when finding which is being nil and you may need to modify your code:

private func loadTextures() -> Textures {
    return Textures(loader: { name in
        guard let image = UIImage(named: name) else {
            fatalError("UIImage for \(name) cannot be created")
        }
        guard let bitmap = Bitmap(image: image) else {
            fatalError("Bitmap for \(name) (image: \(image)) cannot be created")
        }
        return bitmap
    })
}

Generally, you use toooo.... many forced unwrappings (!) in your code. I recommend you to learn safe ways to work with Optionals.

Thread 1: Swift runtime failure: Unexpectedly found nil while unwrapping an Optional value
 
 
Q