首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何修复Swift中的“线程1:致命错误:在隐式展开可选值时意外发现nil”错误?

如何修复Swift中的“线程1:致命错误:在隐式展开可选值时意外发现nil”错误?
EN

Stack Overflow用户
提问于 2020-06-05 11:53:16
回答 2查看 179关注 0票数 0

我目前正在参加一个关于Make School的iOS游戏开发课程。本课程的第一步是。制作一个类似flappy bird的游戏,使用一些基本的东西,比如程序生成和无限滚动,等等。我不熟悉Swift,所以我还不能真正解释这个游戏的机制,但我会链接到下面的课程页面:

Make School - iOS Game Development - Obstacles

在本节中,本课程重点介绍如何从源障碍中程序化地生成障碍。代码如下:

代码语言:javascript
复制
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:添加了错误和层次结构的屏幕截图。

编辑2:添加了额外的资源。

这里有一个指向screen recording的链接

这是指向whole file的链接

EN

回答 2

Stack Overflow用户

发布于 2020-06-05 12:04:46

检查nil

让newObstacle = obstacleSource.copy()作为!SKNode

->

代码语言:javascript
复制
if let newObstacle = obstacleSource.copy() as? SKNode {
    //newObstacle is SKNode
}
票数 0
EN

Stack Overflow用户

发布于 2020-06-05 12:12:30

此错误(线程1:致命错误:隐式展开可选值时意外发现空值)当您试图访问/解包为nil的值时,可能会由于很多原因而发生。

在您的例子中,这是因为obstacleSource变量为空。

当你试图通过obstacleSource.copy() as? SKNode访问它时,这反过来会使你的应用程序崩溃。

由于您按如下方式初始化obstacleSource变量:

代码语言:javascript
复制
obstacleSource = self.childNode(withName: "obstacle")

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

它会像预期的那样工作。

现在,假设由于任何原因,您不确定节点obstacle是否实际存在,那么在这种情况下,不要将节点声明为implicitly unwrapped optional

代码语言:javascript
复制
var obstacleSource: SKNode!

您应该将其初始化为可选的,如下所示:

代码语言:javascript
复制
var obstacleSource: SKNode?

在这种情况下,因为它被声明为可选的,所以您的应用程序不会崩溃,并且您可以通过使用可选绑定来确保您的变量实际包含一个值

代码语言:javascript
复制
if let obstacleSource = self.obstacleSource {
   // Access your node here
} else {
   // The node 'obstacle' could not be found
}

编辑:感谢您在问题中添加屏幕截图!应用程序崩溃的原因是因为您的节点名称不匹配。您应该简单地将引用节点重命名为“障碍”,因为这是您在代码中访问它的方式,而且它应该工作得很好!

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62207850

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档