如何解决“线程1:致命错误:在Swift中意外发现nil,同时隐式展开一个可选值”错误?

问题描述 投票:0回答:2

我目前正在Make School上iOS游戏开发课程。本课程的第一步是。制作一个类似于飘扬的小鸟的游戏,并具有一些基本知识,例如程序生成和无限滚动等等。我不熟悉Swift,因此我还无法真正解释游戏的机制,但是我将链接下面的课程页面:

Make School - iOS Game Development - Obstacles

在本节中,本课程着重于从源障碍物以程序方式产生障碍物的过程。这是代码:

import SpriteKit
import GameplayKit

class GameScene: SKScene {

    var hero: SKSpriteNode!
    var scrollLayer: SKNode!
    var sinceTouch : CFTimeInterval = 0
    var spawnTimer: CFTimeInterval = 0
    let fixedDelta: CFTimeInterval = 1.0 / 60.0 /* 60 FPS */
    let scrollSpeed: CGFloat = 100
    var obstacleSource: SKNode!
    var obstacleLayer: SKNode!

    override func didMove(to view: SKView) {
        /* Setup your scene here */

        /* Recursive node search for 'hero' (child of referenced node) */
        hero = (self.childNode(withName: "//hero") as! SKSpriteNode)

        /* Set reference to scroll layer node */
        scrollLayer = self.childNode(withName: "scrollLayer")

        /* Set reference to obstacle layer node */
        obstacleLayer = self.childNode(withName: "obstacleLayer")

        /* allows the hero to animate when it's in the GameScene */
        hero.isPaused = false

        /* Set reference to obstacle Source node */
        obstacleSource = self.childNode(withName: "obstacle")
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        /* Called when a touch begins */

        /* Apply vertical impulse */
        hero.physicsBody?.applyImpulse(CGVector(dx: 0, dy: 300))

         /* Apply subtle rotation */
         hero.physicsBody?.applyAngularImpulse(1)

         /* Reset touch timer */
         sinceTouch = 0
    }

    override func update(_ currentTime: TimeInterval) {
        /* Called before each frame is rendered */

        /* Grab current velocity */
        let velocityY = hero.physicsBody?.velocity.dy ?? 0

        /* Check and cap vertical velocity */
        if velocityY > 400 {
          hero.physicsBody?.velocity.dy = 400
        }

        /* Apply falling rotation */
        if sinceTouch > 0.2 {
            let impulse = -20000 * fixedDelta
            hero.physicsBody?.applyAngularImpulse(CGFloat(impulse))
        }

        /* Clamp rotation */
        hero.zRotation.clamp(v1: CGFloat(-90).degreesToRadians(), CGFloat(30).degreesToRadians())
        hero.physicsBody?.angularVelocity.clamp(v1: -1, 3)

        /* Update last touch timer */
        sinceTouch += fixedDelta

        /* Process world scrolling */
        scrollWorld()

        /* Process obstacle scrolling*/
        updateObstacles()

        spawnTimer+=fixedDelta
    }

    func scrollWorld() {
        /* Scroll World */
        scrollLayer.position.x -= scrollSpeed * CGFloat(fixedDelta)

        /* Loop through scroll layer nodes */
        for ground in scrollLayer.children as! [SKSpriteNode] {

            /* Get ground node position, convert node position to scene space */
            let groundPosition = scrollLayer.convert(ground.position, to: self)

            /* Check if ground sprite has left the scene */
            if groundPosition.x <= -ground.size.width / 2 {

                    /* Reposition ground sprite to the second starting position */
                    let newPosition = CGPoint(x: (self.size.width / 2) + ground.size.width, y: groundPosition.y)

                    /* Convert new node position back to scroll layer space */
                    ground.position = self.convert(newPosition, to: scrollLayer)
            }
        }
    }

    func updateObstacles() {
      /* Update Obstacles */

      obstacleLayer.position.x -= scrollSpeed * CGFloat(fixedDelta)

      /* Loop through obstacle layer nodes */
      for obstacle in obstacleLayer.children as! [SKReferenceNode] {

          /* Get obstacle node position, convert node position to scene space */
          let obstaclePosition = obstacleLayer.convert(obstacle.position, to: self)

          /* Check if obstacle has left the scene */
          if obstaclePosition.x <= -26 {
          // 26 is one half the width of an obstacle

              /* Remove obstacle node from obstacle layer */
              obstacle.removeFromParent()
          }

      }

        /* Time to add a new obstacle? */
        if spawnTimer >= 1.5 {

            /* Create a new obstacle by copying the source obstacle */
            let newObstacle = obstacleSource.copy() as! SKNode
            obstacleLayer.addChild(newObstacle)

            /* Generate new obstacle position, start just outside screen and with a random y value */
            let randomPosition =  CGPoint(x: 347, y: CGFloat.random(in: 234...382))

            /* Convert new node position back to obstacle layer space */
            newObstacle.position = self.convert(randomPosition, to: obstacleLayer)

            // Reset spawn timer
            spawnTimer = 0
        }
    }
}

当我在Xcode中运行此代码时,该问题显示在该行中let newObstacle = obstacleSource.copy() as! SKNode

我检查了几次代码,甚至从课程页面复制粘贴了代码,但仍然无法解决问题

编辑1:添加了错误和层次结构的屏幕截图。enter image description here

ios swift infinite-scroll procedural-generation flappy-bird-clone
2个回答
0
投票

检查零头

让newObstacle = barrierSource.copy()为! SKNode

->

if let newObstacle = obstacleSource.copy() as? SKNode {
    //newObstacle is SKNode
}

0
投票

此错误((线程1:致命错误:意外发现nil,同时隐式展开一个可选值)出于多种原因,当您尝试访问/解开一个nil值时,可能会发生。 >

在您的情况下,这是因为obstacleSource变量为nil。

当您尝试通过obstacleSource.copy() as? SKNode访问它时,它会使您的应用程序崩溃。

因为您像这样初始化了obstacleSource变量:

obstacleSource = self.childNode(withName: "obstacle")

这意味着在您的场景中找不到名称为“ obstacle”的子节点,因此您只需检查GameScene.sks文件并确保正确命名您的节点(“ obstacle”)。

它将按预期工作。

现在让我们说,由于某种原因,您不确定节点obstacle是否确实存在,那么在这种情况下,不必将节点声明为implicitly unwrapped optional

var obstacleSource: SKNode!

您应将其初始化为可选内容,例如:

var obstacleSource: SKNode?

在这种情况下,因为它被声明为可选,所以您的应用不会崩溃,并且您可以通过使用可选绑定

确保您的变量实际上持有一个值:
if let obstacleSource = self.obstacleSource {
   // Access your node here
} else {
   // The node 'obstacle' could not be found
}
© www.soinside.com 2019 - 2024. All rights reserved.