Hello,
How can I change translation of one entity according to translation of another one in parallel.
Is seems to me that the problem now is that I mark ComponentDataFromEntity with Translations as readonly and after that it is changed in last line of job. Am I right ?
Thank you.
public class MoveToTargetSystem : SystemBase {
protected override void OnUpdate() {
var entityTranslations = GetComponentDataFromEntity<Translation>();
Entities
.WithReadOnly(entityTranslations)
.ForEach((Entity entity, int entityInQueryIndex, ref Translation translation, in Target target, in MoveData moveData) => {
var targetTranslation = entityTranslations[target.Entity];
var distance = math.distance(targetTranslation.Value, translation.Value);
if (distance <= 1f) {
return;
}
var direction = math.normalize(targetTranslation.Value - translation.Value);
translation.Value += direction * moveData.MoveSpeed * Time.DeltaTime; // this place
}).ScheduleParallel();
}
}
error DC0002: Entities.ForEach Lambda expression invokes 'get_Time' on a ComponentSystemBase which is a reference type. This is only allowed with .WithoutBurst() and .Run().```
Above the Entities.ForeEach, add
float dt = Time.DeltaTime;
Then replace Time.DeltaTime with dt inside your Entities.ForEach.
The reason you need to do this is because Time is a property, not a field, so for the lambda to capture it, it has to capture the class which is not supported in Burst.
3 Likes
Thank you it helps to compile, but in runtime I got this error:
InvalidOperationException: The writable NativeArray <>c__DisplayClass_OnUpdate_LambdaJob0.JobData._lambdaParameterValueProviders.forParameter_translation._type is the same NativeArray as <>c__DisplayClass_OnUpdate_LambdaJob0.JobData.entityTranslations, two NativeArrays may not be the same (aliasing).
You can’t use ComponentDataFromEntity and Translation as a ForEach argument at the same time.
1 Like
So now I am fully stuck, can you please give me a tip how I can change Translation and at the same time get readonly reference to target’s Translation?
So at the cost of having to use Schedule instead of ScheduleParallel, you can remove the Translation argument from the ForEach and use SystemBase’s GetComponent/SetComponent methods instead of ComponentDataFromEntity.
Another option would be to create a temporary NativeArray containing each Entity’s target Translation in one Entities.ForEach pass, and then writing the new translations in a second pass.
1 Like
Thank you for your help.
I have implement both variants, they both works, but second one (with parallel) is twice slower than first one - is it ok or I have done smth wrong?
public class MoveToTargetSystem : SystemBase {
protected override void OnUpdate() {
var deltaTime = Time.DeltaTime;
Entities
.ForEach((Entity entity, int entityInQueryIndex, in Target target, in MoveData moveData) => {
var translation = GetComponent<Translation>(entity);
var targetTranslation = GetComponent<Translation>(target.Entity);
var distance = math.distance(targetTranslation.Value, translation.Value);
if (distance <= 1f) {
return;
}
var direction = math.normalize(targetTranslation.Value - translation.Value);
translation.Value += direction * moveData.MoveSpeed * deltaTime;
SetComponent(entity, translation);
}).Schedule();
}
}
public class MoveToTargetSystem : SystemBase {
private EntityQuery _query;
protected override void OnCreate() {
_query = GetEntityQuery(
ComponentType.ReadOnly<Target>(),
ComponentType.ReadOnly<MoveData>());
}
protected override void OnUpdate() {
var deltaTime = Time.DeltaTime;
var entityTranslations = GetComponentDataFromEntity<Translation>();
var targetTranslations = new NativeArray<Translation>(_query.CalculateEntityCount(), Allocator.TempJob);
Entities
.WithReadOnly(entityTranslations)
.ForEach((Entity entity, int entityInQueryIndex, in Target target, in MoveData moveData) => {
targetTranslations[entityInQueryIndex] = entityTranslations[target.Entity];
}).ScheduleParallel();
Entities
.WithReadOnly(targetTranslations)
.WithDeallocateOnJobCompletion(targetTranslations)
.ForEach((Entity entity, int entityInQueryIndex, ref Translation translation, in Target target, in MoveData moveData) => {
var targetTranslation = targetTranslations[entityInQueryIndex];
var distance = math.distance(targetTranslation.Value, translation.Value);
if (distance <= 1f) {
return;
}
var direction = math.normalize(targetTranslation.Value - translation.Value);
translation.Value += direction * moveData.MoveSpeed * deltaTime;
}).ScheduleParallel();
}
}
Nothing wrong with code, other than some potential optimizations (which may or may not matter to Burst).
The first approach you don’t need int entityInQueryIndex in the arguments, and the second you could use WithStoreEntityQueryInField instead of manually recreating the EntityQuery. Also in the second approach you should remove the in MoveData moveData and replace it with WithAll() in the first ForEach. Same goes for Target in the second ForEach.
The other thing I would check is to make sure Safety Checks and Jobs Debugger are disabled when profiling as those can heavily impact results.
1 Like
Then how I can get index of my native array ?
You don’t have an array in that approach.