Samuli Natri - Software Developer
menu

Unity ECS - Entity Component System

Simple example on how to use the new Entity Component System and Job System in Unity.

Final files

Bootstrap.cs

using Unity.Entities;
using Unity.Mathematics;
using Unity.Rendering;
using Unity.Transforms;
using UnityEngine;

public class Bootstrap : MonoBehaviour
{
    public Mesh PlayerMesh;
    public Material PlayerMaterial;    
    
    public Mesh BlockMesh;
    public Material BlockMaterial;

    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
    private void Start()
    {
        var entityManager = World.Active.GetOrCreateManager<EntityManager>();
        
        var playerArchetype = entityManager.CreateArchetype(
            typeof(TransformMatrix),
            typeof(Position),
            typeof(MeshInstanceRenderer),
            typeof(PlayerInput)
        );

        var player = entityManager.CreateEntity(playerArchetype);
        
        entityManager.SetSharedComponentData(player, new MeshInstanceRenderer
        {
            mesh = PlayerMesh,
            material = PlayerMaterial
        });        
        
        var blockArchetype = entityManager.CreateArchetype(
            typeof(TransformMatrix),
            typeof(Position),
            typeof(MeshInstanceRenderer),
            typeof(Block)
        );

        for (int x = -3; x < 2; x++)
        {
            for (int y = -3; y < 2; y++)
            {
                var block = entityManager.CreateEntity(blockArchetype);

                entityManager.SetSharedComponentData(block, new MeshInstanceRenderer
                {
                    mesh = BlockMesh,
                    material = BlockMaterial
                });

                entityManager.SetComponentData(block, new Position {Value = new float3(x, y, 0)});
            }
        }
    }
}

Components.cs

using Unity.Entities;

public struct PlayerInput : IComponentData
{
    public float Horizontal;
    public float Vertical;
}

public struct Block : IComponentData {}
public struct Fly : IComponentData {}

MovementSystem.cs

using Unity.Entities;
using UnityEngine;
using Unity.Transforms;

public class MovementSystem : ComponentSystem
{
    public struct PlayerGroup
    {
        public ComponentDataArray<Position> playerPos;
        public ComponentDataArray<PlayerInput> playerInput;
        public int Length;
    }
    
    [Inject] PlayerGroup playerGroup;
    
    protected override void OnUpdate()
    {
        for (int i = 0; i < playerGroup.Length; i++)
        {   
            var newPos = playerGroup.playerPos[i];
            newPos.Value.x += Input.GetAxis("Horizontal") * 5 * Time.deltaTime;
            newPos.Value.y += Input.GetAxis("Vertical") * 5 * Time.deltaTime;
            playerGroup.playerPos[i] = newPos;      
        }
    }
}

PlayerInputSystem.cs

using Unity.Entities;
using Unity.Jobs;
using UnityEngine;

public class PlayerInputSystem: JobComponentSystem
{
    public struct PlayerInputJob : IJobProcessComponentData<PlayerInput>
    {
        public bool Left;
        public bool Right;
        public bool Up;
        public bool Down;
        
        public bool UpLeft;
        public bool UpRight;
        public bool DownLeft;
        public bool DownRight;

        public void Execute(ref PlayerInput input)
        {
            input.Horizontal = Left || UpLeft || DownLeft ? -1f : Right || UpRight || DownRight? 1f : 0f;
            input.Vertical = Down || DownLeft || DownRight ? -1f : Up || UpRight || UpLeft ? 1f : 0f;        
        }
    }
    
    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        var job = new PlayerInputJob
        {      
            Left = Input.GetKeyDown(KeyCode.A),
            Right = Input.GetKeyDown(KeyCode.D),
            Up = Input.GetKeyDown(KeyCode.W),
            Down = Input.GetKeyDown(KeyCode.S),
            
            UpLeft = Input.GetKeyDown(KeyCode.Q),
            UpRight = Input.GetKeyDown(KeyCode.E),
            DownLeft = Input.GetKeyDown(KeyCode.Less),
            DownRight = Input.GetKeyDown(KeyCode.X),
        };
 
        return job.Schedule(this, 1, inputDeps);
    }
}

PlayerMovementSystem.cs

using Unity.Entities;
using Unity.Jobs;
using Unity.Transforms;
using UnityEngine;

public class PlayerMovementSystem: JobComponentSystem
{   
    public struct PlayerInputJob : IJobProcessComponentData<Position, PlayerInput>
    {  
        public void Execute(ref Position position, ref PlayerInput playerInput)
        {
            position.Value.x += playerInput.Horizontal;
            position.Value.y += playerInput.Vertical;
        }
    }
    
    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        var job = new PlayerInputJob{};

        return job.Schedule(this, 1, inputDeps);
    }
}

BlockDeleteSystem.cs

using Unity.Transforms;
using Unity.Entities;
using Unity.Mathematics;

public class DeleteBlocksSystem : ComponentSystem
{
    public struct PlayerGroup
    {
        public ComponentDataArray<Position> playerPos;
        public ComponentDataArray<PlayerInput> input;
        public int Length; 
    }

    public struct BlocksGroup
    {
        public ComponentDataArray<Block> block;
        public ComponentDataArray<Position> blockPos;
        public EntityArray entityArray;
        public int Length;
    }

    [Inject] PlayerGroup playerGroup;
    [Inject] BlocksGroup blocksGroup;

    protected override void OnUpdate()
    { 
        for (int i = 0; i < blocksGroup.Length; i++)
        {
            float dist = math.distance(playerGroup.playerPos[0].Value, blocksGroup.blockPos[i].Value);
            
            if ( dist < 1)
            {
                /*PostUpdateCommands.DestroyEntity(blocksGroup.entityArray[i]);*/
                PostUpdateCommands.AddComponent(blocksGroup.entityArray[i], new Fly());
            }
        }
    }
}

FlySystem.cs

using Unity.Entities;
using Unity.Jobs;
using Unity.Transforms;
using UnityEngine;
using Unity.Mathematics;

public class FlySystem : JobComponentSystem
{
    private struct FlyJob : IJobProcessComponentData<Position, Fly>
    {
        public float dt;
      /*  public bool FlyAway;*/
        
        public void Execute(ref Position position, ref Fly fly)
        {
    /*        if (FlyAway)
            {*/
                float xMove = Mathf.PerlinNoise(position.Value.x, position.Value.y) - 1.5f;
                float yMove = Mathf.PerlinNoise(position.Value.y, position.Value.x) + 0.5f;
                position.Value += dt * new float3(xMove, yMove, 0);
           /* }*/
        }
    }
    
    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        var job = new FlyJob
        {
            dt = Time.deltaTime,
            /*FlyAway = Input.GetKey(KeyCode.F),*/
        };
        
        return job.Schedule(this, 1, inputDeps);
    }
}