Swift SCNScene亚类化困难

Swift SCNScene亚类化困难,swift,scenekit,Swift,Scenekit,我一直在尝试将SCNScene子类化,因为这似乎是保存场景相关逻辑的最佳位置。现在我不确定这是否会被提出,所以我的第一个问题是——我是否应该将SCNScene子类化,如果不是,为什么不?文档似乎表明它很常见,但我在网上看到的评论表明我不应该将其子类化。废话,我正在看SKScene的文档。SCNScene类引用没有提到子类化 假设这样设计我的游戏是可以的,下面是我的进展 // GameScene.swift import Foundation import SceneKit class G

我一直在尝试将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
,与

在我的视图控制器中,我正试图像这样使用游戏场景

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()
    }
}
但是,调用
GameSecene.someMethod()
会触发
EXEC\u BAD\u ACCESS
错误

另外,如果我省略了对
GameSecene.someMethod
的调用,场景将正确加载,但是
GameSecene
中被重写的初始值设定项似乎没有被调用

我不知道这里发生了什么。很明显,Swift中的子类化有一些我不理解的地方。或者,也许有些事情的运行顺序是我错过的

我应该子类化
SCNScene
,如果不是,为什么不

不,您不需要子类化,而且您最好不要子类化
SCNScene
更像基本数据/模型类(
NSString
UIImage
,等等),而不像行为/控制器/视图类(
UIViewController
UIControl
,等等)。也就是说,它是对某物(在本例中为3D场景)的一般描述或容器,并不真正提供行为。因为没有太多的行为方式,所以没有太多机会用子类重写行为。(类也不是围绕要重写的子类入口点设计的,比如
viewDidLoad
等等。)

虽然可以将
SCNScene
子类化,但这样做并不会带来太多好处,而且某些API可能无法按预期工作

但是,调用
GameSecene.someMethod()
会触发
EXEC\u BAD\u ACCESS
错误

另外,如果我省略了对
GameSecene.someMethod
的调用,场景将正确加载,但是
GameSecene
中被重写的初始值设定项似乎没有被调用

.scn
文件实际上是一个
NSKeyedArchiver
归档文件,这意味着其中的对象具有与创建该文件相同的类。当您在
SCNScene
子类上调用
init(命名:)
时,超类的实现最终会调用
NSKeyedUnarchiver
unarchiveObjectWithData:
以加载存档。如果没有一些未归档的损坏,该方法将实例化一个
SCNScene
,而不是子类的实例

因为您没有获得子类的实例,所以不会调用子类初始值设定项,并且尝试调用子类的方法会导致运行时错误

旁白:为什么SpriteKit和SceneKit在这里不同
SKScene
有点奇怪,因为它既不是纯模型类,也不是纯控制器类。这就是为什么您会看到很多项目,包括使用
SKScene
子类的Xcode模板。然而,这种方法也有缺点——如果你不仔细计划,游戏逻辑很容易与游戏数据紧密结合,这使得扩展游戏(比如说,扩展到多个级别)需要繁琐的编码而不是数据编辑。有一部电影很值得一看

那么,该怎么办? 你在这里有几个选择

  • 不要子类化。将游戏逻辑保留在单独的类中。这不一定是视图控制器类-您可以拥有一个或多个视图控制器或应用程序代理拥有的
    游戏
    对象。(如果您的游戏逻辑在多个场景之间转换或操纵多个场景的内容,这一点尤其有意义。)

  • 子类,但安排将未归档的
    SCNScene
    中的内容放入子类的实例中-实例化子类,加载
    SCNScene
    ,然后将其根节点的所有子节点移动到子类的根节点中

  • 子类,但强制
    nskeyedunachiver
    加载子类,而不是
    SCNScene
    。(您可以使用以下选项执行此操作。)


  • 其中#1可能是最好的;dr:Extend
    SCNNode
    以编程方式生成场景

    这一开始是对Rickster极好的回答的评论,但我觉得它太长了。本季度我教了一门课程,花了大量时间在SceneKit上。我们一直子类化
    SCNScene
    ,没有遇到任何问题

    然而,在花了一个小时回顾了我展示的代码、学生们编写的代码以及大量其他示例代码(来自苹果和其他开发人员)之后,我不会再将
    SCNScene
    子类化

    没有一个Apple示例代码子类
    SCNScene
    。这应该是一个非常有力的线索。处理序列化(
    initWithCoder
    和从文件读取)是StackOverflow上经常出现的问题。这是你不想去那里的第二个强有力的线索

    在我回顾的所有子类
    SCNScene
    的代码中,这样做的原因是以编程方式生成场景。这是里克斯特的选择。但是这些代码实际上都不需要在
    SCNScene
    上。如果您正在以特定配置构建一组节点,添加照明和查看约束,并设置材质,则感觉就像在构建一个
    SCNScene
    。但实际上,您正在构建一个包含
    SCNNode
    实例的树

    因此,我认为事后看来,选择2并不能提供