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;
}
}
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.
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:
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?
The WithStructuralChanges is required when you use the EntityManager to modify entity data. Alternatively, you can also use an EntityCommandBuffer instead.
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).
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.
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.
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?
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:
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.
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.