EntityModule
class SoftKitty.EntityModule
EntityModule is an extension system for the Entity class.
It allows additional features and data to be attached to an Entity without modifying the core Entity code.
This design keeps the core system lightweight while allowing different packages (such as InventoryEngine, CombatCore, or other future systems) to extend entities independently.
Typical examples of modules include:
-
Inventory systems
-
Combat data
-
Skill trees
-
AI states
-
Dialogue data
-
Character customization data
Each system can provide its own module that stores data and implements its own logic and editor interface.
How It Works
An Entity contains a list of modules:
public List<EntityModuleWrapper> Modules = new List<EntityModuleWrapper>();
public class EntityModuleWrapper
{
public EntityModule GetModule(){...}
...
}
Each module derives from the abstract base class:
public abstract class EntityModule
The module receives a reference to its parent entity during initialization:
[SerializeReference]
protected Entity entity;
This allows modules to access or modify the entity when necessary.
Accessing Modules
Modules can be retrieved using their type.
Retrieve a module
InventoryModule inventory = entity.GetModule<InventoryModule>();
Check if a module exists
if(entity.HasModule<InventoryModule>())
{
// Inventory system available
}
This allows systems to safely interact with optional features.
Runtime Initialization
Modules can run initialization logic when the game starts:
public virtual void RuntimeInit()
{
}
This is typically used for:
-
setting runtime state
-
rebuilding references
-
preparing cached data
Providing Additional Attributes
Modules can contribute additional attributes to the entity.
Two helper methods are available:
public virtual float GetAttributeValue(int id)
public virtual float GetAttributeValue(string uid)
For example, an equipment module could provide bonus attributes such as:
-
strength
-
defense
-
critical chance
The core attribute system will query modules when calculating final attribute values.
Saving and Loading
Each module is responsible for saving and loading its own data.
Save module data
public abstract string ToJson();
This converts the module data into a json string.
Load module data
public abstract EntityModule FromJson(string _json);
This reconstructs the module's data from a json string.
Cloning Modules
Entities can be duplicated during gameplay. To support this, modules must implement a copy function:
public abstract EntityModule Copy();
This should return a new instance containing the same data.
Runtime save data
public abstract string CompressData();
Compress full data into a compact format. To ensure only minimal data is saved for the game.
public abstract string UncompressData(string _compactJson);
Uncompress data from a compact format. This should be the reversed conversion of CompressData();
Creating a Custom Module
To extend the Entity system, create a class that inherits from EntityModule.
Example::
public class InventoryModule : EntityModule
{
public List<Item> items = new List<Item>();
public override void RuntimeInit(){
for(int i=0;i<items.Count;i++)items[i].Init();
}
public override EntityModule Copy()
{
InventoryModule copy = new InventoryModule();
copy.items= new List<Item>();
for(int i=0;i<items.Count;i++)copy.items.Add(items[i].clone());
return copy;
}
public override string ToJson()
{
return JsonUtility.ToJson(this);
}
public override EntityModule FromJson(string _json)
{
return (InventoryModule)JsonUtility.FromJson(_json, typeof(InventoryModule));
}
public override string CompressData(){
return ItemDataFormatter.Zip(items);
}
public override string UncompressData(string _compactJson){
return ItemDataFormatter.Unzip(_compactJson);
}
...your own api
}
This module can now be attached to entities and accessed by other systems.
Extending the Entity Inspector
Modules can also extend the Entity editor inspector.
The system uses an inspector registry:
EntityInspectorRegistry.Register(...)
Custom inspector modules implement:
public interface IEntityInspectorModule
{
bool DrawInspector(Entity entity, EntityManagerObject target);
void DrawRuntimeInspector(EntityComponent target);
}
Example::
public class InventoryInspectorModule : IEntityInspectorModule
{
public bool DrawInspector(Entity entity, EntityManagerObject target)
{
// draw editor UI
for(var item in myTarget.mData.GetModule<InventoryModule>().items){
GUILayout.BeginHorizontal();
GUILayout.Space(20);
GUILayout.Box(item.icon, GUILayout.Width(20), GUILayout.Height(20));
GUILayout.TextField( item.name, GUILayout.Width(100));
...
GUILayout.EndHorizontal();
}
return GUI.changed;
}
public void DrawRuntimeInspector(EntityComponent target)
{
// draw runtime UI (readonly)
}
}
Then register it automatically:
[InitializeOnLoad]
public static class InventoryInspectorRegister
{
static InventoryInspectorRegister()
{
EntityInspectorRegistry.Register(new InventoryInspectorModule());
}
}
This allows external packages to extend the Entity editor without modifying the core inspector code.
Benefits of the Module System
The EntityModule system provides several advantages:
-
Modular architecture — systems remain independent
-
No circular dependencies between packages
-
Expandable entities without modifying the base class
-
Optional features that only exist when the corresponding package is installed
-
Custom inspector support for editor extensions
This architecture makes it easy to build complex gameplay systems while keeping the core framework stable and maintainable.
Check InventoryModule as an example reference.