-
Notifications
You must be signed in to change notification settings - Fork 0
Core.ObjectManager
The ObjectManager is the backbone of your engine's architecture. It functions as a Service Locator and Dependency Injection container, allowing different parts of the game (Input, Sound, Physics, AI) to communicate without being tightly coupled.
In a typical game engine, you might be tempted to make everything a "Singleton." However, Singletons are hard to reset and difficult to test. The ObjectManager solves this by:
-
Decoupling: A system (like a UI menu) doesn't need to know where the
ArcadeGamedata comes from; it just asks the manager for it. - Scope Management: By using different instances of the manager, you can isolate data.
-
Automatic Cleanup: The
Disposemethod ensures that when a manager is destroyed, all registered textures, sounds, and services are cleaned up correctly.
Your engine employs a hierarchical approach to data:
-
GlobalObjectManager(Static): Used for "Evergreen" services that must exist from the moment the app starts until it closes. Examples include theLoggingService,MQTTModule, andAssetManager. -
Scene Object Managers: Each Raylib scene typically maintains its own instance. When you transition from an
IntroSceneto aLevelScene, theIntroScene's manager is disposed of, clearing its specific UI buttons and local timers, while theGlobalObjectManagerkeeps the background music and input settings alive.
-
Register<T>(instance, tag): Maps a type and a unique string (tag) to an object. If you register a new object with the same type and tag, it overwrites the old one. -
RegisterOnce<T>(instance, tag): Only registers the object if the key doesn't exist yet. This is perfect for "lazy loading" or ensuring you don't accidentally overwrite a core system like the Logger.
Instead of just single objects, the manager can handle groups of objects.
-
RegisterList<T>/RegisterSet<T>: Pre-allocates a container in the registry. -
Add<T>(instance, tag): Finds a registered List or HashSet of that type and adds the instance to it. This is how you manage "Groups" (e.g., a list of all active enemies in a scene).
- These are the "Search" functions. They use the
(Type, Tag)tuple to perform a lightning-fast dictionary lookup. Because they use generics (<T>), they return correctly typed objects, eliminating the need for manual casting ((MyClass)obj).
This is the most complex part of the code. When Dispose() is called:
- It iterates through every object in the registry.
- If an object implements
IDisposable, it calls.Dispose()on it. - It handles Collections: If you have a
List<Texture>, it will iterate through the list and dispose of every texture inside. - It uses a
ReferenceEqualityComparerto ensure that if the same object is registered under two different tags, it is only disposed once to prevent crashes.
In your Program.cs, you register the global game configuration so any scene can access it:
// Registering the global game data
GlobalObjectManager.ObjectManager.Register(new ArcadeGame { Name = "CyberMaze" });
// Somewhere else in the code (e.g., a UI component)
var gameName = GlobalObjectManager.ObjectManager.Get<ArcadeGame>()?.Name;Imagine a game scene where you want to track multiple enemies:
Notice This is a bare bone example. For RayLib scenes with a BaseScene it will be done for you. For more detail check the documentation about Scenes / GameObjects in RayLib
public class BattleScene {
private ObjectManager _sceneManager = new ObjectManager();
public void Initialize() {
// Prepare a list for enemies
_sceneManager.RegisterList<Enemy>();
// Add enemies to the scene's list
_sceneManager.Add(new Enemy("Goblin"));
_sceneManager.Add(new Enemy("Orc"));
}
public void Update() {
// Retrieve and update all enemies
var enemies = _sceneManager.GetList<Enemy>();
enemies?.ForEach(e => e.Update());
}
public void Close() {
// This clears all enemies and disposes their textures automatically
_sceneManager.Dispose();
}
}If you have two different fonts (one for UI, one for World Space), you use tags to distinguish them:
// Registration
_manager.Register(fontMain, "UI_Font");
_manager.Register(fontSmall, "Tooltip_Font");
// Retrieval
var uiFont = _manager.Get<Font>("UI_Font");