.sks 文件中 SKSpriteNode 的子类 [英] Subclass of SKSpriteNode in .sks file
问题描述
我正在使用 SpriteKit .sks 文件我可以将 .sks 中的精灵制作成 SKSpriteNode 子类的实例吗?
I'm using SpriteKit .sks file Can I make a sprite in .sks into an instance of subclass of SKSpriteNode?
这是我的子类中的 init 方法:
This is the init method in my subclass:
init(imageNamed: String) {
let blockTexture = SKTexture(imageNamed: imageNamed)
super.init(texture: blockTexture, color: nil, size: blockTexture.size())
}
在 GameScene.swift 中,我可以创建一个这样的实例:
In GameScene.swift I can create an instance like this:
var myObj = Block(imageNamed: "Block")
我的问题是如何将此实例与 .sks 文件相关联?
My question is how can I relate this instance with .sks file?
我试过这行代码,但没有用.
I tried this line of code but it doesn't work.
myObj = childNodeWithName("block1") as Block
有什么帮助吗?
谢谢.
推荐答案
这里有几个问题需要解决...
There are a couple of issues here to address...
当您加载 .sks
文件时,SpriteKit 会使用 NSKeyedUnarchiver
实例化其中的所有内容.这意味着里面的所有节点都被加载为 Xcode 在创建 .sks
文件时指定为它们的任何基类 - SKSpriteNode
用于带有纹理艺术的精灵,SKLabelNode
用于文本,SKFieldNode
用于物理场等.Xcode 目前不提供为 .sks
中的节点设置自定义类的选项文件.
When you load a .sks
file, SpriteKit instantiates everything within using NSKeyedUnarchiver
. This means that all the nodes inside are loaded as whatever base classes they were specified as by Xcode when it created the .sks
file — SKSpriteNode
for sprites with texture art, SKLabelNode
for text, SKFieldNode
for physics fields, etc. And Xcode doesn't currently provide an option for setting custom classes for the nodes inside a .sks
file.
(对此的一个例外是更改场景本身的运行时类——.sks
文件中所有内容的顶级容器.这仅仅是因为自定义 SKNode.在项目模板中为您提供了 unarchiveFromFile
实现.当您在存档中只有一个特定类的一个且只有一个实例时,它用于在加载时更改类的技术有效 - 对 SKScene
很有用,对于场景中的许多节点来说不是很好.)
(The one exception for this is changing the runtime class of the scene itself — the top level container for everything in the .sks
file. And that's only because of the custom SKNode.unarchiveFromFile
implementation provided for you in the project template. Its technique for changing classes at load time works when you have one and only one instance of a particular class in an archive — good for SKScene
, not so good for the many nodes in a scene.)
当你写这样的东西时:
myObj = childNodeWithName("block1") as Block
你告诉编译器类似:嘿,你知道你从 childNodeWithName
得到的东西吗?你只知道它是一个 SKNode
,但我知道它实际上是一个 Block
,所以请让我在其上调用 Block
方法."(编译器说,好吧,随便.")
You're telling the compiler something like: "Hey, you know that thing you got from childNodeWithName
? All you know is that it's an SKNode
, but I know it's really a Block
, so please let me call Block
methods on it." (And the compiler says, "Okay, whatever.")
但是在运行时,你得到的东西最好真的是Block
,否则你的应用会因为你试图用不是 Block
的东西做一些 Block
y.而且,根据上面关于 .sks
加载的一点,那个东西不是也不能是 Block
— Xcode 不知道如何放置 Block
s 到 .sks
文件.所以你不能从中获取Block
,所以你的应用肯定会崩溃.
But then at run time, that thing you got had better really be a Block
, or your app will crash because you tried to do something Block
y with something that's not a Block
. And, per the bit about .sks
loading above, that thing isn't and can't be a Block
— Xcode doesn't know how to put Block
s into a .sks
file. So you can't get a Block
out of it, so your app is guaranteed to crash.
因此,如果您不能将自定义类放入 .sks
文件中,您可以做什么?这在一定程度上取决于您究竟要完成什么.但有一个很好的技巧,通常也可能是好的游戏/应用程序设计:使用 .sks
文件进行总体布局和配置,并使用第二遍引入需要自定义行为的东西.
So, if you can't put custom classes into a .sks
file, what can you do? It depends a bit on what exactly you're trying to accomplish. But there's a good trick that might also be good game/app design in general: use the .sks
file for general layout and configuration, and use a second pass to bring in things that need custom behavior.
例如,如果您在 2D 平台游戏中构建关卡,您不会真的希望 .sks
文件包含您的 Plumber
实例> 班级,即使你可以——那个班级可能有很多关于这个人有多高或多胖、他跳多高、他的胡子形状等的细节,你不想每次都重新设置这些当您创建一个新关卡时,更不用说将它们再次保存在每个关卡的 .sks
文件中.相反,您在每个级别文件中真正需要知道的唯一事情是他开始的位置.因此,在 Xcode 中拖出一个空节点",并在加载时将该节点替换为您的 Plumber
类的实例,如下所示:
For example, if you're building a level in a 2D platform game, you wouldn't really want to have the .sks
file contain an instance of your Plumber
class even if you could — that class probably has lots of details about how tall or fat the guy is, how high he jumps, the shape of his mustache, etc, and you don't want to have to set those up again every time you make a new level, much less have them saved again in each level's .sks
file. Instead, the only thing you really need to know in each level file is the position he starts at. So, drag out an "Empty Node" in Xcode, and at load time, replace that node with an instance of your Plumber
class, like so:
let spawnPoint = childNodeWithName("spawnPoint")
let player = Plumber()
player.position = spawnPoint.position
addChild(player)
spawnPoint.removeFromParent()
如果您想在 .sks
文件中设置更多配置详细信息,您可以考虑自动化该过程.
If you have more configuration details that you want to set in the .sks
file, you might consider automating that process.
- 制作一个执行上述节点交换技巧的方法.(称之为
replaceNode(_:withNode:)
.) - 为采用
SKNode
或SKSpriteNode
的自定义类创建一个初始化程序,并让它设置其所有继承的属性(或至少是您关心的属性,例如颜色和纹理)来自该节点. 使用
enumerateChildNodesWithName:usingBlock:
带有 搜索模式 用某种名称,并将它们替换为使用初始化程序创建的新节点.类似的东西:
- Make a method that does the above node-swapping trick. (Call it something like
replaceNode(_:withNode:)
.) - Make an initializer for your custom class that takes a
SKNode
orSKSpriteNode
, and have it set all its inherited properties (or at least the ones you care about, like color and texture) from that node. Use
enumerateChildNodesWithName:usingBlock:
with a search pattern to find all the nodes in your scene with a certain kind of name, and replace them with a new node created using your initializer. Something like:
enumerateChildNodesWithName("//brick_[0-9]*") { node, stop in
self.replaceNode(node, withNode: BrickBlock(node))
}
enumerateChildNodesWithName("//question_[0-9]*") { node, stop in
self.replaceNode(node, withNode: QuestionBlock(node))
}
这篇关于.sks 文件中 SKSpriteNode 的子类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!