Добавление пользовательского GKComponent к объекту в редакторе сцен Xcode SpriteKit устанавливает для GKScene.rootNode значение nil.

Когда я добавляю CustomComponent (GKComponent) к объекту в редакторе сцен Xcode SpriteKit и пытаюсь загрузить этот файл .sks с помощью GKScene.init, GKScene.rootNode не устанавливается. Что еще более странно, это происходит только на iOS 13, а не на iOS 12.

У меня есть небольшая настройка проекта github с набором спрайтов, которая четко демонстрирует эту проблему. Просто запустите приложение на эмуляторе iOS 13, чтобы воспроизвести проблему. https://github.com/hdsenevi/answers-gkscene-rootnode-nil-bug

Если я удалю CustomComponent из объекта/спрайта редактора сцен SpriteKit, он будет работать нормально. то есть: загружает SKScene в GKScene.rootNode

  1. Существуют ли какие-либо другие специальные модификации, которые необходимо выполнить при добавлении GKComponents из редактора сцен Xcode SpriteKit?
  2. Я пропустил что-то очевидное здесь?
  3. И почему этот код без проблем работает на iOS 12, а не на iOS 13?
  4. Изменилась ли функциональность SpriteKit в связи с этим в iOS 13?

Для справки

import UIKit
import SpriteKit
import GameplayKit

class GameViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Load 'GameScene.sks' as a GKScene. This provides gameplay related content
        // including entities and graphs.
        if let scene = GKScene(fileNamed: "GameScene") {

            // Get the SKScene from the loaded GKScene
            if let sceneNode = scene.rootNode as! GameScene? {

                // Copy gameplay related content over to the scene
                sceneNode.entities = scene.entities
                sceneNode.graphs = scene.graphs

                // Set the scale mode to scale to fit the window
                sceneNode.scaleMode = .aspectFill

                // Present the scene
                if let view = self.view as! SKView? {
                    view.presentScene(sceneNode)

                    view.ignoresSiblingOrder = true

                    view.showsFPS = true
                    view.showsNodeCount = true
                }
            } else {
                print("Error. No GameScene was found on GKScene.rootNode")
            }
        } else {
            print("Error loading GKScene file GameScene")
        }
    }
}
import SpriteKit
import GameplayKit

class CustomComponent: GKComponent {
    override init() {
        super.init()
    }

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

    override func didAddToEntity() {
        guard let gkSkNodeComponent = self.entity?.component(ofType: GKSKNodeComponent.self) else {
            print("Error. Cannot obtain a reference to GKSKNodeComponent")
            return
        }

        if let sprite = gkSkNodeComponent.node as? SKSpriteNode {
            sprite.texture?.filteringMode = .nearest
        }
    }
}

Обновить

Немного подробностей о моей настройке

  • macOS Мохаве 10.14.6
  • Xcode 11.0 (пробовал на 10.1 и 10.3, такое же поведение)

person hdsenevi    schedule 05.10.2019    source источник
comment
Правильно ли вы очистили проект при сборке для iOS 13?   -  person Knight0fDragon    schedule 06.10.2019
comment
Да. Я сделал все, о чем только мог подумать. 1) удалил папку build 2) удалил папку derived data 3) сбросил симуляторы iOS 12 и 13 4) попробовал разные симуляторы устройств (6, 8, X, 11). Вы можете попробовать это сами, просто просмотрев репозиторий, который я упомянул в вопросе, и запустив этот образец проекта на симуляторе iOS 12 и iOS 13. Даже iOS 13 работает, если вы удалите CustomComponent, прикрепленный к узлу red sprite в файле GameScene.sks. Но очевидно хотелось бы прикрепить компоненты из редактора сцен SpriteKit.   -  person hdsenevi    schedule 07.10.2019
comment
У меня точно такая же проблема с вами после установки Xcode11. Нам нужно отправить отчет об ошибке в Apple! Дополнительные отчеты могут ускорить процесс отладки в команде Apple.   -  person Sungwook Kim    schedule 12.10.2019
comment
Облом, это, по сути, делает часть редактора сцен системы компонентов бесполезной. Жалко, однако, такого. отличная функция   -  person Travis M.    schedule 17.12.2019


Ответы (5)


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

person Andrew Eades    schedule 27.10.2019
comment
Есть новости по отчету об ошибке? У вас есть ссылка, так что, может быть, мы можем проверить напрямую или это частная ссылка? У меня такая же проблема на Xcode 11.2.1 GM. - person La pieuvre; 07.11.2019
comment
Я не получил ответа (FB7411636). Я обнаружил еще одну проблему, заключающуюся в том, что при копировании узла компоненты не копируются, поэтому вам нужно заново добавить их все. Мое решение состояло в том, чтобы написать некоторый код, который проходит через узлы при загрузке и программно добавляет компоненты на основе либо имени, либо установки значения в userData, такого как playerInputComponent: true. - person Andrew Eades; 09.11.2019
comment
@AndrewEades, не могли бы вы опубликовать ссылку на статью, в которой описывается проблема и/или код обходного пути, который вы использовали? - person rswayz; 17.03.2020

Чтобы решить эту проблему, ваш компонент должен переопределить переменную supportsSecureCoding, и она должна возвращать значение true.

Это сработало для меня:

override class var supportsSecureCoding: Bool {
    return true
}
person GoSti0    schedule 02.10.2020

Это происходило и со мной. У меня есть Color Sprite с типом тела Alpha Mask, я обнаружил, что если я изменю тип тела на любой другой тип, это исправит это, и rootNode снова заработает.

person vauxhall    schedule 17.01.2020

Так что у меня есть довольно хитрый обходной путь, если вы хотите сохранить тот же стиль определения компонентов в редакторе сцен SK с помощью UserData.

В редакторе сцен SK заполните узлы UserData атрибутами, которые ваш код затем использует для превращения в компоненты, а затем автоматизируйте настройку GKSKNodeComponents.

Я создал класс TemplateScene, который я поместил в качестве сцены файла sks.

import Foundation
import SpriteKit
import GameplayKit

class TemplateScene: SKScene {
    
    lazy var entities: [GKEntity] = {
        var entities: [GKEntity] = []
        // applyAllChildren is an extension I made that simple recurses all children, children's children etc
        self.applyAllChildren { node in
            if let userData = node.userData {
                var components: [GKComponent] = []
                if userData["c.BasicComponent"] as? Bool == true {
                    components.append(BasicComponent())
                }
                if components.count > 0 {
                    components.append(GKSKNodeComponent(node: node))
                    let entity = GKEntity()
                    for component in components {
                        entity.addComponent(component)
                    }
                    entities.append(entity)
                }
            }
        }
        return entities
    }()
}

Просто продолжайте расширять код, чтобы обрабатывать больше компонентов. Если у вас есть атрибуты компонента, тогда используйте UserData в виде c.BasicComponent.x и т. д.

(Чтобы было ясно, это хак, чтобы обойти тот факт, что у Apple есть серьезная ошибка, которая делает большую часть функций редактора SpriteKit непригодной для всей версии iOS. Может быть, это исправлено в iOS 14?)

person The Mad Bug    schedule 26.07.2020

До iOS 13 я обращался к корневому узлу GKScene. По непонятным мне причинам это приводит к нулю с iOS 13+. Однако, о чудо, вы можете получить доступ к устаревшему «корневому узлу» прямо из GameScene. Мне не пришлось каким-либо образом изменять какие-либо пользовательские классы, чтобы восстановить полную функциональность.

//From GameViewController (UIViewController):

- (void)viewDidLoad {

 [super viewDidLoad];
 GameScene *sceneNode;

 NSOperatingSystemVersion ios13_0_0 = (NSOperatingSystemVersion){13, 0, 0};
 if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:ios13_0_0]) {
    // iOS 13.0.0 and above logic for rootNode
    // Load the SKScene from 'GameScene.sks'
    GameScene *scene = (GameScene *)[SKScene nodeWithFileNamed:@"GameScene"];

    SKView *skView = (SKView *)self.view;

     // The fix for use in iOS 13.0+
    sceneNode = scene;

    // Set the scale mode to scale to fit the window
    sceneNode.scaleMode = SKSceneScaleModeAspectFill;

    // Present the scene
    [skView presentScene:scene];
} else {
   // prior to iOS 13.0.0 logic
    GKScene *scene = [GKScene sceneWithFileNamed:@"GameScene"];

    SKView *skView = (SKView *)self.view;

     // This will result in nil if used in iOS 13.0+
     sceneNode = (GameScene *)(scene.rootNode);

    // Set the scale mode to scale to fit the window
    sceneNode.scaleMode = SKSceneScaleModeAspectFill;

    // Present the scene
    [skView presentScene:sceneNode];
 } 
}

Это создает экземпляр GameScene (SKScene). В GameScene я могу получить доступ к пользовательским узлам, которые я добавил в сцену, с произвольным именем через:

for (SKNode *node in self.children)
{
       if ([node.name containsString:@"NameOfNodeHere"])
    {
         // Access your custom node in your .sks here
    }
}
person rswayz    schedule 05.01.2020
comment
Это дает вам ссылку на SKScene, но есть ли способ получить доступ к объекту GKScene из ветки кода iOS 13? Скажем, чтобы получить список сущностей в сцене? Похоже, что этот код можно использовать только в том случае, если файл .sks не использует компоненты. - person cc.; 21.04.2020
comment
@cc. - Да. Я обновил свой ответ выше - надеюсь, ответил на ваш вопрос. - person rswayz; 22.04.2020