SpriteKit: странный сбой при предварительной загрузке текстурных атласов

В настоящее время я работаю над игрой для iOS с использованием SpriteKit, и я столкнулся со странным сбоем при реализации предварительной загрузки атласов текстур. Вот краткий обзор того, что я получил до сих пор:

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 немного длинная, поскольку она инициализирует камеру, карту тайлов, элементы пользовательского интерфейса и некоторые ограничения. Тем не менее, это компилируется просто отлично. Но когда я запускаю игру, она сразу же вылетает со следующей фатальной ошибкой: "неожиданно найден ноль при развертывании необязательного значения". Как оказалось, этим необязательным значением является (случайным образом) либо self.tilemap, либо self.cam, и ошибка возникает в функции update() GameScene в одной из следующих строк (также случайным образом):

// here
var p = self.cam.position

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

Отладчик говорит мне, что либо self.tilemap, либо self.cam - ноль. Но я не могу понять, почему это так. Мне кажется, что функция didMoveToView выполняется при представлении сцены, а затем внезапно на полпути вызывается функция обновления, когда self.tilemap или self.cam еще не инициализированы.

Когда я изменяю функцию viewDidLoad GameViewController на это:

    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 ответ

прикрепить изображение к настройке SKView для UIView

Вы создаете приложение, используя шаблон, оно создаст вид как SCNView, это SCNView столкнуться с желанием иметь на доске объявлений SKView,

  1. удалять SCNView от вашей раскадровки
  2. SKView недоступно как опция просмотра
  3. Введите регулярный UIView к раскадровке
  4. По классу свойств введите SKView, он будет принимать, даже если его нет в списке
  5. Это исправит проблему при попытке создать SKView

Смотрите прикрепить изображение.

Другие вопросы по тегам