EgoCS: An Entity (GameObject) Component System framework for Unity3D
An Entity (GameObject) Component System framework for Unity3D, in C#.
For more detailed info, please see the EgoCS Wiki.
EgoCS aims to improve upon Unity3D's GameObject / Component relationship by completely decoupling Data and Behaviour, typical in Unity3D Components.
While there isn't a standard Entity Component System (ECS) pattern or reference implementation, EgoCS follows the most popular conventions:
// Movement.cs using UnityEngine;[DisallowMultipleComponent] public class Movement : MonoBehaviour { public Vector3 velocity = new Vector3( 1.0f, 2.0f, 3.0f ); }
// MovementSystem.cs using UnityEngine;// MovementSystem updates any GameObject with a Transform & Movement Component public class MovementSystem : EgoSystem< EgoConstraint >{ public override void Start() { // Create a Cube GameObject var cubeEgoComponent = Ego.AddGameObject( GameObject.CreatePrimitive( PrimitiveType.Cube ) ); cubeEgoComponent.gameObject.name = "Cube"; cubeEgoComponent.transform.position = Vector3.zero;
// Add a Movement Component to the Cube Ego.AddComponent<movement>( cubeEgoComponent.gameObject ); } public override void Update() { // For each GameObject that fits the constraint... constraint.ForEachGameObject( ( egoComponent, transform, movement ) => { // ...move it by the velocity in its Movement Component transform.Translate( movement.velocity * Time.deltaTime ); } ); }
}
Following this convention literally, Systems are completely isolated from one another. To allow inter-system communication, EgoCS uses Events and a global Event Queue:
// ExampleSystem.cs using UnityEngine;public class ExampleSystem : EgoSystem< EgoConstraint >{ public override void Start() { // Create a falling cube var cubeEgoComponent = Ego.AddGameObject( GameObject.CreatePrimitive( PrimitiveType.Cube ) ); var cubeGameObject = cubeEgoComponent.gameObject; cubeGameObject.name = "Cube"; cubeGameObject.transform.position = new Vector3( 0f, 10f, 0f ); Ego.AddComponent( cubeGameObject ); Ego.AddComponent( cubeGameObject );
// Create a stationary floor var floorEgoComponent = Ego.AddGameObject( GameObject.CreatePrimitive( PrimitiveType.Cube ) ); var floorGameObject = floorEgoComponent.gameObject; floorGameObject.name = "Floor"; floorGameObject.transform.localScale = new Vector3( 10f, 1f, 10f ); Ego.AddComponent<rigidbody>( floorGameObject ).isKinematic = true; Ego.AddComponent<oncollisionentercomponent>( floorGameObject ); // Register Event Handlers EgoEvents<collisionenterevent>.AddHandler( Handle ); } void Handle( CollisionEnterEvent e ) { var name1 = e.egoComponent1.gameObject.name; var name2 = e.egoComponent2.gameObject.name; Debug.Log( name1 + " collided with " + name2 ); }
}
// ExampleSystem.cs using UnityEngine;public class ExampleEvent: EgoEvent { public readonly int num;
public ExampleEvent( int num ) { this.num = num; }
}
public class ExampleSystem : EgoSystem< EgoConstraint >{ public override void Start() { // Register Event Handlers EgoEvents.AddHandler( Handle );
var e = new ExampleEvent( 42 ); EgoEvents<exampleevent>.AddEvent( e ); } void Handle( ExampleEvent e ) { Debug.Log( e.num ); // 42 }
}
TL;DR: Changes in Data (Components) will not break logic, and changes in logic (Systems) will not break Data. Maximum decoupling is achieved, and you will never have to write
[RequireComponent(...)]*shudder* again.
Place the "EgoCS" folder anywhere in your project's Assets folder:
cd [project_dir]/Assets/git clone https://github.com/andoowhy/EgoCS.git EgoCS
Create an empty GameObject in the scene, and give it an appropriate name (Ex:
Game Manageror
EgoCS).
Attach an
EgoInterfaceComponent to this GameObject. This Component is the bridge between Unity3D and EgoCS.
Add your Systems to EgoCS in your
EgoInterface's static contructor:
// EgoInterface.cs using UnityEngine;public class EgoInterface : MonoBehaviour { static EgoInterface() { // Add Systems Here: EgoSystems.Add( new ExampleSystem(), new MovementSystem() ); }
void Start() { EgoSystems.Start(); } void Update() { EgoSystems.Update(); } void FixedUpdate() { EgoSystems.FixedUpdate(); }
}
Like with GameObjects and MonoBehaviours, you can easily enable & disable Systems on-the-fly before and during runtime: