GKState:为什么委托调用时 StateMachine == nil?
GKState: Why is StateMachine == nil on delegate call?
我正在开发一个 SpriteKit 游戏,使用 GKStateMachine 在多个场景之间进行转换。我正在使用 class 来保存 StateMachine 的一个实例,该实例具有多个状态。状态本身可以通过初始化程序 属性 访问当前的 SKView。状态是负责呈现场景的,所以他们有一个场景属性,会在didEnter-Method上呈现。
现在我有一个 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)
}
}
}
经过一番研究,我发现此行为是由 Swifts ARC 系统引起的。
保存状态机引用的 class 仅在总体 ViewController 的函数中声明。所以在存在 func 之后,class 和状态机被释放。
我在视图控制器中解决了它:
class ViewController {
var classWithStateMachine: ClassWithStateMachine?
func initializeClassWithStateMachine {
self.classWithStateMachine = ClassWithStateMachine()
}
}
此代码片段只是概念演示,没有实际代码。
我正在开发一个 SpriteKit 游戏,使用 GKStateMachine 在多个场景之间进行转换。我正在使用 class 来保存 StateMachine 的一个实例,该实例具有多个状态。状态本身可以通过初始化程序 属性 访问当前的 SKView。状态是负责呈现场景的,所以他们有一个场景属性,会在didEnter-Method上呈现。 现在我有一个 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)
}
}
}
经过一番研究,我发现此行为是由 Swifts ARC 系统引起的。 保存状态机引用的 class 仅在总体 ViewController 的函数中声明。所以在存在 func 之后,class 和状态机被释放。 我在视图控制器中解决了它:
class ViewController {
var classWithStateMachine: ClassWithStateMachine?
func initializeClassWithStateMachine {
self.classWithStateMachine = ClassWithStateMachine()
}
}
此代码片段只是概念演示,没有实际代码。