GKState: Почему StateMachine == nil при вызове делегата?

#swift #sprite-kit #gameplay-kit

#swift #sprite-kit #игровой процесс-комплект

Вопрос:

Я разрабатываю игру SpriteKit, используя GKStateMachine для перехода между несколькими сценами. Я использую класс, который содержит экземпляр StateMachine, который имеет несколько состояний. Сами состояния имеют доступ к текущему SKView через свойство инициализатора. Состояния отвечают за представление сцен, поэтому у них есть свойство сцены, которое будет представлено в didEnter-методе. Теперь у меня есть MainMenuState, в котором есть сцена с 3 кнопками. Для передачи событий кнопки в состояние я написал пользовательский делегат. MainMenuState реализует протокол делегирования и устанавливает свойство делегата сцены в ‘self’. Итак, когда пользователь нажимает кнопку, действие перенаправляется в метод делегирования. Я хотел использовать метод делегирования для перехода в следующее состояние (например, SettingsState). Однако, когда я пытаюсь получить доступ к свойству GKStates StateMachine в функции делегирования, оно всегда равно нулю. Я думаю, что у меня здесь проблема с дизайном, и я не знаю, как ее решить.

Код MainMenuState

 import Foundation
import GameplayKit

class MainMenuState : GKState, MenuSceneDelegate {

    var view: SKView
    var scene: GKScene?

    init(view: SKView) {
        self.view = view;
        self.scene = GKScene(fileNamed: "MenuScene")
        super.init()
    }

    override func isValidNextState(_ stateClass: AnyClass) -> Bool {
        return stateClass is MultiplayerHostState.Type ||
        stateClass is MultiplayerSearchState.Type ||
        stateClass is SettingsState.Type
    }

    override func didEnter(from previousState: GKState?) {
        super.didEnter(from: previousState)
        // Load 'GameScene.sks' as a GKScene. This provides gameplay related content
        // including entities and graphs.
        if let scene = self.scene {

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

                // Set delegate
                sceneNode.menuDelegate = self

                // 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
                sceneNode.size = view.bounds.size

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

                    view.ignoresSiblingOrder = true

                    view.showsFPS = true
                    view.showsNodeCount = true
                }
            }
        }
    }



    override func willExit(to nextState: GKState) {
        super.willExit(to: nextState)
    }

    func hostGameClicked() {
        if let stateMachine = self.stateMachine {
            stateMachine.enter(MultiplayerHostState.self)
        }
    }

    func joinGameClicked() {
        if let stateMachine = self.stateMachine {
            stateMachine.enter(MultiplayerSearchState.self)
        }
    }

    func settingsClicked() {
// <-- Here the StateMachine is nil -->
        if let stateMachine = self.stateMachine {
            stateMachine.enter(SettingsState.self)
        }
    }
}
  

Ответ №1:

После некоторых исследований я выяснил, что такое поведение было вызвано системой Swifts ARC. Класс, содержащий ссылку на конечный автомат, был объявлен только в функции общего ViewController. Итак, после создания функции класс и конечный автомат были освобождены. Я решил это в контроллере просмотра:

 class ViewController {
    var classWithStateMachine: ClassWithStateMachine?

    func initializeClassWithStateMachine {
        self.classWithStateMachine = ClassWithStateMachine()
    }
}
  

Этот фрагмент кода — просто демонстрация концепции, никакого реального кода.