Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to write a protocol that will run a function at specific time with - swift - SpriteKit

Is it possible to create a protocol like this:

protocol SetupProt {
    func setup()
}

then at the end of my didMove function, tell the app to run all the setup functions from all the subclasses that implement this protocol?

Or will I need to store references to all these objects that are implementing the protocol and iterate through them?

Currently I have that protocol in use right now.

Then I have a global array like this:

setups = [SetupProt] = []

I have subclass for "a platform" in my game. I subclass it in the XCODE editor. And within the aDecoder init function, I add that node to this global array...

I do this because the scene property is nil at this moment, so I can't access it yet, it hasn't finished loading I"m guessing.

At the end of my scenes didMove, I iterate through this array:

for set in setups { set.setup() }

And this gets the job done.

I'm wondering if instead of storing an array of all these objects, can I just tell the app: "Hey, run the setup function for anything that is implementing this protocol.

like image 963
Discoveringmypath Avatar asked Feb 03 '26 22:02

Discoveringmypath


1 Answers

You are doing this in GameplayKit, so what you want to do is go through your components

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

This will avoid having to attach notification listeners to every item that uses this protocol. Setup is usually a one and done type deal, so to have your instances "listen" for a message that is sent only once could end up being a waste of resources.

What is nice with gameplay kit is technically you do not even need the protocol, you could have a component that is designed for setup, then we can avoid the needless filtering,casting, and looping.

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

--Inside SetupComponent

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

You can then take things one step further, and create "buckets" for all your components. This will help you manage things a lot better.

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.

You could add things like a setup bucket, update bucket, etc, and all these are are arrays of each type of component. This way if you ever need to call a method on all entities of a given component type, you have the bucket you can run through. Of course you are going to have to properly manage the buckets, and make sure you add/remove when a component is attached/removed to/from an entity.

Now I seem to recall coming across an issue with components in iOS 10 where I had to make sub component classes because subclassing was not properly working, but I do not know the state of it in iOS 11. I can tell you that Scenekit builder components are broken in XCode 9, so I would avoid using this if you can for now (You are on Sprite Kit, I do not know if they broke it on Sprite Kit also)

like image 105
Knight0fDragon Avatar answered Feb 05 '26 11:02

Knight0fDragon