Проблема с подклассом SCNScene

Я пытался создать подкласс SCNScene, так как это кажется лучшим местом для хранения логики, связанной со сценой. Теперь я не уверен, рекомендуется ли это, поэтому мой первый вопрос: должен ли я создавать подклассы SCNScene, а если нет, то почему? Кажется, в документации предполагается, что это распространено, но я читал комментарии в Интернете, в которых предлагалось не создавать подклассы. Забудьте об этом, я просматривал документацию для SKScene. Ссылка на класс SCNScene не упоминает о подклассах.

Предполагая, что можно структурировать мою игру таким образом, вот мой прогресс

//  GameScene.swift

import Foundation
import SceneKit


class GameScene: SCNScene {

    lazy var enityManager: BREntityManager = {
        return BREntityManager(scene: self)
    }()

    override init() {
        print("GameScene init")
        super.init()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }


    func someMethod() {
        // Here's where I plan to setup the GameplayKit stuff
        // for the scene
        print("someMethod called")
    }

}

Примечание. Я использую lazy var в соответствии с ответом на этот вопрос

В моем контроллере представления я пытаюсь использовать GameScene следующим образом.

class GameViewController: UIViewController {

    override func viewDidLoad() {

        super.viewDidLoad()

        // create a new scene
        let scene = GameScene(named: "art.scnassets/game.scn")!

        // retrieve the SCNView
        let scnView = self.view as! SCNView

        // set the scene to the view
        scnView.scene = scene

        // Should print "someMethod called"
        scene.someMethod()
    }
}

Однако вызов GameScene.someMethod() вызывает ошибку EXEC_BAD_ACCESS.

Кроме того, если я опускаю вызов GameScene.someMethod, сцена загружается правильно, но переопределенный инициализатор в GameScene не вызывается.

Я не уверен, что здесь происходит. Понятно, что в подклассах в Swift есть что-то, чего я не понял. Или, возможно, есть какой-то аспект порядка, в котором вещи должны работать, который я упустил.


person gargantuan    schedule 30.12.2015    source источник


Ответы (2)


Должен ли я создавать подклассы SCNScene, а если нет, то почему?

Нет, вам не нужно создавать подклассы, и вам, вероятно, лучше не создавать подклассы. SCNScene больше похож на базовые классы данных/модели (NSString, UIImage и т. д.), чем на классы поведения/контроллера/представления (UIViewController, UIControl и т. д.). То есть это общее описание или контейнер для чего-то (в данном случае трехмерной сцены) и на самом деле не обеспечивает поведение. Так как в поведении не так много возможностей, не так много возможностей переопределить поведение с помощью подкласса. (Кроме того, класс, разработанный вокруг точек входа подкласса, не предназначен для переопределения, например viewDidLoad и еще много чего.)

Хотя возможно создать подкласс SCNScene, вы не получите от этого многого, и некоторые API могут работать не так, как вы ожидаете...

Однако вызов GameScene.someMethod() вызывает ошибку EXEC_BAD_ACCESS.

Кроме того, если я опускаю вызов GameScene.someMethod, сцена загружается правильно, но переопределенный инициализатор в GameScene не вызывается.

Файл .scn на самом деле является архивом NSKeyedArchiver, что означает, что объекты внутри него имеют те же классы, которые использовались для его создания. когда вы вызываете init(named:) в своем подклассе SCNScene, реализация суперкласса в конечном итоге вызывает NSKeyedUnarchiver unarchiveObjectWithData: для загрузки архива. При отсутствии каких-либо искажений при разархивировании этот метод создаст экземпляр SCNScene, а не экземпляр вашего подкласса.

Поскольку вы не получаете экземпляр своего подкласса, ваши инициализаторы подкласса не вызываются, и попытка вызвать методы вашего подкласса приводит к ошибке времени выполнения.

Кроме того, чем здесь SpriteKit отличается от SceneKit? SKScene немного странная утка в том смысле, что это не чистый класс модели и не чистый класс контроллера. Вот почему вы видите множество проектов, включая шаблон Xcode, использующих подкласс SKScene. Однако у этого подхода есть недостатки: если вы не планируете тщательно, становится слишком легко тесно связать вашу игровую логику с вашими игровыми данными, что затрудняет расширение вашего игры (скажем, на несколько уровней) требуют утомительного кодирования вместо редактирования данных. На эту тему есть сессия WWDC 2014, которую стоит посмотреть. суп>

Так что делать?

У вас есть несколько вариантов здесь...

  1. Не делайте подклассы. Держите свою игровую логику в отдельном классе. Это не обязательно должен быть класс контроллера представления — у вас может быть один или несколько объектов Game, которые принадлежат вашему контроллеру представления или делегату приложения. (Это особенно важно, если ваша игровая логика перемещается между несколькими сценами или манипулирует ими.)

  2. Подкласс, но организуйте размещение материала из неархивированного SCNScene в экземпляр вашего подкласса — создайте экземпляр вашего подкласса, загрузите SCNScene, затем переместите все дочерние элементы его корневого узла в корневой узел вашего подкласса.

  3. Подкласс, но заставьте NSKeyedUnarchiver загружать ваш подкласс вместо SCNScene. (Вы можете сделать это с помощью сопоставления имен классов.)

Из них № 1, вероятно, лучший.

person rickster    schedule 30.12.2015
comment
Хорошие комментарии. Я бы сказал, используйте № 1 и № 2, где это необходимо. Но помните, что для выполнения № 1 вам нужна сцена, в которую можно поместить содержимое загруженной сцены — вот где № 2 пригодится. - person StackUnderflow; 30.12.2015
comment
Спасибо за этот подробный ответ. В сочетании с ответом Хэла он заполняет много пробелов. В то время как Apple Docs и онлайн-учебники великолепны, как новичок в SceneKit, кажется, что слишком мало времени уходит на то, чтобы решить, как все части должны сочетаться друг с другом. - person gargantuan; 31.12.2015

Вкратце: расширьте SCNNode, чтобы сгенерировать сцену программно.

Это началось как комментарий к отличному ответу Рикстера, но я видел, что он становится слишком длинным. В этом квартале я читал курс, в ходе которого значительное количество времени было посвящено SceneKit. Мы все время создавали подклассы SCNScene и не сталкивались ни с какими проблемами.

Однако, потратив только что час на просмотр кода, который я представил, кода, написанного студентами, и тонны другого примера кода (от Apple и других разработчиков), я не стал бы снова создавать подкласс SCNScene.

Ни один из примеров кода Apple не является подклассом SCNScene. Это должно быть очень сильной зацепкой. Обработка сериализации (initWithCoder и чтение из файла) — это повторяющийся вопрос в StackOverflow. Это вторая сильная подсказка, что вы не хотите идти туда.

Во всем коде, который я рассмотрел, который подклассировал SCNScene, причиной этого было программное создание сцены. Это выбор Рикстера №2. Но ни один из этих кодов не должен быть на SCNScene. Если вы строите кучу узлов в определенных конфигурациях, добавляете освещение и ограничения обзора, а также настраиваете материалы, создается впечатление, что вы строите SCNScene. Но на самом деле вы строите дерево из SCNNode экземпляров.

Так что, оглядываясь назад, я думаю, что вариант № 2 не дает достаточно веских причин для создания подкласса. Всю эту конструкцию можно выполнить, написав функцию-генератор в расширении SCNNode.

Заманчиво создать подкласс SCNNode для этого пользовательского дерева. Не делай этого. Вы сможете сохранить его и прочитать (при условии, что вы написали соответствующие функции NSSecureCoding). Но вы не сможете открыть свою сцену с помощью редактора сцен Xcode, потому что редактор сцен не знает, как разархивировать и создавать экземпляры ваших пользовательских подклассов SCNNode.

person Hal Mueller    schedule 30.12.2015
comment
Хорошо сказано. И если у вас есть коллекция SCNNode, то на самом деле у вас есть просто данные... нет причин встраивать их в код каждый раз, когда ваше приложение запускается. Создайте его один раз в коде и заархивируйте в файл .scn или создайте файл .scn с помощью редактора сцен в Xcode. - person rickster; 31.12.2015
comment
Разве SCNScene не является подклассом в проекте примера кода Apple, Bananas, в частности, в AAPLGameSimulation.h? - person Jim Hillhouse; 27.06.2016