如何使用-SpriteKit编写一个在特定时间运行函数的协议



是否可以创建这样的协议:

protocol SetupProt {
func setup()
}

然后在我的didMove函数的末尾,告诉应用程序运行所有实现该协议的子类中的所有设置函数?

还是我需要存储对所有这些实现协议的对象的引用,并对它们进行迭代?

目前我正在使用该协议。

然后我有一个像这样的全局数组:

setups = [SetupProt] = []

我在我的游戏中有"一个平台"的子类。我在XCODE编辑器中将其子类化。在aDecoder-init函数中,我将该节点添加到这个全局数组中。。。

我这样做是因为场景属性目前为零,所以我还不能访问它,我猜它还没有完成加载。

在我的场景didMove结束时,我遍历这个数组:

for set in setups { set.setup() }

这就完成了任务。

我想知道,与其存储所有这些对象的数组,我是否可以告诉应用程序:"嘿,运行任何实现该协议的设置函数

您在GameplayKit中执行此操作,因此您要做的是检查组件

entities.foreach{
($0.components.filter({$0 as? SetupProtocol != nil}) as? [SetupProtocol]).foreach{
$0.setup()
}
}

这将避免必须将通知侦听器附加到使用此协议的每个项目。安装程序通常是一次性的,因此让实例"监听"只发送一次的消息可能会浪费资源。

游戏套件的好处是,从技术上讲,你甚至不需要协议,你可以有一个专为设置而设计的组件,这样我们就可以避免不必要的过滤、选角和循环。

entities.foreach{$0.component(ofType:SetupComponent).setup()}

--内部设置组件

class SetupComponent : GKComponent
{
func setup()
{ 
guard let entity = entity as? SetupProtocol else {return}
entity.setup()
}
}

然后,您可以更进一步,为所有组件创建"bucket"。这将帮助你更好地管理事情。

let setupBucket =  entities.flatmap{$0.components.filter({$0 as? SetupProtocol != nil})} as? [SetupProtocol]. //I may need to verify this syntax is correct when I get home, doing it from memory.

您可以添加一些东西,比如设置bucket、更新bucket等,所有这些都是每种类型组件的数组。这样,如果您需要在给定组件类型的所有实体上调用一个方法,那么您就有了可以运行的bucket。当然,您必须正确管理bucket,并确保在将组件附加到实体/从实体移除组件时添加/移除组件。

现在,我似乎记得在iOS 10中遇到过一个组件问题,因为子类化无法正常工作,我不得不创建子组件类,但我不知道它在iOS 11中的状态。我可以告诉你,Scenekit构建器组件在XCode 9中损坏了,所以如果可以的话,我会暂时避免使用它(你在使用精灵套件,我不知道他们是否也在精灵套件上损坏了它)

致我;听起来你最好使用通知/观察者。。。您有多个对象要对单个事件作出响应。在这些对象的init或viewDidLoad函数中添加:

NotificationCenter.default.addObserver(_ observer: Any, selector aSelector: Selector, name aName: NSNotification.Name?, object anObject: Any?)

然后在didMove(_:):结束时

NotificationCenter.default.post(name aName: NSNotification.Name, object anObject: Any?)

这将导致这些#selector函数运行。只需确保在从内存中删除对象时删除观察者小心僵尸

如果您的节点都是SKScene的子级,则可以从场景开始递归调用方法。

protocol SetupProt {
func setup()
}
class GameScene: SKScene, SetupProt {
override func didMove(to view: SKView) {
super.didMove(to: view)
// Perform setup
self.setup()
}
// MARK: - Private
func setup() {
self.setup(with: self)
}
func setup(with node: SKNode ) {
// Call you method
(node as? SetupProt)?.setup()
// Recursively call for every node in scene
for child in self.children where child is SetupProt {
self.setup(with: child)
}
}
}

我认为协议扩展可以为您尝试。我将给出一个关于如何使用协议扩展定义可选协议的示例。

// Protocol has empty default implementation of the following methods making them optional to implement:
// cancel()
protocol Cancelable {
/// default implementation is empty.
func cancel()
}
extension Cancelable {
func cancel() {}
}
class Plane: Cancelable {
//Since cancel() have default implementation, that is optional to class Plane
}
let plane = Plane()
plane.cancel()
// Print what you want

如果您希望某个类的多个实例(例如一个节点)响应单个事件,最好的方法是混合使用通知和协议。

首先,你需要像以前一样定义你的协议:

protocol SetupProt {
func setup()
}

然后,创建一个实现协议的自定义节点类,并在init中,将节点作为观察者添加到将由Scene:发送的通知中

class MyNode: SKNode, SetupProt {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
// Add the node as observer for future trigger setup notifications
NotificationCenter.default.addObserver(
self,
selector: #selector(setup),
name: NSNotification.Name("TriggerSetup"),
object: nil
)
}
// Protocol implementation
@objc func setup() {
// Implement your node setup here
}
}

最后,实现您的场景并触发didMove方法中的观察者:

class MyScene: GameScene {
override func didMove(to view: SKView) {
super.didMove(to: view)
// Once your Scene is in place, trigger the setup of all observers
NotificationCenter.default.post(
name: NSNotification.Name("TriggerSetup"),
object: nil
)
}
}

通过这样做,您可以同时设置所有节点,而无需在场景中直接引用它们。它还避免使用迭代器。

您可以在这里阅读更多关于通知和观察员如何工作的信息。

最新更新