Well, the way you organize the data in your code is essentially your choice of preference. I might have an opinion, but it would be my own preferring. You will be fine either way, if you handle the data properly. Whether it is done by managers or not is not really an issue and either way is valid.
I can share my experience. After developing a number of projects, I came up with a concept of Sequences, that worked well for me so far. The principle is in categorizing the functionality under own areas of responsibility. Each area is then handled by a Manager, where each task within that area is called a sequence.
For example, you are making a basketball game, where you are throwing a ball into a basket. We can define following managers that handle the areas of interest:
Ball Manager - anything related to ball, like its flying trajectory, spawning, physics, changing model, textures, etc.
Basket Manager - anything related to basket, like physics and tracking where the ball hits the basket, tracking when a ball passes some area that gives a point, spawning the basket, etc
Input Manager - anything related to game input, like tracking input start, end, drag, mouse, keyboard, joystick, etc
General Manager - tracks the game state, like score, loading procedures, connections to backends, etc
UI Manager and so on…
All the communication between Managers and between a Manager and its Sequences are done via events. The General Manager would be the main one and know when the game is loaded. It also knows when other Managers have loaded and initiates the game. Once initiated all Managers do the stuff they are responsible for.
For example, on game init event from the General Manager
- Ball Manager would start a ball spawn Sequence, which finds the ball in the scene hierarchy, clones it and positions it in front of camera. Once done, it reports back to Ball Manager. Ball Manager knowing that the ball has spawned, starts the ball aiming Sequence, which based on input from Input Manager adjusts ball position.
- Input Manager would start screen input Sequence, which tracks screen touches for example
The key takeaway here is that each sequence has an explicit start and an end. This allows a Manager to run them sequentially (hence the name), one after another. Like with the ball spawning example, where the aiming sequence starts only after spawning sequence. Manager in this case doesn’t do anything, except managing the sequences and their order of execution. As such, in order for this framework to work properly, I defined 3 rules:
- Each Manager can only listen to another Manager or its own Sequences. Never on Sequences that do not belong to them.
- Each Sequence can listen on its siblings or its Manager. Never on other Managers or their Sequences. The only exception can be the input events from Input Manager, which is acceptable to be listened by other Sequences.
- Each Sequence must have an explicit start and an explicit end.
- Without following these rules, it will quickly become hard to maintain and know which Sequence called this function. When you are strict about who can talk to whom, then it is easier to debug later. For example, if the ball is flying awkwardly, it must be the flying Sequence having some issue and since nobody can talk to it except Ball Manager, you already half-solved the problem by isolating it.
- This one complements rule 1.
- This is important. A Sequence must not have a start without an end - only Managers have such characteristic. Most of them are very short lived and focusing on doing one specific thing. Again, the input sequence is an exception in the most of the smaller games, where you never end it and it always tracks the inputs. This quality guarantees that sequences can be run sequentially. It also allows to bind/unbind events, based on whether a sequence is currently active. For example, if the ball has not spawned, there is no need to listen on input events in the aiming sequence, which is currently inactive until the ball is spawned. This rule helps with the performance optimization as a bonus, since only currently required Sequences are active and doing their stuff.
As for your comment about read-only data, here each Sequence is responsible for handling its own data. For example, the aiming sequence could create position vector at initialization, and when it activates, use it by updating it based on input from Input Manager.
On bigger games, with additional tools like rStats, you can actually see which Sequences are active at the moment, their life times, performance measurements, etc: