是否可以创建这样的协议:
protocol SetupProt {
func setup()
}
然后在我的didMove函数结束时,告诉应用程序运行实现此协议的所有子类的所有设置函数?
或者我是否需要存储对实现协议的所有这些对象的引用并进行迭代?
目前我正在使用该协议。
然后我有一个像这样的全局数组:
setups = [SetupProt] = []
我有"一个平台"的子类。在我的游戏中。我在XCODE编辑器中将其子类化。在aDecoder init函数中,我将该节点添加到此全局数组...
我这样做是因为此时场景属性为零,所以我无法访问它,它还没有完成加载我猜测。
在我的场景结束时移动,我遍历这个数组:
for set in setups { set.setup() }
这就完成了工作。
我想知道如果不是存储所有这些对象的数组,我可以告诉应用程序:"嘿,为实现此协议的任何内容运行setup函数。
答案 0 :(得分:2)
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
函数运行。只需确保在从内存中删除对象时删除观察者小心僵尸
答案 1 :(得分:2)
你在GameplayKit中这样做,所以你要做的就是浏览你的组件
entities.foreach{
($0.components.filter({$0 as? SetupProtocol != nil}) as? [SetupProtocol]).foreach{
$0.setup()
}
}
这将避免必须将通知侦听器附加到使用此协议的每个项目。安装通常是一个完成类型的交易,因此让您的实例“监听”仅发送一次的消息可能最终浪费资源。
游戏套件的优点在技术上你甚至不需要协议,你可以有一个专为设置而设计的组件,然后我们可以避免不必要的过滤,转换和循环。
entities.foreach{$0.component(ofType:SetupComponent).setup()}
- 内部SetupComponent
class SetupComponent : GKComponent
{
func setup()
{
guard let entity = entity as? SetupProtocol else {return}
entity.setup()
}
}
然后您可以更进一步,为所有组件创建“存储桶”。这将有助于您更好地管理事物。
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.
您可以添加设置存储桶,更新存储桶等内容,所有这些都是每种类型组件的数组。这样,如果您需要在给定组件类型的所有实体上调用方法,则可以使用可以运行的存储桶。当然,您必须正确管理存储桶,并确保在组件与实体连接/移除时添加/删除。
现在我好像回想起iOS 10中我必须制作子组件类的组件问题,因为子类化不能正常工作,但我不知道它在iOS 11中的状态。我可以告诉你在XCode 9中破坏了Scenekit构建器组件,所以如果你现在可以避免使用它(你在Sprite Kit上,我不知道他们是否也在Sprite Kit上破坏了它)
答案 2 :(得分:1)
如果您的节点都是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)
}
}
}
答案 3 :(得分:0)
我认为协议扩展适合你试试这个。我将举例说明如何使用协议扩展定义可选协议。
// 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
答案 4 :(得分:0)
如果您希望某个类的多个实例(例如某个节点)响应单个事件,最好的方法是使用通知和协议的混合。
首先,您需要像以前一样定义协议:
protocol SetupProt {
func setup()
}
然后,创建一个实现协议的自定义节点类,并在init
内,将节点作为观察者添加到将由场景发送的通知中:
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
)
}
}
通过这样做,您可以同时设置所有节点,而无需在场景中直接引用它们。它还避免使用迭代器。
您可以阅读更多关于通知和观察者如何工作的here。