Создание подклассов SKNodes, созданных с помощью файла сцены SpriteKit .sks

(это для XCode 6 и iOS 8 beta 4)

Нравится новый редактор SceneKit. Я успешно загружаю сцену из файла .sks в собственный класс SKScene. Однако объекты внутри него создаются как классы по умолчанию (SKNode, SKSpriteNode и т. д.), и я не уверен, как связать их для создания экземпляров в качестве пользовательских подклассов.

В настоящее время я обхожу это, создавая пользовательские классы и связывая узлы спрайтов в качестве свойства, и это работает нормально.


person Rudi    schedule 04.08.2014    source источник
comment
Я надеюсь, что Apple сделает это доступным в следующих обновлениях Xcode.   -  person Dima Deplov    schedule 29.08.2014
comment
Добавление награды, потому что я хотел бы знать, есть ли предпочтительный способ выполнить это (Xcode 6.1, декабрь 2014 г.).   -  person Ephemera    schedule 05.12.2014
comment
Я думаю, что в конечном итоге они сделают что-то в редакторе сцен, как они делают для IBDesignable сейчас в IB для AppKit и UIKit. Но если вы этого хотите, все сообщают об ошибках, запрашивая это! Это одна из ключевых вещей, которая движет их процессом расстановки приоритетов.   -  person uchuugaka    schedule 05.12.2014
comment
Мы сможем сделать это в Xcode 7!   -  person NobodyNada    schedule 16.06.2015
comment
@NobodyNada: как вы это делаете в Xcode 7?   -  person cocoseis    schedule 26.09.2015
comment
@cocoseis Выберите узел, затем измените его в инспекторе пользовательских классов. (Лично у меня это не заработало; я думаю, что устройство должно работать под управлением iOS 9, и я не тестировал его с тех пор, как обновил.)   -  person NobodyNada    schedule 26.09.2015


Ответы (4)


Я написал небольшой вспомогательный делегат, чтобы справиться с этим: https://github.com/ice3-software/node-archive-delegate. Он использует NSKeyedUnarchiverDelegate для подкласса ваших спрайтов по имени.

person sf13579    schedule 23.03.2015

В настоящее время вы должны сделать это по имени в методе, добавленном в игровые шаблоны SpriteKit. Они освещают эту тему в видео SpriteKit Best Practices на WWDC 2014. Его легко пропустить, потому что в видео много всего.

Вот метод.

+ (instancetype)unarchiveFromFile:(NSString *)file {
    /* Retrieve scene file path from the application bundle */
    NSString *nodePath = [[NSBundle mainBundle] pathForResource:file ofType:@"sks"];
    /* Unarchive the file to an SKScene object */
    NSData *data = [NSData dataWithContentsOfFile:nodePath
                                          options:NSDataReadingMappedIfSafe
                                            error:nil];
    NSKeyedUnarchiver *arch = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
    [arch setClass:self forClassName:@"SKScene"];
    SKScene *scene = [arch decodeObjectForKey:NSKeyedArchiveRootObjectKey];
    [arch finishDecoding];

    return scene;
}

Это категория на SKScene, которая включает метод использования NSKeyedUnarchiver для замены класса при загрузке из файла .sks в mainBundle. В iOS они добавляют его в файл GameViewController.m, а в OS X — в файл AppDelegate.m.

Здесь или в ваших отдельных подклассах SKNode вы можете реализовать это. Это то, что он сделал ниже в своем посте. Это было предоставлено Apple и освещено в видео WWDC. Я думаю, в следующем году это будет обработано лучше. Тем временем зарегистрируйте ошибку, требующую, чтобы SKNodes получили что-то вроде IBDesignable для редактора сцен. :)

person uchuugaka    schedule 05.12.2014
comment
Я немного не уверен, что вы имеете в виду. Демонстрация в [8:00], я думаю, это то, о чем вы говорите, но она не касается подклассов SKSpriteNodes... Показанный метод -setupLevel1 может извлекать только узлы из файлов .sks, которые можно вставить в это (не определяемые пользователем подклассы). - person Ephemera; 05.12.2014
comment
Хотя, если вы объедините этот метод с ответом Флинта, он может работать при условии, что вы хотите, чтобы каждый узел в данном файле .sks был подклассом одного и того же класса... - person Ephemera; 05.12.2014
comment
Напротив, я вижу это в скачанном видео на 7 минуте, и это соответствует тому, что я описал выше. Это категория на SKScene. Файлы .sks — это SKScenes. SKScene — это подкласс SKNode (и SKEffectNode). Вы можете применить все это в классе сцены, так как только он действительно разархивирует сцену из файла. Такое ощущение, что они немного поторопились с этим за дверью. :) - person uchuugaka; 05.12.2014
comment
Я понимаю, откуда вы исходите, но разве вам все еще не нужен дополнительный: [arch setClass:MySpriteNodeSubclass forClassName:@"SKSpriteNode"] для всех SKSpriteNodes внутри SKScene? - person Ephemera; 05.12.2014
comment
Да, вам нужно настроить логику для каждого типа в архиве. Это тот же малоизвестный способ подстановки классов в initWithCoder: в ваших собственных подклассах в какао в целом. Чаще встречается с условной логикой в ​​кластерах классов, например в базовых классах. - person uchuugaka; 05.12.2014
comment
Ах я вижу. Спасибо, вы очень помогли! :) - person Ephemera; 08.12.2014

Я сделал небольшой трюк, как использовать редактор SceneKit, и я попытаюсь использовать его, но я не уверен, как это может снизить производительность. Я хочу использовать редактор сцен для создания сложных структур SKNodes и загрузки их в текущую сцену. Это также может помочь с подклассами узлов.

Я настраиваю свой узел в редакторе сцен, устанавливаю родительский узел как пустой узел с именем «база» и создаю подкласс SKNode. Затем я создал метод инициализации и вызываю его, когда мне нужно в моей сцене:

// subclass code
class MyOwnSKNodeSubclass: SKNode {

let nodeLayer = SKNode()

   init(fromNode: SKNode){
       super.init()

       nodeLayer.addChild(fromNode.childNodeWithName("base").copy() as SKNode)
       addChild(nodeLayer)
   }
}



// scene code
if let myNode = SKNode.unarchiveNodeFromFile("MyScene")
{
   // you can use your subclass here
   let node = MyOwnSKNodeSubclass(fromNode: myNode)

   addChild(node)
}

Я также использовал собственное расширение SKNode, разница невелика, но:

class func unarchiveNodeFromFile(file:String) -> SKNode?{

    if let path = NSBundle.mainBundle().pathForResource(file, ofType: "sks") {
        var nodeData = NSData.dataWithContentsOfFile(path, options: .DataReadingMappedIfSafe, error: nil)
        var archiver = NSKeyedUnarchiver(forReadingWithData: nodeData)

        archiver.setClass(self.classForKeyedUnarchiver(), forClassName: "SKNode")
        let scene = archiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as SKNode
        archiver.finishDecoding()
        return scene
    } else {
        return nil
    }
}

Как я уже сказал, я не уверен в производительности, но предполагаю, что это не сильно повредит, если не использовать это очень часто. Надеюсь, это поможет кому-то.

person Dima Deplov    schedule 29.08.2014

Да, Apple должна сделать это проще для всех нас, но на данный момент вот наиболее подходящее решение для моих нужд:

Избегайте создания подклассов

Конечно, это может быть вообще невозможно, но в моем случае подкласс, который я имел в виду, содержит логику для управления графикой, которую я разместил в файле SKS... Звучит ужасно близко к ViewController, не так ли?

Я создал класс OverlayController и инициализировал его с помощью:

self.childNodeWithName(Outlet.OverlayNode)!

Так что теперь оверлейный узел — это просто тупой узел, а контроллер — полноценный подкласс со всеми преимуществами.

Но это еще не все

Просто добавлю это, чтобы подсластить сделку:

private extension SKNode {
    var progressBar: SKNode {
        return self.childNodeWithName("ProgressBar")!
    }
}

Это приватное расширение внутри нового файла класса контроллера, так что мы можем безболезненно получить доступ к дочерним элементам тупого SKNode.

Болевая точка

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

person Mazyod    schedule 21.03.2015