So my game has several managers, a turn manager, cutscene manager, game state manager, as well as several other GameObjects(ie . the camera) that I would like a majority of the other objects in the scene to have references to (ie. an enemy sends a signal to the camera to shake during one of its attacks). I dont want to rely on GameObject.Find as that would be a bad habit for future projects, so I was wondering what the correct approach would be? Making the managers and related GameObjects static? Or passing references to them around in a group? How would I achieve the latter?
The singleton pattern is a good way to handle managers, but only for the handful of managers that really absolutely need to be globally accessible or else hurt efficiency. Essentially, the script keeps a static reference to its own instance, which it assigns in OnAwake if it’s null and then any subsequent attempt to assign it (from creating multiples of the same manager) will see the reference exists and destroy itself. That way, you maintain a single unique controller that can be accessed statically from anywhere, but still work as a component attached to a “Manager” GameObject and get all of the benefits from inspector-assigned values and references (MonoBehaviours can’t be static classes, so this is the “workaround”).
You should take it easy on singletons though- if a manager doesn’t really need to be accessed by anything outside of a single hierarchical family in your project (like a canvas manager or a manager for a squad of enemies), there’s no need for it to a singleton, and in fact if there’s any sort of a possibility at all of there being a need for a second copy of it, it CAN’T be. You should look into UnityEvents and setting up event broadcasts to relevant listeners as opposed to maintaining references to other scripts.
I’ve read about singletons, and with it all the bad wrap it gets as a cheap, yet inefficient workaround. I keep on forgetting about Unity Events too.
To clarify, say that I have a Cutscene Manager that handles the camera work and cutscenes. Since cutscenes can be triggered by a variety of factors like the environment and the spawned opponents, I should use a singleton?
And in the case of a turn manager that just manages the order and attacking status of enemies and tells the player when the fight is over, would that be better using Unity Events?
I tend to prefer pure statics over singletons. But I jump back and forwards.
If you need more flexibility, you can build a service manager. So any object that needs a service can request it from the manager. You can further abstract things by handing out interfaces to your services, instead of the concrete instance. This gives you a high degree of flexibility in how you implement the services.
That sounds reasonable:
Your CutScenes will always end up be a static list of finite number of cutScenes. If you have a singleton manager that knows the entire list and what to do wtih one, Any other object can just call your CutSceneManager Singleton with a CutScene ID and tell it to start playing it.
The TurnManager could go either way. If your game were realtime with enemies and other objects running around and acting asynchronously. A Event system would be ideal. Players could trigger IamNearYou events, enemies could Trigger IHitYou Events, etc.
Though with an actual turn based game it might not make much sense to use events. If everything is always happening in Sync. having a Manager with the current list of enemies and players that sorts them, then calls their relevant attack / take damage functions seems reasonable.
I’m quite interested in these alternatives. Do you have specific resources you can share that delve more deeply into this subject that would apply well to Unity’s unique programming environment?
Lately, I’ve been looking for ways to decrease coupling in my programs- I don’t have any deep-seeded hatred for coupling, but learning C# entirely from the perspective of developing in Unity has made me feel rather crippled as a programmer in some ways and I’m trying to broaden my “range”, so to speak.
I’m a big fan of registering listeners to events and broadcasting changes to interested parties as a means of communicating between two or more scripts, but while this cuts down on what scripts need to know about each other considerably, the receiver still needs to register to the event, and this means a direct instance reference or a static/singleton reference that I can only seemingly abstract by defining the event itself in a base class (since interfaces can’t define static members), and then changing all references to be BaseType.EventName.AddListener() and this feels sort of… hack-y… and possibly not even the intended behavior for static members when inherited.
I feel you there. I’m in the same boat.
I don’t have any specific resources. Most of the stuff I know I’ve picked up from the forums.