[BurstCompile]
[WithAll(typeof(UtilityBehaviourState))]
public partial struct UtilityBehaviourJob : IJobEntity
{
[ReadOnly]
public CollisionWorld CollisionWorld;
public float DeltaTime;
private unsafe void AvoidObstacles(Entity self, RefRW<UtilityBehaviourState> state, RefRW<GameplayCharacterInputs> inputs, RefRO<LocalTransform> transform, RefRO<PhysicsCollider> physicsCollider)
{
if (state.ValueRO.Target.HasValue)
{
float3 directionToTarget = math.normalizesafe(state.ValueRO.Target.Value - transform.ValueRO.Position);
CapsuleCollider* capsuleCollider = (CapsuleCollider*)state.ValueRO.CollisionAvoidanceCollider.GetUnsafePtr();
float maxDistance = 0.5f;
CollisionAvoidanceCollector collector = new CollisionAvoidanceCollector(self, transform.ValueRO.Position, directionToTarget, state, maxDistance, CollisionWorld);
ColliderDistanceInput distanceInput = new ColliderDistanceInput()
{
Collider = (Collider*)(capsuleCollider),
MaxDistance = maxDistance,
Scale = 1.0f,
Transform = new RigidTransform() { pos = transform.ValueRO.Position }
};
CollisionWorld.CalculateDistance(distanceInput, ref collector);
}
}
private unsafe void AvoidStacking(Entity self, RefRW<UtilityBehaviourState> state, RefRW<GameplayCharacterInputs> inputs, RefRO<LocalTransform> transform, RefRO<PhysicsCollider> physicsCollider)
{
CapsuleCollider* capsuleCollider = (CapsuleCollider*)state.ValueRO.StackingAvoidanceCollider.GetUnsafePtr();
float distanceAvoidanceRadius = capsuleCollider->Radius;
StackingAvoidanceCollector collector = new StackingAvoidanceCollector(self, transform.ValueRO.Position, state, distanceAvoidanceRadius, CollisionWorld);
CollisionWorld.OverlapSphereCustom(transform.ValueRO.Position, distanceAvoidanceRadius + 1.0f, ref collector, capsuleCollider->GetCollisionFilter());
}
private unsafe void Seek(Entity self, RefRW<UtilityBehaviourState> state, RefRW<GameplayCharacterInputs> control, RefRO<LocalTransform> transform, RefRO<PhysicsCollider> physicsCollider)
{
if (state.ValueRO.Target.HasValue)
{
float3 directionToTarget = state.ValueRO.Target.Value - transform.ValueRO.Position;
float distanceToTarget = math.length(directionToTarget);
if (distanceToTarget > state.ValueRO.ApproachRadius)
{
directionToTarget /= distanceToTarget;
distanceToTarget = math.min((distanceToTarget - state.ValueRO.ApproachRadius), state.ValueRO.ApproachRadius);
distanceToTarget = distanceToTarget > 1.0f ? state.ValueRO.MaxGroundSpeed : (distanceToTarget * state.ValueRO.MaxGroundSpeed);
for (int it = 0; it < 8; ++it)
{
state.ValueRW.Interest[it] = math.max(state.ValueRW.Interest[it], distanceToTarget * math.dot(directionToTarget, Utils.ComputeDirection(it)));
}
}
}
}
[BurstCompile]
public unsafe void Execute(Entity self, RefRW<UtilityBehaviourState> state, RefRW<GameplayCharacterInputs> inputs, RefRO<LocalTransform> localToWorld, RefRO<PhysicsCollider> physicsCollider)
{
for (int it = 0; it < 8; ++it)
{
state.ValueRW.Interest[it] = 0.0f;
state.ValueRW.Danger[it] = 0.0f;
}
AvoidObstacles(self, state, inputs, localToWorld, physicsCollider);
AvoidStacking(self, state, inputs, localToWorld, physicsCollider);
Seek(self, state, inputs, localToWorld, physicsCollider);
float3 average = 0.0f;
float3 averageInterest = 0.0f;
float3 averageDanger = 0.0f;
for (int it = 0; it < 8; ++it)
{
float3 dir = Utils.ComputeDirection(it);
averageInterest += dir * state.ValueRO.Interest[it];
averageDanger += dir * (state.ValueRO.Danger[it] * state.ValueRO.MaxGroundSpeed);
}
averageInterest = Utils.ClampToMaxLength(averageInterest, state.ValueRO.MaxGroundSpeed);
averageDanger = Utils.ClampToMaxLength(averageDanger, state.ValueRO.MaxGroundSpeed);
average = averageInterest - averageDanger;
average = math.lengthsq(average) >= 0.25f ? (average / state.ValueRO.MaxGroundSpeed) : float3.zero;
inputs.ValueRW.MoveInput = new float2(average.x, average.z);
}
}
[GhostComponent(PrefabType = GhostPrefabType.Server)]
public unsafe struct UtilityBehaviourState : IComponentData
{
public BlobAssetReference<Collider> CollisionAvoidanceCollider;
public BlobAssetReference<Collider> StackingAvoidanceCollider;
public BlobAssetReference<Collider> TargetCollider;
public fixed float Interest[8];
public fixed float Danger[8];
public float MaxGroundSpeed;
public float ApproachRadius;
public float AccumulatedDeltaTime;
public float3? Target;
}