ECS Advice

Hi Guys, Sorry I do not have any code but more of a clarification question regarding ECS.

I have seen a lot of tutorials on the subject but they all use various types of ways of using ECS either pure or hybrid.

I have used Monobehaviours before but this ECS is a bit confusing to me at the moment.

Q1 : If i want to create a Entity do I need to create an Entity Manager for each one or can I create one script which is a “Root” EnetityManager for all enrtities created?

Q2: Is it best to create an individual Entity script with a accompanying Component Script which passes to a ComponentSystem??
Example : BulletEntity, PlayerEntity, EnemyEntity, etc…

Q3:Instantiate or CreateEntity (After seeing multiple tutorials which use both, It kind of gets confusing)
I am assuming the Instantiate is if you are using Hybrid Renderer to Render Gameobjects and CreateEntity basically creates an Empty Entity from which you attach Components to via SetComponentData?

IMany thanks to any who reply

  1. You have to get the EntityManager from a world or system.
  2. Entity is a struct type that is already defined. You just define your IComponentData types.
  3. Instantiate clones an entity and removes the prefab tag if one exists. CreateEntity gives you an empty entity as you suspected, but you will have to use AddComponent API rather than SetComponent API.

Many thanks for the reply, Would you be able to post a example please?

So for example in EntityManager.cs I would have something like

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Entities;

public class _EntityManager : MonoBehaviour
{
    public EntityManager em;
    public void Start()
    {
         em = World.Active.EntityManager;
    }
    public EntityManager UseManager()
    {
        return em;
    }
}

BulletEntity.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Entities;
using Unity.Transforms;

public class BulletEntity : MonoBehaviour
{
    public void Start()
    {
        //This uses our already made EntityManager in the EntityManager script.
       //This creates an error.
        Entity Bullet = _EntityManager.UseManager().CreateEntity(
            typeof(Translation)
        );

        //This creates another entitymanager to use to create the bullet entity.
        //But we already have one in _EntityManager script??
        EntityManager entityManager = World.Active.EntityManager;
        Entity Bullet1 = entityManager.CreateEntity(typeof(Translation));
    }
}

PlayerEntity.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Entities;
using Unity.Transforms;

public class PlayerEntity : MonoBehaviour
{
    // Start is called before the first frame update
    public void Start()
    {
        //This uses our already made EntityManager in the EntityManager script.
        Entity Player = _EntityManager.UseManager().CreateEntity(
            typeof(Translation)
        );
        //This creates another entitymanager to use to create the bullet entity.
        //But we already have one in _EntityManager script??
        EntityManager entityManager = World.Active.EntityManager;
        Entity Player1 = entityManager.CreateEntity(typeof(Translation));
    }
}

RenderEntity.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Entities;
public struct RenderEntity : IComponentData
{
    public Mesh mesh;
}

SpawnEntitySystem.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Entities;


public class SpawnEntitySystem : ComponentSystem
{
        protected override void OnUpdate()
        {
            if(Input.GetMouseButtonDown(1))
            {  
                /* How do we spawn a bullet here ??
                using CreateEntity and then applying the mesh component?
                or
                using Instantiate with this script applied to a empty game object?
                */
            }
        }
}

Sorry for the long post, But I hope this clarifies what I am trying to explain and achieve?

Or am i going about this all wrong if so can you point me in right direction of a good tutorial please, I am currently using CodeMonkeys video,s on Youtube but he writes a lot of his script in 1 script which doesnt show the Entities, Components and Systems working in conjunction with each other like how do you access the data held RenderEntity in SpawnEntitySystem and vice versa.

I will update this post with my experience and progression for anyone else who might be struggling also as it may help others who are super new to this.

I really appreciate anyone who takes the time out to read and even clarify/modify this post to make sense. Many many thanks to you!

When I watch CodeMonkey tutorial he isnt using AddComponent at all, This is why I am finding this confusing at the moment as there seems to be a lot of different ways of doing things.

Do you only need to use AddComponent if the Entity script is not attached to an empty GameObject??

You need to use AddComponent or AddComponentData if the component type is not present on the Entity, otherwise you need to use SetComponentData to update the component value. You will get an error if you try to add a component twice, or if you try to update non-existent component.

Also, you do not need to create your own EntityManager. Each world has its own EntityManager. World.Active.EntityManager just return a reference to the EntityManager of the active world.

You should read the manual and look at the samples.

Here’s a simple system that spawns cubes from a prefab. Start a new project and install the Entities and Hybrid Renderer packages then put this in the assets folder. You don’t need to create any gameobjects / monobevhaiours.

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

public class CubeSpawner : ComponentSystem
{
    Entity prefabEntity;

    protected override void OnCreate()
    {       
        // Temporary gameobject just to generate a mesh and material
        var goCube = GameObject.CreatePrimitive(PrimitiveType.Cube);
        var cubeMesh = goCube.GetComponent<MeshFilter>().mesh;
        var cubeMat = goCube.GetComponent<MeshRenderer>().material;

        prefabEntity = EntityManager.CreateEntity();
        EntityManager.AddComponentData(prefabEntity, new Prefab());
        EntityManager.AddComponentData(prefabEntity, new Translation());
        EntityManager.AddComponentData(prefabEntity, new LocalToWorld());
        EntityManager.AddSharedComponentData(prefabEntity, new RenderMesh() { mesh = cubeMesh, material = cubeMat });

        GameObject.Destroy(goCube);
    }

    protected override void OnUpdate()
    {
        if (Input.anyKeyDown)
        {
            var spawnedEntity = EntityManager.Instantiate(prefabEntity);

            var randomPosition = new float3(
                    UnityEngine.Random.Range(-5f, 5f),
                    UnityEngine.Random.Range(-5f, 5f),
                    UnityEngine.Random.Range(-5f, 5f));

            EntityManager.SetComponentData(spawnedEntity, new Translation() { Value = randomPosition });
        }
    }
}

Many thanks for the replies and the links to the samples.

I will be sure to go over the Samples and the Manual, The example you made will be very useful.

How would you pass prefabEntity to the System in OnUpdate if it was in a seperate script?

 protected override void OnUpdate()
    {
        if (Input.anyKeyDown)
        {
            var spawnedEntity = EntityManager.Instantiate(prefabEntity);
            var randomPosition = new float3(
                    UnityEngine.Random.Range(-5f, 5f),
                    UnityEngine.Random.Range(-5f, 5f),
                    UnityEngine.Random.Range(-5f, 5f));
            EntityManager.SetComponentData(spawnedEntity, new Translation() { Value = randomPosition });
        }
    }

How would “var spawnedEntity = EntityManager.Instantiate(prefabEntity);” work if this was in a seperate script so you could pass any Entity to it for it to spawn?

Would you have to pass prefabEntity as an Entity object for it to receive the values held within prefabEntity?

Once again Many thanks!

Well you can pass Entity references around in the normal OO way, reference them in a monobehaviour, or another system, or whereever you want. However that is the OO way to do it, and it can be error prone as those Entities may be changed or destroyed at any point. ECS systems tend to avoid directly talking to one another. They can and it will work, but it’s a bit of an anti-pattern to tightly couple systems together.

You can watch this talk about converting your thinking from the old OO gameobject way of doing things to the ECS data oriented way of doing it.

And the sample used in this talk is here.

1 Like

Thanks for the link and explanation,

I will take a look at those samples and video, Just 1 final question with using ECS is it better to do

1 : Attach a Game Entity Object Component to a Gameobject and Instantiate that as normal using a Hybrid Renderer?

or

2 : Create a blank entity and then attach a MeshRenderer to display said entity.

Its only the “displaying” and assigning an entity which is causing me issues at the moment , Like for example if you create a blank entity how do you attach a mesh to it via code? or is it ok to do this via the inspector?

  1. Use ConvertToEntity & conversion workflow

I think you should take a look at the github samples BanJaxe mentioned, Unity are pushing the conversion workflow hard, 1. is deprecated and might be removed in the future, and 2. might be jumping the gun as far as starting out. A lot of these things will become clearer once you’ve had a chance to dissect and analyze them.

I can see how DOTS is really confusing, as there are a lot of videos all showing slightly different ways of it working, and even many of the unity ones seem to skip a few steps between what they start with and what they end up with. (Example the shooting one where he converts to dots for the bullets, most of the actual code for the bullet is skipped such as collisions, etc)

As a result it can be frustrating when you cant quite make it work. To those who have cracked it properly in the had (and in my head I get the logic) the “missing” peices are probably blindingly obvious, but for those of us who havent cracked it, there are these gaps that just dont quite mean stuff works.

The samples are great, but for example, I made a cube (yes, trying for the obligatory spinning cube), I made some IComponentData with a rotationspeed, and a job and a system… initially nothing happened, I swore, then, I used slightly different maths and the cube instead of rotating moved, as the default script uses the transform and a read only rotation… so despite the template for the job+data seemingly trying to rotate, it didnt… so hence I made a data, job and system, not a data/job and system, finally i got it rotating, now, it was supposed to be around math.up()… but it actually wasnt, well it was, but even though everything was centered on 0,0,0 the cube actually was in a spiral rather than spinning on the spot… For the life of me, I had no idea why… It had no physics on it, so, it couldnt be blamed on centrifugal force… The latest unity demos do look like its a lot more tied into the IDE and so might be much clearer when we get our hands on it all, but right now, it can feel a little like trying to hold onto smoke with your hands, you think you got it and then its gone…

1 Like

In classical MonoBehaviours, the learning curve is sort of like a smoothstep function. The steepness is at the intermediate level. With DOTS, the curve is more like a 1 - (1 - x)^2 curve, where the steepness is all at the beginning, but once you get it, it is pretty easy to transition into the advanced stuff.

The best thing you can do is post your code and alongside the issues you are facing so we can more easily identify where the missing pieces are and help you fill in the gap.

1 Like

Many thanks for the replies!,

I will be sure to try to do a small demo and use ECS and maybe it,ll become a lot clearer with the assistance of these forums :slight_smile:

I really appreciate all the help!

I recently found the ECS Space Shooter example which might prove helpful also!

I will keep you guys updated on the progress.

Ok well I am following the ECS Space Shooter thing by examining the code that they are using but still not having much luck with it as I get some errors regarding these lines.

I thought if I copied and pasted their example code I could comment each line out and try to figure exactly whats going on, I managed to get it to create a single entity (I can see in the Entity Debugger entitys being added) but again it,s the “visual” problem where I cannot see the entitys I am struggling with I think its the “Position” component

void AddShips(int amount)
{
//Create an array of Entities with the amount specified and the array is Temporary.
NativeArray<Entity> entities = new NativeArray<Entity>(amount, Allocator.Temp);

//This will create a single entity.
manager.Instantiate(enemyShipPrefab);

//Loop through all the entities and assign values to their position, Rotation and MoveSpeed components.
for (int i = 0; i < amount; i++)
{
float xVal = UnityEngine.Random.Range(leftBound, rightBound);
float zVal = UnityEngine.Random.Range(0f, 10f);

//Using the entity manager set the position component values to a new Float3().
/*################################
I do not see a manager.AddComponent anywhere so I assume that the Position, Rotation and MoveSpeed
components below are attached directly to the EnemyShipPrefab GameObject with Monobehaviour??
################################## */

manager.SetComponentData(entities[i], new Position { Value = new float3(xVal, 0f, topBound + zVal) });
manager.SetComponentData(entities[i], new Rotation { Value = new quaternion(0, 1, 0, 0) });
manager.SetComponentData(entities[i], new MoveSpeed { Value = enemySpeed });
}
//Dispose of the entities array as creation has been completed and it is no longer needed.
entities.Dispose();

//Increase count by the amount.
count += amount;

}

Error : The type or namespace name ‘NativeArray<>’ could not be found (are you missing a using directive or an assembly reference?) (CS0246) [Assembly-CSharp]

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Entities;
using UnityEngine.Jobs;

//Am I missing a Using for NativeArray or has it been Deprecated already ?

Many thanks for all the replys and help and I feel I am getting a step closer to understanding how this works. :slight_smile:

Try adding

using Unity.Collections;
1 Like

Thanks!, I didn,t realise NativeArray was in Unity.Collections and thought it was implemented from System.Collections.

Sorry if this is a really basic question but what exactly does this mean ??

public void AddComponent(Entity entity)

so if I want to add a Component to an entity I call

entitymanager.AddComponent (entity)?
or
entitymanager.AddComponent (Mycomponent, Myentity);

I am self taught in programming so spent a lot of time reading tutorials and forums and the such so sometimes a lot of the manuals are not really coherent for me.

1 Like

@Shabbalaka
there are multiple ways of adding components to entity.
You need of course some components on entity, to hold the data.
So EntityManager is one way. EntityCommandBuffer (ECB) is other.
You can also generate entities with group of components via archetype.
And finally via entity conversion workflow, from GameObject.

If I missed anything, someone please let me know please.

there are few ways.
Primarly, not

entitymanager.AddComponent (Mycomponent, Myentity);

but rather

em.AddComponentData ( entity, new MyComponent () { i = 0 } ) ;
em.AddComponent ( entity, typeof ( MyComponent ) ) ;
em.AddComponent <MyComponent> ( entity ) ;
  1. Where you add component with data.
  2. and 3. work same way, as far I am aware.

Many thanks for all the replies! I will post my progress :slight_smile:

public class ECS_MultipleTemplate : MonoBehaviour
{
 
    [SerializeField]
    public Mesh mesh;

    [SerializeField]
    public Material material;
 
      private EntityManager em;

      public int NumToCreate;

    public void Start()
    {

        em = World.Active.EntityManager;

        EntityArchetype ea = em.CreateArchetype(
            typeof (Translation),
            typeof (LocalToWorld),
            typeof (HealthComponent),
            typeof (RenderMesh)
      
        );
        NativeArray<Entity> entityArray = new NativeArray<Entity>(NumToCreate, Allocator.Temp);
        em.CreateEntity(ea, entityArray);
        for(int i = 0; i < entityArray.Length; i++)
        {
          
            Entity entity = entityArray[i];

            em.SetComponentData(entity, new HealthComponent { Health = Random.Range(0f,1000f)});
            em.SetSharedComponentData(entity, new RenderMesh {
              
                mesh = mesh,
                material = material

            });
            em.SetComponentData(entity, new Translation{Value = new Unity.Mathematics.float3(Random.Range(-10f,10f),9f,0f) });
        }

        //Clean up the array as we do not need to keep it in memeory anymore.
        entityArray.Dispose();
    }
}

Righto, I have made some progress with ECS but now encountered another hurdle which I am not sure that I can use

Is it possible to use Coroutines in ECS??

I have created a ECS script to just move some objects down the screen but as expected they are all on top of each other and spawn in a fixed line instead of spawning all at the same time??

I designed this as a “Template” style srcipt that I can always refer to when i need a refresher.