controller/manager architecure

I am trying to create a controller/manager system for a Real-Time-Strategy game/framework (C#). though I am a little lost in the design phase of the control architecture.

  • I think that I will need some kind of manager/controller hierarchy where the manager holds references to each entity of interest in the game (units, and structures), or should I be focusing more on messaging? (I can never seem to completely understand this)
  • then each Unit will have some kind of link back to the manager so that it can send info back (or would this be done with just back propagation as a result of function returns?).
  • I am uncertain where modification to the entities should take place. units will have weapons, and possibly ability which will effect entities should the modifications to the entities that happen as a result be done in these scrips held by an individual entity (passing the references of all the entities to be effected), or should I somehow pass the script to the manager to do the modifications? (how would I even accomplish this second part??)
  • does this manager have to be completely singleton, or would static be enough?
  • then if I end up introducing networking (later iteration) do I introduce a UniversalManager that controls the others (to be held on the “server”), or just have each manager propagate to each other? (I may end up asking this separately later)

So that is a big set of questions :slight_smile:

Your manager should have references to the units under its control and they should have a reference to their manager and any other ancestry should be stored separately - in this way you can send messages (or rather call functions) in either direction. Normally units send report messages to managers and managers send action messages to units - though it depends a lot on your control system.

Generally it is a good idea to compartmentalise functionality in the unit rather than have the manager trying to do everything - that way you can write different units without changing the manager code and making it over complex (you keep the complexity isolated in the unit control code). The best way then is to use interfaces to define the communication protocol to the unit and you can always create standard base classes that implement these interfaces for most circumstances without tying yourself up in knots by trying to make everything have to work with inheritance - this means you can have special cases of unit that just support the messaging protocol without having to inherit everything while still giving you the ability to have standard implementations for most cases.

You should have the scripts contain their own modification values, strengths etc. That way you can have a unit that has a different number of scripts on it, all of which have their own data. If you need an abstracted concept like health, then you implement that as an interface to get the value back - meaning that for one unit this might be returning a float value between 0 and 1, and for another it might involve some calculation that yields a float between 0 and 1.

If you are doing networking it appears to me that you should have a static dictionary of ID > Manager that contains all of them from everywhere on the network. You should probably have a static “MyManager” type variable for your own. It depends on how you are going to do your network but the dictionary approach would allow you to address commands sent over the network in different ways (if just sending it to the viewID was not enough).

I’m not sure what you mean as a differentiation between singleton and static - static is just a way of creating singleton structures.

Hey Kevin,

A good place to start would be to take a system that has already been tried and tested and then slowly refactor it to suit your needs.

Here is a link to a manager system that has some very good ideas and works quite well.

To answer some of your other questions :

  1. Having one central class for each subsystem is a very good idea especially in games where a lot of entities are communicating with each other and managers;
  2. Messaging is great in certain cases and sucks in others. If you have a controller / manager system with references to most of the entities it would be better to avoid messaging;
  3. There is no need to return references as function results, public fields are just fine;
  4. Try to avoid using GetComponent… functions outside of initialisation functions such as Awake and Start. Try to cache references in variables as soon as possible and use those variables often;
  5. It’s always a good idea to encapsulate functionality of various sub systems into their own self sustaining classes. For instance all basic unit functionality can be written in class UnitBase. Then each type of unit that has unique characteristics can be given its own class that inherits UnitBase. This will make our code robust easy to maintain and extend. The same goes for weapons, shields, inventory etc.
  6. In general break things up into black boxes. Each box (class) should exist on its own with as little knowledge about the outside systems as possible. Your weapon class should know how many rounds of ammo it has left by itself, it does however need the calling code to tell it to shoot and so on.
  7. Singleton uses a static variable inside to keep the reference to itself created during the first call to its ‘Instance’ property. So the argument singleton vs static is kinda pointless. Have a look here for a good explanation of the pattern. Implementing Singleton in C# | Microsoft Learn;
  8. Networking is a beast and has its own demons. A good idea would be to use a ready made 3rd party framework like Photon or something similar. If you are determined to try on your own then you would need a Global manager system to keep two or more manager systems synced across game instances. If you are going for a large scale MP then the task will be a far more daunting and technically complicated;