子类化使用 SpriteKit .sks 场景文件创建的 SKNode

Posted

技术标签:

【中文标题】子类化使用 SpriteKit .sks 场景文件创建的 SKNode【英文标题】:Subclassing SKNodes created with SpriteKit .sks scene file 【发布时间】:2014-08-04 16:25:29 【问题描述】:

(适用于 XCode 6 和 ios 8 beta 4)

喜欢新的 SceneKit 编辑器。我已成功将 .sks 文件中的场景加载到自定义 SKScene 类中。但是,其中的对象被实例化为默认类(SKNode、SKSpriteNode 等),我不确定如何将它们绑定为实例化为自定义子类。

目前,我正在通过创建自定义类并将精灵节点作为属性链接来解决这个问题,并且工作正常

【问题讨论】:

我希望苹果能在下一次 Xcode 更新中提供这个功能。 添加赏金,因为我想知道是否有更好的方法来实现这一点(Xcode 6.1,2014 年 12 月)。 我认为他们最终会在场景编辑器中做一些事情,就像他们现在在 IB 中为 AppKit 和 UIKit 做的 IBDesignable 一样。但如果你想要它,每个人都会提交请求它的错误!这是推动他们确定优先顺序的关键因素之一。 我们将能够在 Xcode 7 中做到这一点! @NobodyNada : 你是如何在 Xcode 7 中做到这一点的? 【参考方案1】:

目前您应该在添加到 SpriteKit 游戏模板的方法中通过名称来执行此操作。他们在 WWDC 2014 的 SpriteKit 最佳实践视频中介绍了这个主题。很容易错过,因为视频中有很多内容。

方法如下。

+ (instancetype)unarchiveFromFile:(NSString *)file 
    /* Retrieve scene file path from the application bundle */
    NSString *nodePath = [[NSBundle mainBundle] pathForResource:file ofType:@"sks"];
    /* Unarchive the file to an SKScene object */
    NSData *data = [NSData dataWithContentsOfFile:nodePath
                                          options:NSDataReadingMappedIfSafe
                                            error:nil];
    NSKeyedUnarchiver *arch = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
    [arch setClass:self forClassName:@"SKScene"];
    SKScene *scene = [arch decodeObjectForKey:NSKeyedArchiveRootObjectKey];
    [arch finishDecoding];

    return scene;

它是SKScene 上的一个类别,其中包括一个方法,当从mainBundle 中的.sks 文件加载时,使用NSKeyedUnarchiver 替换类。在 iOS 中,他们将其添加到 GameViewController.m 文件中,而在 OS X 中,他们将其添加到 AppDelegate.m 文件中。

在这里或在您的各个 SKNode 子类中,您可以实现这一点。这就是他在下面的帖子中所做的。这是由 Apple 提供的,并在 WWDC 视频中进行了介绍。我想明年它会得到更好的处理。在此期间,请求 SKNodes 的错误文件会获得类似 IBDesignable for Scene Editor 的内容。 :)

【讨论】:

我有点不确定你的意思。我认为 [8:00] 的演示是您所指的,但它不处理 SKSpriteNodes 的子类...所示的 -setupLevel1 方法只能从 .sks 文件中提取节点插入其中(不是用户定义的子类)。 尽管如果您将该方法与 flinth 的答案结合使用,但前提是您希望给定 .sks 文件中的每个节点都被子类化为同一类... 相反,我在下载的视频中看到了 7 分钟,它与我上面描述的相符。它是 SKScene 上的一个类别。 .sks 文件是 SKScene。 SKScene 是一个 SKNode(和 SKEffectNode)子类。您可以将其全部应用到场景类中,因为这是唯一真正从文件中取消归档场景的方法。这一切都感觉就像他们把它赶出了门一样。 :) 我知道您来自哪里,但您是否还需要一个额外的:[arch setClass:MySpriteNodeSubclass forClassName:@"SKSpriteNode"] 用于 SKScene 中的所有 SKSpriteNodes? 啊,我明白了。谢谢你,你很有帮助! :)【参考方案2】:

我写了一个小助手来处理这个问题:https://github.com/ice3-software/node-archive-delegate。它使用NSKeyedUnarchiverDelegate 按名称对您的精灵进行子类化。

【讨论】:

【参考方案3】:

我做了一个小技巧如何使用 SceneKit 编辑器,我会尝试使用它,但我不确定这会如何降低性能。我想使用场景编辑器制作复杂的 SKNodes 结构并将它们加载到当前场景中。它还可以帮助子类化节点。

我在场景编辑器中配置我的节点,将父节点设置为名为“base”的空节点,并创建了 SKNode 的子类。然后我创建了一个 init 方法,并在我的场景中需要时调用它:

// subclass code
class MyOwnSKNodeSubclass: SKNode 

let nodeLayer = SKNode()

   init(fromNode: SKNode)
       super.init()

       nodeLayer.addChild(fromNode.childNodeWithName("base").copy() as SKNode)
       addChild(nodeLayer)
   




// scene code
if let myNode = SKNode.unarchiveNodeFromFile("MyScene")

   // you can use your subclass here
   let node = MyOwnSKNodeSubclass(fromNode: myNode)

   addChild(node)

我也用了自己的 SKNode 扩展,差别不大但是:

class func unarchiveNodeFromFile(file:String) -> SKNode?

    if let path = NSBundle.mainBundle().pathForResource(file, ofType: "sks") 
        var nodeData = NSData.dataWithContentsOfFile(path, options: .DataReadingMappedIfSafe, error: nil)
        var archiver = NSKeyedUnarchiver(forReadingWithData: nodeData)

        archiver.setClass(self.classForKeyedUnarchiver(), forClassName: "SKNode")
        let scene = archiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as SKNode
        archiver.finishDecoding()
        return scene
     else 
        return nil
    

正如我所说,我不确定生产力,但我认为如果不经常使用它不会造成太大伤害。希望这对某人有所帮助。

【讨论】:

【参考方案4】:

是的,Apple 应该让我们所有人都更容易做到这一点,但就目前而言,这是满足我需求的最可行的解决方案:

避免子类化

当然,这可能根本不可能,但就我而言,我想到的子类包含控制我在 SKS 文件中布局的图形的逻辑......听起来非常接近到 ViewController,不是吗?

我创建了一个OverlayController 类,并使用以下命令对其进行初始化:

self.childNodeWithName(Outlet.OverlayNode)!

所以现在,overlay 节点只是一个哑节点,而控制器是具有所有优点的完整子类。

但还有更多

只是为了让交易更甜蜜:

private extension SKNode 
    var progressBar: SKNode 
        return self.childNodeWithName("ProgressBar")!
    

这是新控制器类文件中的私有扩展,因此我们可以轻松访问哑巴SKNode 的孩子。

痛点

一个公认的痛点是触摸处理。将 SKNode 子类化以进行自定义触摸处理是很自然的,而组合方法在这方面几乎没有任何作用。我现在正在转发现场的触摸,但如果有更好的方法,我会发布更新。

【讨论】:

以上是关于子类化使用 SpriteKit .sks 场景文件创建的 SKNode的主要内容,如果未能解决你的问题,请参考以下文章

SpriteKit .sks文件在分配纹理时崩溃Xcode 10

SpriteKit代码动态调整sks文件中粒子的颜色

在spritekit swift问题中更改场景

从文件名加载 SpriteKit 关卡

如果使用编辑器中的场景加载,如何在 SpriteKit 中检测触摸

将 UITextView 添加到 SpriteKit 中的场景