snap a grid with mathematics?

First of all I apologize for the trivial example, it’s a beginning.

What I am trying to do here is snap to grip position and this doesn’t have this effect.
In mono if I do x=(int)x it will do that, what’s the right way to do this in ECS/mathematics?

Also is OnStartRunning the right way to init the game? Here I want to snap all parts to the grid when the game start, this allows me prototype quickly.

thanks.

using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
using static Unity.Mathematics.math;

public class GridSystem : JobComponentSystem
{
    [BurstCompile]
      protected override void OnStartRunning()
    {
        base.OnStartRunning();
        Entities.ForEach((Translation translation,GridPosition gridPosition) =>
        {
            translation.Value.x = (int)translation.Value.x;
            translation.Value.y = (int)translation.Value.y;
            translation.Value.z = (int)translation.Value.z;
            gridPosition.value = translation.Value;
        }).Run();
    }
    protected override JobHandle OnUpdate(JobHandle inputDependencies)
    {
        var job = Entities.ForEach((Translation translation, GridPosition gridPosition) =>
        {
            translation.Value.x = (int)translation.Value.x;
            translation.Value.y = (int)translation.Value.y;
            translation.Value.z = (int)translation.Value.z;
            gridPosition.value = translation.Value;
        }).Schedule(inputDependencies);
      
        return job;
    }
}

You need to use ref if you want to change the value.

public struct GridPosition : IComponentData {
    public int3 Value;
}

[UpdateBefore(typeof(TransformSystemGroup))]
public class GridSystem : JobComponentSystem {
    protected override JobHandle OnUpdate(JobHandle inputDeps) {
        inputDeps = Entities
            .WithNone<Parent>()
            .ForEach((ref GridPosition gridPosition, ref Translation translation) => {
                gridPosition.Value = (int3)translation.Value;
                translation.Value = gridPosition.Value;
            })
            .Schedule(inputDeps);
       
        return inputDeps;
    }
}

Nop, there is no “OnStart” on systems. The closest thing is OnCreate but will not work for what you want. OnStartRunning is triggered every time a system will update but didn’t update the last time. For this case the OnUpdate will be fine. If you are having any trouble with lagging a frame, it’s because your system is probably missing the UpdateBeforeAttribute.

[ ]'s

1 Like

Now that I added ref OnStartRunning, OnCreate doesn’t.

Why is it accepting that I change the translation.Value if I didn’t add the ref?

OnStartRunning execute inside Update. It’s just a additional call if the system didn’t run last time. There is no need to add the same code as the OnUpdate.

Obs.: Update is different from OnUpdate. Update is what it’s called every loop. OnUpdate is what it’s invoked if the system should run (match any or all required queries, have always update attribute, etc).

It’s something like this:

class ComponentSystem {
    bool m_DidRunLastTime;
  
    public void Update() {
        var shouldRun = ShouldRunSystem();
      
        if (shouldRun) {
            if (!m_DidRunLastTime) {
                OnStartRunning();
            }
          
            OnUpdate();
        } else if (m_DidRunLastTime) {
            OnStopRunning();
        }
      
        m_DidRunLastTime = shouldRun;
    }
}

It’s a struct. It’s just the normal C# behaviour. There is nothing blocking you to use as some variable to be changed and reused:

struct SomeEntityRef : IComponentData {
    public Entity Entity;
}

Entities
    .ForEach((Translation translation, ref SomeEntityRef someEntityRef) => {
        translation.Value /= 2.
        EntityManager.SetComponentData(someEntityRef.Entity, translation);
    })
    .WithStructuralChanges()
    .WithoutBurst()
    .Runt();

[ ]'s

1 Like

Can you give me a code sample of a system that launch a job that initializes things that are in your world at launch (present as converted gameobjects)?

And how do I get the entity index from within a Entities.Foreach?

And I see here https://docs.unity3d.com/Packages/com.unity.entities@0.4/manual/entity_iteration_job.html examples of code that don’t use the lambda, they seem to be doing the same as the more terse lambda expression, why not use lambda everywhere? Even in the case of command buffers?

And this works
code that works

    protected override void OnStartRunning()
    {
        base.OnStartRunning();
        int counter = 0;
        Entities.ForEach((ref Item item) =>
        {
            item.id = counter;
            counter++;
        }).Run();
    }

but this doesn’t even though I’m not reading item, just writing to it. why is that?
no bueno

    protected override void OnStartRunning()
    {
        base.OnStartRunning();
        int counter = 0;
        Entities.ForEach((out Item item) =>
        {
            item.id = counter;
            counter++;
        }).Run();
    }

what is a structural change? the doc doesn’t specify but says it runs on main thread and doesn’t use burst.

got it, I’ll just go along with the pattern

The WithStructuralChanges is required when you use the EntityManager to modify entity data. Alternatively, you can also use an EntityCommandBuffer instead.

The closest thing as an initialization system for the world that you can create will be this.

[UpdateInGroup(typeof(InitializationSystemGroup))]
public class InitSystem : JobComponentSystem {
    protected override JobHandle OnUpdate(JobHandle inputDeps) {
        Enabled = false;
      
        // Init Code
      
        return inputDeps;
    }
}

But your mindset should be more like “initialize unprocessed entities” instead of something like OnStart.
You can use tags for this like a system to query entities that don’t have Initialized tag, initialize this entities and add the tag. You can combine this with ISystemStateComponentData (and variants).

public class InitSometingSystem : JobComponentSystem {
    struct Initialized : ISystemStateComponentData { }
  
    EntityCommandBufferSystem m_CommandBufferSystem;
  
    protected override void OnCreate() {
        base.OnCreate();
      
        m_CommandBufferSystem = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    }
  
    protected override JobHandle OnUpdate(JobHandle inputDeps) {
        var commandBuffer = m_CommandBufferSystem.CreateCommandBuffer().ToConcurrent();
      
        // Entities created
        Entities
            .WithAll<Something>()
            .WithNone<Initialized>()
            .ForEach((Entity entity, int entityInQueryIndex) => {
                // init code
              
                commandBuffer.AddComponent<Initialized>(entityInQueryIndex, entity);
            })
            .Schedule(inputDeps);
      
        // Entities destroyed
        Entities
            .WithAll<Initialized>()
            .WithNone<Something>()
            .ForEach((Entity entity, int entityInQueryIndex) => {
                // clear code
              
                commandBuffer.RemoveComponent<Initialized>(entityInQueryIndex, entity);
            })
            .Schedule(inputDeps);
      
        m_CommandBufferSystem.AddJobHandleForProducer(inputDeps);
      
        return inputDeps;
    }
}

A destroyed entity with ISystemState variants will have all other components removed but be kept alive till all ISystemState variants be removed. IIRC batch operations will bypass this (like this EntityManager.DestroyEntity(EntityQuery)).
Also ISystemState variants wont be copied when using EntityManager.Instantiate.

Add this parameter int entityInQueryIndex.
You can see others special named parameters here:
https://docs.unity3d.com/Packages/com.unity.entities@0.4/manual/entities_job_foreach.html#special-named-parameters

Lambda was introduced recently and don’t cover all types of jobs. If you need to work directly with chunks you can’t use lambda.

You need a valid delegate to use lambda in C#.
For the ForEach, Unity generate a long list with various combinations that doesn’t include out.
You can look in LambdaForEachDescriptionConstructionMethods.cs to see all the lambdas generated for ForEach.
You can use a custom delegate if you need something, but you will need to explicit it. Also the IL Post Processor works with custom delegate, but doesn’t just work with everything.

[ ]'s

Thank you!!

I’m looking at the definition of foreach, what do VVVRI mean? My guess was R means read in, V means ref and I index but as I soon found out the index needs to be at the front var job = Entities.ForEach((int entityInQueryIndex, ref Translation translation, in GridPosition gridPosition) =>
… V stands for Value, R for ref and I for in and are grouped in that order.
I guess the cost of a ref = the cost of an out and that’s why it wasn’t added

So this query would run on each update.
By the way what’s the update frequency? and can it be controlled for some systems so we get slow update on heavy computation stuff?

Love it! but I’m used to the foreach pattern so I have no idea how to grab the number of instances to make and the gameobject to instantiate. what’s the secret sauce?

Not according to the doc, it’s when components get added or removed. Tags are empty components still? that’s a structural change hence command buffer, makes sense.
So commandbuffers are used when you want speed and WithStructuralChange when mainthread is enough.

I read somewhere that adding and removing tags is costly, what’s the truth on that?

And wow WithChangeFilter<T> is powerful!

Also out would force you to set a value even when it’s not necessary.

By default the auto bootstrap will create three ComponentSystemGroup: InitializationSystemGroup, SimulationSystemGroup, PresentationSystemGroup.
Currently the three will run every frame but InitializationSystemGroup on PlayerLoop.Initialization, SimulationSystemGroup on PlayerLoop.Update and finally PresentationSystemGroup on PlayerLoop.LateUpdate.
Any System without an UpdateInGroup attribute will be added to SimulationSystemGroup (even if it’s another ComponentSystemGroup that is not one of the three).
To control the frequency you can just add an early return inside the OnUpdate or create your own ComponentSystemGroup, disable it and control the update by yourself.

What do you mean by “the number of instances”? How many entities where captured in the query? If this is the case:

public class SomeSystem : JobComponentSystem {
    EntityQuery m_Query;
  
    protected override JobHandle OnUpdate(JobHandle inputDeps) {
        var entityCount = m_Query.CalculateEntityCount();
      
        inputDeps = Entities
            .WithStoreEntityQueryInField(ref m_Query)
            .ForEach((...) => {
                ...
            })
            .Schedule(inputDeps);
      
        return inputDeps;
    }
}

Structural change is anything that will cause a Entity to be created, deleted or moved. Adding any component (tag or not) or setting a ISharedComponentData will cause the entity to move.
Even batch operation than can “move” the chunk will cause a structural change.
CommandBuffers aren’t exclude for that. To apply the commands stored in a CommandBuffer you will need to call Playback using an EntityManager in the main thread.
So all structural changes (outside of ExclusiveEntityTransaction) must be applied on the main thread.

Adding/removing any component can be costly (compared with other things in ECS). Each time you add/remove a component the entity will be moved to another chunk. The larger the Entity the worse will performer.
But you have some tools to help with this.
There are methods to add multiples components at the same time, skipping moves between each add. Also adding tags using an EntityQuery is cheap, no Entity will be moved and only the chunk will change the archetype.
The archetype is the reason why queries are fast so some of the cost can be mitigate if the component will be highly queried.
But we are talking about hundreds/thousands (mobile or desktop) of entities being moved every frame. For a few things you shouldn’t care.
We also have batch operations to create and destroy multiple entities or chunks.

Just keep in mind the ChangeFilter works only by chunk and not single Entities. Any entity changed will cause the entire chunk to be processed again. Tracking changes per entity can be really costly.

[ ]'s

Good to know so that might not be that optimal, unless a chunk is a static size.

Yes it’s static size, 64k (hardcoded) to be more precised (to fit in the cache). Currently the 64k is used to store entities and the header of the chunk. This also means that a single entity can’t extrapolate that size (currently it’s hard coded).
The bigger the archetype, less entities you can have in the chunk. To correlate to this, Entities.ForEach, IJobForEach, etc all uses IJobChunk or the same logic underline and they work with a chunk per thread. So if you little small entities that only fill one chunk the data will not be processed in parallel in the same job. Others jobs will run in parallel just fine (including with the same chunk if they all are just reading the values or not conflicting with the writers). IMHO you shouldn’t care about this at all. Mostly of the time this look as a problem means you don’t have enough data to be a problem or you can rearrange the system order/remove sync points to better parallelization between jobs.

[ ]'s

gotcha thanks. chunk is in cache, iterating on one chunk must be blazing fast