无法继承 SCNScene [英] Trouble subclassing SCNScene

查看:48
本文介绍了无法继承 SCNScene的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在尝试对 SCNScene 进行子类化,因为这似乎是保持我的场景相关逻辑的最佳位置.现在我不确定这是否值得推荐,所以我的第一个问题是 - 我应该继承 SCNScene,如果不是为什么不?文档似乎表明它很常见,但我在网上阅读了一些评论,建议我不应将其子类化. 废话,我正在查看 SKScene 的文档.SCNScene 类参考没有提到子类化.

假设可以这样构建我的游戏,这是我的进步

//GameScene.swift进口基金会导入场景套件类 GameScene:SCNScene {懒惰的变量实体管理器:BREntityManager = {返回 BREntityManager(场景:self)}()覆盖初始化(){打印(游戏场景初始化")超级初始化()}需要初始化?(编码器aDecoder:NSCoder){super.init(编码器:aDecoder)}func someMethod() {//这是我计划设置 GameplayKit 的地方//对于场景打印(调用了某个方法")}}

注意:根据对 这个问题

在我的视图控制器中,我正在尝试像这样使用 GameScene

class GameViewController: UIViewController {覆盖 func viewDidLoad() {super.viewDidLoad()//创建一个新场景让scene = GameScene(命名为:art.scnassets/game.scn")!//检索 SCNView让 scnView = self.view as!SCN视图//将场景设置为视图scnView.scene = 场景//应该打印someMethod called"场景.someMethod()}}

但是,调用 GameScene.someMethod() 会触发 EXEC_BAD_ACCESS 错误.

此外,如果我省略对 GameScene.someMethod 的调用,场景会正确加载,但 GameScene 中覆盖的初始值设定项似乎没有被调用.

我不确定这里发生了什么.很明显,我不明白 Swift 中的子类化.或者,也许我错过了某些事情应该运行的顺序.

解决方案

我应该继承 SCNScene,如果不是为什么不?

不,您不需要子类化,而且最好不要子类化.SCNScene 更像是基本的数据/模型类(NSStringUIImage 等)而不是行为/控制器/视图类(UIViewControllerUIControl 等).也就是说,它是某物(在本例中为 3D 场景)的一般描述或容器,并不真正提供行为.由于行为方式并不多,因此没有太多机会用子类覆盖行为.(也不是围绕子类入口点设计的类旨在被覆盖,例如 viewDidLoad 等等.)

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

<块引用>

但是,调用 GameScene.someMethod() 会触发 EXEC_BAD_ACCESS 错误.

此外,如果我省略对 GameScene.someMethod 的调用,场景会正确加载,但 GameScene 中覆盖的初始值设定项似乎没有被调用.

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

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

旁白:这里为什么 SpriteKit 与 SceneKit 不同?SKScene 有点奇怪,因为它既不是纯模型类,也不是纯控制器类.这就是为什么您会看到许多项目(包括 Xcode 模板)使用 SKScene 子类的原因.然而,这种方法有缺点——如果你不仔细计划,很容易将你的游戏逻辑与你的游戏数据紧密结合起来,这使得扩展你的游戏(例如,多层次)需要繁琐的编码而不是数据编辑.有一个WWDC 2014 会议关于这个主题,非常值得一看.上>

那么,该怎么办?

您在这里有几个选择...

  1. 不要子类化.将您的游戏逻辑放在一个单独的类中.这不一定是一个视图控制器类——您可以拥有一个或多个由您的视图控制器或应用程序委托拥有的 Game 对象.(如果您的游戏逻辑在多个场景之间转换或操作多个场景的内容,这尤其有意义.)

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

  3. 子类,但强制 NSKeyedUnarchiver 加载您的子类而不是 SCNScene.(您可以使用 类名映射.)

其中,#1 可能是最好的.

I've been trying to subclass SCNScene as that seems like the best place to keep my scene related logic. Now I'm not sure wether that's reccomended so my first question is - Should I be subclassing SCNScene, and if not why not? The documentation seems to suggest it's common but I've read comments online that suggest I shouldn't subclass it. Scrap that, I was looking at the documentation for SKScene. The SCNScene class reference makes no mention of subclassing.

Assuming it's ok to structure my game that way, here's my progress

//  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")
    }

}

Note: I'm using lazy var as per the answer to this question

In my view controller, i'm trying to use GameScene like this

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()
    }
}

However, the call to GameScene.someMethod() triggers an EXEC_BAD_ACCESS error.

Also, if I omit the call to GameScene.someMethod, the scene loads correctly, but the overridden initializer in GameScene doesn't appear to be called.

I'm not sure what's going on here. It's clear there's something about subclassing in Swift that I've not understood. Or perhaps there' some aspect of order in which things are meant to run that I've missed.

解决方案

Should I be subclassing SCNScene, and if not why not?

No, you don't need to subclass, and you're probably better off not subclassing. SCNScene is more like basic data/model classes (NSString, UIImage, etc) than like behavior/controller/view classes (UIViewController, UIControl, etc). That is, it's a general description of or container for something (in this case, a 3D scene) and doesn't really provide behavior. Since there's not much in the way of behavior, there's not much opportunity to override behavior with a subclass. (Nor is the class designed around subclass entry points meant to be overridden, like viewDidLoad and whatnot.)

While it's possible to subclass SCNScene, you don't gain much from doing so, and some APIs might not work as you expect...

However, the call to GameScene.someMethod() triggers an EXEC_BAD_ACCESS error.

Also, if I omit the call to GameScene.someMethod, the scene loads correctly, but the overridden initializer in GameScene doesn't appear to be called.

A .scn file is actually an NSKeyedArchiver archive, which means that the objects inside it have the same classes that were used to create it. when you call init(named:) on your SCNScene subclass, the superclass' implementation eventually calls through to NSKeyedUnarchiver unarchiveObjectWithData: to load the archive. Absent some unarchiver mangling, that method will instantiate a SCNScene, not an instance of your subclass.

Because you don't get an instance of your subclass, your subclass initializers aren't called, and attempting to call your subclass' methods results in a runtime error.

Aside: Why is SpriteKit different from SceneKit here? SKScene is a bit of an odd duck in that it's neither a pure model class nor a pure controller class. This is why you see a lot of projects, including the Xcode template, using an SKScene subclass. There are drawbacks to this approach, however — if you don't plan carefully, it gets too easy to wed your game logic tightly to your game data, which makes expanding your game (say, to multiple levels) require tedious coding instead of data editing. There's a WWDC 2014 session on this topic that's well worth watching.

So, what to do?

You have a few choices here...

  1. Don't subclass. Keep your game logic in a separate class. This doesn't necessarily have to be a view controller class — you could have one or more Game objects that are owned by your view controller or app delegate. (This makes especially good sense if your game logic transitions between or manipulates the content of multiple scenes.)

  2. Subclass, but arrange to put the stuff from an unarchived SCNScene into an instance of your subclass — instantiate your subclass, load an SCNScene, then move all children of its root node into your subclass' root node.

  3. Subclass, but force NSKeyedUnarchiver to load your subclass instead of SCNScene. (You can do this with class name mappings.)

Of these, #1 is probably the best.

这篇关于无法继承 SCNScene的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆