我目前正在参加一个关于Make School的iOS游戏开发课程。本课程的第一步是。制作一个类似flappy bird的游戏,使用一些基本的东西,比如程序生成和无限滚动,等等。我不熟悉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:添加了错误和层次结构的屏幕截图。

编辑2:添加了额外的资源。
这里有一个指向screen recording的链接
这是指向whole file的链接
发布于 2020-06-05 12:04:46
检查nil
让newObstacle = obstacleSource.copy()作为!SKNode
->
if let newObstacle = obstacleSource.copy() as? SKNode {
//newObstacle is SKNode
}发布于 2020-06-05 12:12:30
此错误(线程1:致命错误:隐式展开可选值时意外发现空值)当您试图访问/解包为nil的值时,可能会由于很多原因而发生。
在您的例子中,这是因为obstacleSource变量为空。
当你试图通过obstacleSource.copy() as? SKNode访问它时,这反过来会使你的应用程序崩溃。
由于您按如下方式初始化obstacleSource变量:
obstacleSource = self.childNode(withName: "obstacle")这意味着在场景中找不到名为“障碍”的子节点,因此只需检查GameScene.sks文件并确保节点的名称正确(“障碍”)即可。
它会像预期的那样工作。
现在,假设由于任何原因,您不确定节点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
}编辑:感谢您在问题中添加屏幕截图!应用程序崩溃的原因是因为您的节点名称不匹配。您应该简单地将引用节点重命名为“障碍”,因为这是您在代码中访问它的方式,而且它应该工作得很好!
https://stackoverflow.com/questions/62207850
复制相似问题