Home

Awesome

EgoCS

An Entity (GameObject) Component System framework for Unity3D, in C#.

Join the chat at https://gitter.im/andoowhy/EgoCS

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<Transform, Movement>
>{
	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<Rigidbody>
>{
	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<Rigidbody>( cubeGameObject );
		Ego.AddComponent<OnCollisionEnterComponent>( 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<Rigidbody>
>{
    public override void Start()
    {
        // Register Event Handlers
        EgoEvents<ExampleEvent>.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.

Installation

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 Manager or EgoCS).

Attach an EgoInterface Component 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();
    }
}

Debugging

Like with GameObjects and MonoBehaviours, you can easily enable & disable Systems on-the-fly before and during runtime:

Easily Enable / Disable Systems

Limitations

TODO