using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using static Unity.Mathematics.math;
using Unity.Transforms;
using UnityEngine; //Math4.max
// JobComponentSystems can run on worker threads.
// However, creating and removing Entities can only be done on the main thread to prevent race conditions.
// The system uses an EntityCommandBuffer to defer tasks that can't be done inside the Job.
public class FindProfessionSystem : JobComponentSystem
{
// EndSimulationBarrier is used to create a command buffer which will then be played back when that barrier system executes.
EndSimulationEntityCommandBufferSystem m_EntityCommandBufferSystem;
EntityQuery Professions;
protected override void OnCreateManager()
{
// Cache the EndSimulationBarrier in a field, so we don't have to create it every frame
m_EntityCommandBufferSystem = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
Professions = GetEntityQuery(ComponentType.ReadOnly<ProfessionRequirements>());
}
[ExcludeComponent(typeof(RobotJob))]
struct FindProfessionJob : IJobForEachWithEntity<RobotPropertiesComponent>
{
public EntityCommandBuffer CommandBuffer;
[DeallocateOnJobCompletion] public NativeArray<ProfessionRequirements> ProfessionRequirements;
[DeallocateOnJobCompletion] public NativeArray<Entity> Professions;
[DeallocateOnJobCompletion] public NativeArray<float> BestUsePerTimes;
public void Execute(Entity entity, int index, [ReadOnly] ref RobotPropertiesComponent properties)
{
var bestUseTimeForThisEntity = 0f;
Entity bestProfession = Professions[0];
for (int i = 0; i < Professions.Length; i++)
{
var professionSkill = dot(ProfessionRequirements[i].Value, properties.Value);
float use = BestUsePerTimes[i] * (1 - 0.5f * professionSkill);
if (use > bestUseTimeForThisEntity)
{
bestUseTimeForThisEntity = use;
bestProfession = Professions[i];
}
}
CommandBuffer.AddComponent(entity, new RobotJob { Job = bestProfession });
}
}
public float goodUse(Entity good)
{
var good_data = EntityManager.GetComponentData<Good>(good);
return Mathf.Max(good_data.Nutrition, good_data.Taxes);
}
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
//Instead of performing structural changes directly, a Job can add a command to an EntityCommandBuffer to perform such changes on the main thread after the Job has finished.
//Command buffers allow you to perform any, potentially costly, calculations on a worker thread, while queuing up the actual insertions and deletions for later.
// Schedule the job that will add Instantiate commands to the EntityCommandBuffer.
var professions = Professions.ToEntityArray(Allocator.TempJob);
var professions_requirements = Professions.ToComponentDataArray<ProfessionRequirements>(Allocator.TempJob);
var dummy = new float[professions.Length]; //default contains only 0s
var bestUsePerTimeArray = new NativeArray<float>(dummy, Allocator.TempJob);
var goodProductionBuffers = GetBufferFromEntity<GoodProductionBufferElement>();
for (int i = 0; i < professions.Length; i++)
{
var professionEntity = professions[i];
var goodsBuffer = goodProductionBuffers[professionEntity];
float bestUsePerTime = 0;
for (int j = 0; j < goodsBuffer.Length; j++)
{
GoodProduction goodProduction = goodsBuffer[j];
var use = goodUse(goodProduction.Good);
bestUsePerTime = Mathf.Max(bestUsePerTime, use / goodProduction.Duration);
}
bestUsePerTimeArray[i] = bestUsePerTime;
}
var job = new FindProfessionJob
{
CommandBuffer = m_EntityCommandBufferSystem.CreateCommandBuffer(),
Professions = professions,
ProfessionRequirements = professions_requirements,
BestUsePerTimes = bestUsePerTimeArray,
}.ScheduleSingle(this, inputDeps);
// SpawnJob runs in parallel with no sync point until the barrier system executes.
// When the barrier system executes we want to complete the SpawnJob and then play back the commands (Creating the entities and placing them).
// We need to tell the barrier system which job it needs to complete before it can play back the commands.
m_EntityCommandBufferSystem.AddJobHandleForProducer(job);
return job;
}
}
This is my system that finds a profession for every robot that does not have a profession yet. Most of the time all the robots have a profession (something like cook etc.). But the system is still run instead of being inactive because of the logic in OnUpdate. Can I somehow rewrite the system s.t. it only runs when there are Robots without a Profession? Are there other ways to optimize the above code?