.sks 文件中 SKSpriteNode 的子类
Subclass of SKSpriteNode in .sks file
我正在使用 SpriteKit .sks 文件我可以将 .sks 中的精灵制作成 SKSpriteNode 的子类实例吗?
这是我子类中的初始化方法:
init(imageNamed: String) {
let blockTexture = SKTexture(imageNamed: imageNamed)
super.init(texture: blockTexture, color: nil, size: blockTexture.size())
}
在 GameScene.swift 中,我可以创建这样的实例:
var myObj = Block(imageNamed: "Block")
我的问题是如何将此实例与 .sks 文件相关联?
我试过这行代码,但它不起作用。
myObj = childNodeWithName("block1") as Block
有什么帮助吗?
谢谢。
这里有几个问题需要解决...
.sks
加载如何工作
当您加载 .sks
文件时,SpriteKit 会使用 NSKeyedUnarchiver
实例化所有内容。这意味着里面的所有节点都被加载为任何基础 classes 它们在创建 .sks
文件时由 Xcode 指定为 - SKSpriteNode
对于具有纹理艺术的精灵, SKLabelNode
用于文本,SKFieldNode
用于物理场等。并且 Xcode 目前不提供为 [=13= 中的节点设置自定义 classes 的选项] 文件。
(唯一的例外是更改场景本身的 运行 时间 class — .sks
文件中所有内容的顶级容器。那只是因为项目模板中为您提供的自定义 SKNode.unarchiveFromFile
实现。它在加载时更改 classes 的技术适用于您在存档中只有一个特定 class 实例的情况 —适合 SKScene
,不适合场景中的许多节点。)
铸造的工作原理
当你写这样的东西时:
myObj = childNodeWithName("block1") as Block
你是在告诉编译器:"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."(编译器说,"Okay, whatever.")
但是后来在运行的时候,你得到的那个东西最好真的是一个Block
,或者你的应用程序会崩溃,因为你试图用 Block
之外的东西做一些 Block
y。而且,根据上面关于 .sks
加载的一点,那东西不是也不能是 Block
— Xcode 不知道如何将 Block
放入一个 .sks
文件。所以你无法从中得到 Block
,所以你的应用肯定会崩溃。
解决方法
那么,如果您不能将自定义 classes 放入 .sks
文件中,那么 可以 做什么?这在一定程度上取决于您要完成的目标。但是有一个技巧可能也适用于一般的 game/app 设计:使用 .sks
文件进行一般布局和配置,然后使用第二遍来引入需要自定义行为的东西。
例如,如果您在 2D 平台游戏中构建关卡,您不会真的希望 .sks
文件包含您的 Plumber
class 即使你可以 - 那 class 可能有很多关于这个人有多高或多胖,他跳多高,他的胡子形状等的细节,你不想设置这些每次你创建一个新的关卡时都会重新启动,更不用说将它们再次保存在每个关卡的 .sks
文件中了。相反,在每个关卡文件中你真正需要知道的唯一一件事就是他的起始位置。因此,在 Xcode 中拖出一个 "Empty Node",并在加载时将该节点替换为 Plumber
class 的实例,如下所示:
let spawnPoint = childNodeWithName("spawnPoint")
let player = Plumber()
player.position = spawnPoint.position
addChild(player)
spawnPoint.removeFromParent()
如果您想在 .sks
文件中设置更多配置详细信息,您可以考虑自动执行该过程。
- 制作一个执行上述节点交换技巧的方法。 (将其命名为
replaceNode(_:withNode:)
。)
- 为您的自定义 class 创建一个初始值设定项,它接受
SKNode
或 SKSpriteNode
,并设置它的所有继承属性(或者至少是您关心的属性,例如颜色和纹理)来自该节点。
使用enumerateChildNodesWithName:usingBlock:
with a search pattern查找场景中具有某种名称的所有节点,并将它们替换为使用初始化程序创建的新节点。类似于:
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))
}
我正在使用 SpriteKit .sks 文件我可以将 .sks 中的精灵制作成 SKSpriteNode 的子类实例吗?
这是我子类中的初始化方法:
init(imageNamed: String) {
let blockTexture = SKTexture(imageNamed: imageNamed)
super.init(texture: blockTexture, color: nil, size: blockTexture.size())
}
在 GameScene.swift 中,我可以创建这样的实例:
var myObj = Block(imageNamed: "Block")
我的问题是如何将此实例与 .sks 文件相关联?
我试过这行代码,但它不起作用。
myObj = childNodeWithName("block1") as Block
有什么帮助吗?
谢谢。
这里有几个问题需要解决...
.sks
加载如何工作
当您加载 .sks
文件时,SpriteKit 会使用 NSKeyedUnarchiver
实例化所有内容。这意味着里面的所有节点都被加载为任何基础 classes 它们在创建 .sks
文件时由 Xcode 指定为 - SKSpriteNode
对于具有纹理艺术的精灵, SKLabelNode
用于文本,SKFieldNode
用于物理场等。并且 Xcode 目前不提供为 [=13= 中的节点设置自定义 classes 的选项] 文件。
(唯一的例外是更改场景本身的 运行 时间 class — .sks
文件中所有内容的顶级容器。那只是因为项目模板中为您提供的自定义 SKNode.unarchiveFromFile
实现。它在加载时更改 classes 的技术适用于您在存档中只有一个特定 class 实例的情况 —适合 SKScene
,不适合场景中的许多节点。)
铸造的工作原理
当你写这样的东西时:
myObj = childNodeWithName("block1") as Block
你是在告诉编译器:"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."(编译器说,"Okay, whatever.")
但是后来在运行的时候,你得到的那个东西最好真的是一个Block
,或者你的应用程序会崩溃,因为你试图用 Block
之外的东西做一些 Block
y。而且,根据上面关于 .sks
加载的一点,那东西不是也不能是 Block
— Xcode 不知道如何将 Block
放入一个 .sks
文件。所以你无法从中得到 Block
,所以你的应用肯定会崩溃。
解决方法
那么,如果您不能将自定义 classes 放入 .sks
文件中,那么 可以 做什么?这在一定程度上取决于您要完成的目标。但是有一个技巧可能也适用于一般的 game/app 设计:使用 .sks
文件进行一般布局和配置,然后使用第二遍来引入需要自定义行为的东西。
例如,如果您在 2D 平台游戏中构建关卡,您不会真的希望 .sks
文件包含您的 Plumber
class 即使你可以 - 那 class 可能有很多关于这个人有多高或多胖,他跳多高,他的胡子形状等的细节,你不想设置这些每次你创建一个新的关卡时都会重新启动,更不用说将它们再次保存在每个关卡的 .sks
文件中了。相反,在每个关卡文件中你真正需要知道的唯一一件事就是他的起始位置。因此,在 Xcode 中拖出一个 "Empty Node",并在加载时将该节点替换为 Plumber
class 的实例,如下所示:
let spawnPoint = childNodeWithName("spawnPoint")
let player = Plumber()
player.position = spawnPoint.position
addChild(player)
spawnPoint.removeFromParent()
如果您想在 .sks
文件中设置更多配置详细信息,您可以考虑自动执行该过程。
- 制作一个执行上述节点交换技巧的方法。 (将其命名为
replaceNode(_:withNode:)
。) - 为您的自定义 class 创建一个初始值设定项,它接受
SKNode
或SKSpriteNode
,并设置它的所有继承属性(或者至少是您关心的属性,例如颜色和纹理)来自该节点。 使用
enumerateChildNodesWithName:usingBlock:
with a search pattern查找场景中具有某种名称的所有节点,并将它们替换为使用初始化程序创建的新节点。类似于: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)) }