Hello, Ive done all research I can and have done all I can to optimise an enemy move system I can. With it I can get about 3 million enemies moving around at 20fps. I know this seems a bit ridiculous, but I want MORE!
Perhaps I'm incorrectly using Jobs, or I'm missing out on burst performance? In the profiler the time to move the 3 mil entities is split 50/50 between my EnemyMoveJob and a local transform job (which I believe I won't be able to help). Perhaps not queuing to the ECB in my job but instead figuring out a different way to do it?
Here are the scripts in question (execution order from top to bottom):
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
//stuff
EntityCommandBuffer ecb = new(Allocator.TempJob);
state.Dependency = new EnemyMoveJob
{
//stuff
}.ScheduleParallel(_enemyQuery, state.Dependency);
state.CompleteDependency();
ecb.Playback(state.EntityManager);
ecb.Dispose();
}
[BurstCompile]
public partial struct EnemyMoveJob : IJobEntity
{
[ReadOnly] public float DeltaTime;
[ReadOnly] public BlobAssetReference<EnemyTargetsBlob> Targets;
[ReadOnly] public float2 Offset;
[ReadOnly] public GameQuality Quality;
[ReadOnly] public int MaxTarget;
public EntityCommandBuffer.ParallelWriter ECB;
public void Execute([ChunkIndexInQuery] int chunkIndex, EnemyAspect enemy)
{
enemy.MoveTowardsNextTarget(DeltaTime, Targets, Offset, Quality);
if (enemy.Order >= MaxTarget)
{
ECB.SetComponentEnabled<EnemyDeleteData>(chunkIndex, enemy.EntityReference, true);
}
}
}
public void MoveTowardsNextTarget(float deltaTime, BlobAssetReference<EnemyTargetsBlob> Targets, float2 RandomFloat2, GameQuality Quality)
{
if (Order >= Targets.Value.Targets.Length) return;
if (TargetOffset.Equals(float3.zero))
{
_enemy.ValueRW.TargetOffset = new float3(RandomFloat2.x, 0, RandomFloat2.y);
_enemy.ValueRW.Distance -= (ushort) math.distance(TargetOffset + EnemyPosition, Targets.Value.Targets[0].Location);
}
float3 location = Targets.Value.Targets[Order].Location + TargetOffset;
float speedDelta = Speed * deltaTime * 2;
if (math.distance(location, _transform.ValueRO.Position) < speedDelta) _enemy.ValueRW.Order++; //go to different target
if (Order >= Targets.Value.Targets.Length) return; //reached final target
float3 direction = location - _transform.ValueRO.Position;
if (Quality == GameQuality.High)
_transform.ValueRW.Rotation =
math.slerp(EnemyRotation, quaternion.LookRotation(direction, _transform.ValueRO.Up()), speedDelta);
else if (Quality == GameQuality.Medium)
_transform.ValueRW.Rotation =
quaternion.LookRotation(direction, _transform.ValueRO.Up());
if (Quality == GameQuality.Low) _transform.ValueRW.Position += speedDelta / 2 * math.normalize(direction);
else _transform.ValueRW.Position += speedDelta / 2 * _transform.ValueRO.Forward();
_enemy.ValueRW.Distance++;
}
Sorry if the code is difficult to read, I've tried to simplify it as much as I could without removing relevant stuff. Any help would be much appreciated and I would love to learn new optimisations to apply to my other scripts (assuming that there are more remaining).
I understand that there is some performance on the table by minimising the amount of data in my Enemy Component, however most the the memory on an enemy entity is rendering/transform. In total I'm fitting about 50 enemies per chunk. I'll do some deep profiling at some point to see what is the most demanding part of moving an enemy, though usually it isn't the math part considering SIMD, and that's pretty much all I'm doing.
Am I doing something that is a big nono with stuff like this? Am I passing in too many variables into a method or something?