Hi All,
First off, I realised I had posted this in the main forum, and it really now seems to be specifically with the physics system firing immediately.
Original post: Component behaves oddly in setup if it has a parent entity
The problem I am having:
- The objects I care about can have 1 of 2 states. Infected/Not infected.
- Each object is spawned in a non infected state.
- Some numbers for an example - Out of 100 spawned objects, 1 is infected, all the others are not infected.
- Because at least one object is infected, a trigger system detects when the objects physics triggers touch and I spread the infection this way.
- This works fine, until I put a parent entity on each child.
- Now in the first frame, the trigger system fires as if every object is touching.
- If I leave the parent component but turn off the move system I do not get the same problem.
- From this I assume something about the move system must be causing the issue, but it leans towards something around the timing of transforms being set or something similar.
My ECS manager, this runs at the start to set up the scene. This is where I attach the parent object
[UpdateAfter(typeof(LocationEntity))]
public class HumansECSManager : MonoBehaviour
{
EntityManager manager;
public GameObject HumanPrefab;
public int EntitiesPerLocation = 100;
BlobAssetStore store;
// Start is called before the first frame update
void Start()
{
store = new BlobAssetStore();
manager = World.DefaultGameObjectInjectionWorld.EntityManager;
var settings = GameObjectConversionSettings.FromWorld(World.DefaultGameObjectInjectionWorld, store);
var human = GameObjectConversionUtility.ConvertGameObjectHierarchy(HumanPrefab, settings);
// lets grab all our locations now
var query = new EntityQueryDesc()
{
All = new ComponentType[]
{
ComponentType.ReadOnly<LocationIndex>(),
}
};
EntityQuery m_Group = manager.CreateEntityQuery(query);
var locations = m_Group.ToComponentDataArray<LocationIndex>(Allocator.TempJob);
var locationEntities = m_Group.ToEntityArray(Allocator.TempJob);
// now we can set up our entites
int totalLocations = locations.Length;
int patientZero = UnityEngine.Random.Range(0, totalLocations * EntitiesPerLocation);
for (int x = 0; x < totalLocations; x++)
{
// each location could be smaller or bigger
float locationSize = (100 - 1) / 2.0f;
for (int y = 0; y < EntitiesPerLocation; y++)
{
var instance = manager.Instantiate(human);
float startX = UnityEngine.Random.Range(-1 * locationSize, locationSize);
float startZ = UnityEngine.Random.Range(-1 * locationSize, locationSize);
float endX = UnityEngine.Random.Range(-1 * locationSize, locationSize);
float endZ = UnityEngine.Random.Range(-1 * locationSize, locationSize);
float speed = UnityEngine.Random.Range(2.0f, 4.0f);
float3 startPos = new Vector3(startX, 0, startZ);
float3 endPos = new Vector3(endX, 0, endZ);
Vector3 travelVector = endPos - startPos;
manager.SetComponentData(instance, new HumanMovement
{
Speed = speed,
TravelVector = math.normalize(travelVector) * speed
});
manager.SetComponentData(instance, new HumanLocationData
{
Location = locations[x].index
});
manager.SetComponentData(instance, new HumanPositioning
{
StartPos = startPos,
EndPos = endPos,
distance = math.distance(endPos, startPos)
});
bool infected = ((x * EntitiesPerLocation) + y == patientZero);
manager.SetComponentData(instance, new HumanInfection
{
isAsymptomatic = false,
isInfected = infected,
infectionAmount = infected ? 100.0f : 0.0f
});
manager.AddComponentData(instance, new Parent
{
Value = locationEntities[x]
});
float4x4 f4x4 = float4x4.identity;
manager.AddComponentData(instance, new LocalToParent() { Value = f4x4 });
manager.SetComponentData(instance, new Translation { Value = startPos });
}
}
locations.Dispose();
locationEntities.Dispose();
}
private void OnDestroy()
{
store.Dispose();
}
}
My move system
[UpdateInGroup(typeof(MainLocationDirectorGroup))]
public class HumanMoveSystem : SystemBase
{
protected override void OnUpdate()
{
float deltaTime = Time.DeltaTime;
var randomArray = World.GetExistingSystem<RandomSystem>().RandomArray;
float locationSize = 98 / 2.0f;
Entities
.WithName("HumanGetNewLocationSystem")
.WithAny<HumanTag>()
.WithNativeDisableParallelForRestriction(randomArray)
.WithBurst()
.ForEach((int nativeThreadIndex,
ref Translation position,
ref HumanMovement humanMovementData,
ref HumanLocationData humanLocation,
ref HumanPositioning humanPositioning) =>
{
position.Value += humanMovementData.TravelVector * deltaTime;
float distance = math.distance(humanPositioning.EndPos, position.Value);
humanPositioning.distance = distance;
var random = randomArray[nativeThreadIndex];
if (distance < 1f)
{
float change = random.NextFloat(0, 100);
if (change > 90.0f)
humanLocation.changeDestination = true;
else
{
humanPositioning.StartPos = humanPositioning.EndPos;
float distanceCheck;
do
{
float x = random.NextFloat(-1 * locationSize, locationSize);
float z = random.NextFloat(-1 * locationSize, locationSize);
humanPositioning.EndPos = new float3(x, 0, z);
distanceCheck = math.distance(humanPositioning.StartPos, humanPositioning.EndPos);
float3 travelVector = humanPositioning.EndPos - humanPositioning.StartPos;
humanMovementData.TravelVector = math.normalize(travelVector) * humanMovementData.Speed;
}
while (distanceCheck < locationSize / 2.0f);
}
}
randomArray[nativeThreadIndex] = random;
})
.ScheduleParallel();
}
}
Finally the trigger system
[UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
[UpdateAfter(typeof(EndFramePhysicsSystem))]
public class InfectionTriggerDetectionSystem : SystemBase
{
// we need a couple of extra worlds that have finished simulating
BuildPhysicsWorld physicsWorld;
StepPhysicsWorld stepWorld;
protected override void OnCreate()
{
physicsWorld = World.GetOrCreateSystem<BuildPhysicsWorld>();
stepWorld = World.GetOrCreateSystem<StepPhysicsWorld>();
}
[BurstCompile]
struct BoxTriggerEventJob : ITriggerEventsJob
{
public ComponentDataFromEntity<HumanInfection> InfectionGroup;
public float SpreadPercentageLevel;
public void Execute(TriggerEvent triggerEvent)
{
Entity entityA = triggerEvent.EntityA;
Entity entityB = triggerEvent.EntityB;
var componentA = InfectionGroup[entityA];
var componentB = InfectionGroup[entityB];
bool infectedA = componentA.isInfected && componentA.infectionAmount > 0;
bool infectedB = componentB.isInfected && componentB.infectionAmount > 0;
if (infectedA || infectedB)
{
componentA.isInfected = true;
if (componentB.infectionAmount > 0)
componentA.infectionAmount += componentB.infectionAmount * SpreadPercentageLevel;
componentA.infectionAmount = math.min(100.0f, componentA.infectionAmount);
componentB.isInfected = true;
if (componentA.infectionAmount > 0)
componentB.infectionAmount += componentA.infectionAmount * SpreadPercentageLevel;
componentB.infectionAmount = math.min(100.0f, componentB.infectionAmount);
InfectionGroup[entityA] = componentA;
InfectionGroup[entityB] = componentB;
}
}
}
protected override void OnUpdate()
{
Dependency = new BoxTriggerEventJob
{
InfectionGroup = GetComponentDataFromEntity<HumanInfection>(),
SpreadPercentageLevel = Config.INFECTION_PERCENTAGE
}.Schedule(stepWorld.Simulation,
ref physicsWorld.PhysicsWorld, Dependency);
}
}
For sanity - here is my player loop
I feel its to do with when systems run and I am unsure what to do next. Is there some extra thing I need to wait for because it has a parent object?
I have tried a number of things including ordering the trigger system before or after move, and it made no difference. I am stuck with the physics wanting to be updated in the Simulation group, yet the parent entity seems to create havoc with the physics.
Can anyone point me in the right direction or perhaps do you have any suggestions?