SpriteKit:预加载纹理图集

时间:2016-05-06 11:07:50

标签: ios swift sprite-kit crash preloading

我目前正在使用SpriteKit开发iOS游戏,我在实现纹理地图集的预加载时遇到了一个奇怪的崩溃。以下是我到目前为止的简要概述:

GameViewController.swift

import UIKit
import SpriteKit

class GameViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Configure the view.
        let skView = self.view as! SKView
        skView.showsFPS = true
        skView.showsNodeCount = true
        skView.showsDrawCount = true
        skView.showsPhysics = false
        skView.ignoresSiblingOrder = true
        skView.multipleTouchEnabled = true

        // Setup scene
        let scene = GameScene(size: skView.bounds.size)
        scene.scaleMode = .ResizeFill

        let MyAtlas1 = SKTextureAtlas(named: "MyAtlas1")
        let MyAtlas2 = SKTextureAtlas(named: "MyAtlas2")
        let MyAtlas3 = SKTextureAtlas(named: "MyAtlas3")

        SKTextureAtlas.preloadTextureAtlases([MyAtlas1, MyAtlas2, MyAtlas3], withCompletionHandler: 
        {
            print("Everything loaded!")

            // Present scene
            skView.presentScene(scene)
        })
    }
    [...]

GameScene.swift

import SpriteKit

class GameScene: SKScene {

    var cam: SKCameraNode!
    var tilemap: Tilemap!
    var previousTime: CFTimeInterval = 0
    [...]

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

        [...]

        // Setup camera
        self.cam = SKCameraNode()
        self.cam.setScale(self.camScaleFactor)
        self.camera = self.cam
        self.addChild(self.cam)

        [...]

        // Load Tilemap
        self.tilemap = Tilemap(mapName: "testlevel2")
        self.addChild(self.tilemap)

        // Setup UI
        [...]
    }

    [...]

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

        if self.previousTime == 0 {
            self.previousTime = currentTime
        }

        let delta = currentTime - self.previousTime

        var p = self.cam.position
        p.x += 2.0 * CGFloat(delta)
        p.y += 1.5 * CGFloat(delta)
        self.cam.position = p

        // Update tilemap
        self.tilemap.update(delta)

        self.previousTime = currentTime
    }
}

didMoveToView函数有点冗长,因为它初始化了一个摄像头,一个tilemap,UI的东西和一些约束。但是,编译得很好。但是当我运行游戏时,它会立即崩溃并出现以下致命错误:"在解开一个Optional值时意外发现nil"。事实证明,可选值是(随机)self.tilemap或self.cam,并且错误发生在GameScene的update()函数中的以下行之一(也是随机的):

// here
var p = self.cam.position

// or here
self.tilemap.update(delta)

调试器告诉我self.tilemap或self.cam是nil。但我无法弄清楚为什么会这样。在我看来,函数didMoveToView在场景出现时执行,然后突然中途执行,调用更新函数,其中self.tilemap或self.cam仍然未初始化。

当我将GameViewController的viewDidLoad函数更改为:

    override func viewDidLoad() {
        super.viewDidLoad()

        // Configure the view.
        let skView = self.view as! SKView
        skView.showsFPS = true
        skView.showsNodeCount = true
        skView.showsDrawCount = true
        skView.showsPhysics = false
        skView.ignoresSiblingOrder = true
        skView.multipleTouchEnabled = true

        // Setup scene
        let scene = GameScene(size: skView.bounds.size)
        scene.scaleMode = .ResizeFill

        let MyAtlas1 = SKTextureAtlas(named: "MyAtlas1")
        let MyAtlas2 = SKTextureAtlas(named: "MyAtlas2")
        let MyAtlas3 = SKTextureAtlas(named: "MyAtlas3")

        SKTextureAtlas.preloadTextureAtlases([MyAtlas1, MyAtlas2, MyAtlas3], withCompletionHandler: 
        {
            print("Everything loaded!")                
        })

        // Present scene
        skView.presentScene(scene)
    }
    [...]

一切都很完美。但这显然不是解决方案,因为我想在启动场景之前预先加载所有内容。还要注意:我知道预加载的地图集会立即从内存中删除,因为我没有对它们进行强有力的引用。无论如何,该代码实际上仅用于演示和测试。

所以我的问题是:有没有人遇到类似的问题或知道该错误发生了什么?我无法弄清楚我做错了什么。非常感谢任何帮助。

1 个答案:

答案 0 :(得分:0)

attach image ho setup SKView for UIView

您使用模板创建应用,它会将视图创建为SCNView。 这个SCNView与您希望在故事板SKView上发生碰撞。

  1. 从故事板中删除SCNView
  2. SKView无法用作查看选项
  3. 将常规UIView输入故事板
  4. 在属性类上,键入SKView,即使它不在列表中也会接受
  5. 当您尝试创建SKView
  6. 时,这将解决问题

    请参阅附加图片。