Follow us on git!
Hello everyone!
I want to do something good to all of you!
I like the new ECS approach of unitys tech-team! But I cant wait for it… So I’ve build my own one, that’s similar to Unity’s upcoming.
And because of that…
It’s purposeless to sell it on the store… So I decided to publish it for FREE!
(attachment)
Whats the benefit…
- Huge speedup in relation to the (legacy) Unity System
- Easy maintenance and extensibility
- System debugging like Entitas ECS
- Inversion of control (dependency injection)
- No Garbage Collecting (only if you do live debugging with components on gameobjects, but you can turn on and off in playtime)
- TupleInjection for multiple components in systems (only entities that matches all tuples)
- Easy to use components, supports structs and classes
- Easy to extend for multithreaded Systems (but not yet implemented)
- Fast system and component creation with templates
- Unity monobehaviour integration
- Supports data prefetching (fetches data before it is needed)
- Creating entities from Prefabs
- Supports different contextes for entity (just by creating new classes of RootSystems and EntityManagers)
- No Object pooling required
Here is an short example of usage:
Controller Executes all the systems
namespace ECS {
// Use this class to control the ECS System
public class GameController : ECSController<UnityStandardSystemRoot, EntityManager> {
// Use this for initialization
protected override void Initialize() {
AddSystem<MySystem>();
// or you can use this for more controll over compositions of systems
//AddSystem(new MySystem());
}
// Override this function if you want to controll what Scene Entity should be load in this context
// The base implementation will add all Scene Entities to the context
//protected override void AddSceneEntitiesToSystem() {
//}
}
}
It creates the system and the entity manager by dependency injection for you
With the AddSystem methods you can assign your systems to the ECSSystemRoot.
The ECSController also executes the Start, Update, and FixedUpdate Routines of the Systems
An ECS Component
using ECS;
using System;
namespace ECSExample {
[Serializable]
public struct FloatComponent : IComponent {
public float value;
public FloatComponent(float value) {
this.value = value;
}
}
public class FloatDataComponent : ComponentDataWrapper<FloatComponent> { }
}
Initialize Entity System
using ECS;
using ECS.VisualDebugging;
using UnityEngine;
namespace ECSExample {
[DebugSystemGroup("Init")]
class InitEntitiesSystem : ComponentSystem {
private GameObject _gameObject;
[InjectDependency]
private EntityManager entityManager;
public override void OnStart() {
_gameObject = new GameObject("Entities");
for (int i = 0; i < 1000; i++) {
Entity entity = entityManager.CreateEntity();
GameObject gameObject = new GameObject("Entity-" + i);
gameObject.transform.SetParent(_gameObject.transform);
GameObjectEntity goEntity = gameObject.AddComponent<GameObjectEntity>();
goEntity.SetEntity(entity, entityManager);
entityManager.AddComponent(entity, new FloatComponent(1f));
}
}
}
}
Update float component of all entities
using ECS;
namespace ECSExample {
[DebugSystemGroup("Update")]
class UpdateFloatSystem : ComponentSystem {
[InjectDependency]
private EntityManager entityManager;
[InjectTuple]
private ComponentArray<FloatComponent> floats;
public override void OnUpdate() {
float sum = 0;
for (int i = 0; i < floats.Length; i++) {
entityManager.SetComponent(floats.GetEntity(i), new FloatComponent(floats[i].value + 1));
}
}
}
}
Accessing Components in Systems
//This class see only Enities with ComponentA and B attached to it
class MySystem : ComponentSystem {
[InjectTuples]
ComponentArray<ComponentA> componentA;
[InjectTuples]
ComponentArray<ComponentB> componentB;
}
// if you want to manualy will filter components use the following:
ComponentGroup group = m_EntityManager.GetComponentGroup(typeof(ComponentA), typeof(ComponentB),...)
ComponentArray<ComponentA> compA = group.GetComponentArray<ComponentA>();
ComponentArray<ComponentA> compB = group.GetComponentArray<ComponentB>();
Instantiate from Prefab without instantiating the GameObject:
struct Position : IComponent {
public Vector3 position;
public Quaternion rotation;
}
PositionComponent : ComponentDataWrapper<Position>{ }
class Spawner : Monobehaviour {
public GameObject prefab;
[InjectDependency]
EntityManager _entityManager;
void Awake() {
InjectionManager.ResolveObject(this);
}
void Start() {
// Instantiate the prefab with all its components attached to it
Entity entity = _entityManager.Instantiate(prefab);
// just update the position component
var position = new Position(Vector3.zero, Quaternion.identity);
entityManager.SetComponent(entity, position);
}
}
Instantiate Entities from Prefab with instantiating the GameObject
public class PrefabSpawner : ScriptBehaviour {
public GameObject _prefab;
[InjectDependency]
private EntityManager _entityManager;
// Use this for initialization
void Start() {
GameObject gameObject = _entityManager.InstantiateWithGameObject(_prefab, transform.position, transform.rotation);
}
}
Components that supports Unity Component
[Serializable]
public class ECSTransform : IComponent, ICloneable {
public Transform transform;
public object Clone() {
return MemberwiseClone();
}
}
[HideInInspector] // dont show inner component in Inspector
public class ECSTransformComponent : ComponentWrapper<ECSTransform> {
// This will assign the Unity Component to the ECS Component
public override void Initialize() {
TypedComponent.transform = gameObject.transform;
}
}
Creating of different Contexts
//context for Cars
class CarEntityManager : EntityManager{}
class CarRootSystem : UnityRootSystem<CarEntityManager>{}
//context for Humans
class HumanEntityManager : EntityManager{}
class HumanRootSystem : UnityRootSystem< HumanEntityManager>{}
//usage
class Controllers : Monobehaviour {
CarRootSystem carSystem;
HumanRootSystem humanSystem;
void Awake() {
carSystem = new CarRootSystem ();
humanSystem= new HumanRootSystem ();
... add systems to the rootsystems
}
void Start() {
carSystem .Start();
humanSystem .Start();
}
void Update() {
carSystem .Update();
humanSystem .Update();
}
void FixedUpdate() {
carSystem .FixedUpdate();
humanSystem .FixedUpdate();
}
}
This will enforce that you will separate enitities, components and systems
Systems of context Car only knows entities of that system and so on.
If you want to communicate with the other Context use the EntityManager of that context
Its almost identical to the video of Joachim Ante from Unite Austin 2017
Great thanks to him and his team!!!
It took me a week of afternoons and a whole weekend to write it that performant!
So please enjoy it and fell free to use it.
It’s not fully completed yet and tested.
I also want to implement some basic components: like transform and so on.
Here are some pics for you:
Entitas like system debugging
Enitity Debugging live in GameObjects
Easy Setup for template script integration (be aware its global)
Template Scripts
The GC pike in the image comes of enable debug mode of an entity (for confusion)
IOC:
it works with any class you want.
Just call InjectionManager.Resolve() or if the object already exist use InjectionManager.ResolveDependency(my object)
A injectable class should have the [InjectableDependency] Attribute on it
And to inject some object to your Fields and Props use [InjectDependency] Attribute
The system will automatically recognize dependencies of constructors
Example
[InjectableDependency(LifeTime.PerInstance)]
class MyClass {
... some code
}
class SecondClass {
[InjectDependency]
private MyClass myClassInstance;
}
class ThirdClass {
public ThirdClass(MyClass myClassInstance) {
... some code ....
}
}
... somewhere in the programm
InjectionManager.Resolve<SecondClass>();
InjectionManager.Resolve<Thirdclass>();
// or
SecondClass mySecClassInstance = new SecondClass();
InjectionManager.ResolveDependency(mySecClassInstance);
If you want to use Dependency Injection in a Monobehaviour class,
just use ScriptBehaviour instead of Monobehaviour.
public class SomeMonoBehaviourClass: ScriptBehaviour {
[InjectDependency]
private EntityManager _entityManager;
}
If you have some questions, tips or hints, feel free to contact me!
Again, enjoy it!
Kind regards,
Spy-Shifty