216 lines
8.5 KiB
C#
216 lines
8.5 KiB
C#
using SepCore.Debugger;
|
|
using Unity.Burst;
|
|
using Unity.Collections;
|
|
using Unity.Jobs;
|
|
using Unity.Mathematics;
|
|
|
|
namespace SepCore.Simulation
|
|
{
|
|
internal static class CollisionQuerySystem
|
|
{
|
|
// 读写集:R EnemyTransform / EnemyMoveConfig / EnemyIdentity / CollisionQueryData(瞬时)
|
|
// W CollisionCandidateData(瞬时) / 敌人碰撞分桶
|
|
public static void BuildEnemyCollisionBuckets(EnemyStore enemies,
|
|
NativeParallelMultiHashMap<long, int> buckets, float cellSize)
|
|
{
|
|
buckets.Clear();
|
|
int enemyCount = enemies.Count;
|
|
if (enemyCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
BuildCollisionBucketsJob job = new BuildCollisionBucketsJob
|
|
{
|
|
Transforms = enemies.Transform.Data.AsArray(),
|
|
Buckets = buckets.AsParallelWriter(),
|
|
CellSize = cellSize
|
|
};
|
|
|
|
using (CustomProfilerMarker.TickEnemies_Complete.Auto())
|
|
{
|
|
job.Schedule(enemyCount, 64).Complete();
|
|
}
|
|
}
|
|
|
|
public static bool ScheduleCandidateQuery(NativeList<CollisionQueryData> queries,
|
|
NativeList<CollisionCandidateData> candidates, NativeParallelMultiHashMap<long, int> enemyBuckets,
|
|
EnemyStore enemies, bool hasEnemyTargets, bool hasPlayerTarget, int playerTargetEntityId,
|
|
float3 playerPosition, float cellSize, out JobHandle handle)
|
|
{
|
|
handle = default;
|
|
if (!queries.IsCreated || !candidates.IsCreated)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
candidates.Clear();
|
|
int queryCount = queries.Length;
|
|
if (queryCount == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
QueryCollisionCandidatesJob job = new QueryCollisionCandidatesJob
|
|
{
|
|
Queries = queries.AsArray(),
|
|
EnemyBuckets = enemyBuckets,
|
|
EnemyTransforms = enemies.Transform.Data.AsArray(),
|
|
EnemyConfigs = enemies.MoveConfig.Data.AsArray(),
|
|
EnemyIdentities = enemies.Identity.Data.AsArray(),
|
|
Candidates = candidates.AsParallelWriter(),
|
|
HasEnemyTargets = hasEnemyTargets,
|
|
HasPlayerTarget = hasPlayerTarget,
|
|
PlayerTargetEntityId = playerTargetEntityId,
|
|
PlayerPosition = playerPosition,
|
|
CellSize = cellSize
|
|
};
|
|
|
|
handle = job.Schedule(queryCount, 64);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
[BurstCompile]
|
|
internal struct BuildCollisionBucketsJob : IJobParallelFor
|
|
{
|
|
[ReadOnly] public NativeArray<EnemyTransformSim> Transforms;
|
|
public NativeParallelMultiHashMap<long, int>.ParallelWriter Buckets;
|
|
public float CellSize;
|
|
|
|
public void Execute(int index)
|
|
{
|
|
float3 position = Transforms[index].Position;
|
|
int cellX = SimulationCell.ToCell(position.x, CellSize);
|
|
int cellZ = SimulationCell.ToCell(position.z, CellSize);
|
|
Buckets.Add(SimulationCell.Key(cellX, cellZ), index);
|
|
}
|
|
}
|
|
|
|
[BurstCompile]
|
|
internal struct QueryCollisionCandidatesJob : IJobParallelFor
|
|
{
|
|
[ReadOnly] public NativeArray<CollisionQueryData> Queries;
|
|
[ReadOnly] public NativeParallelMultiHashMap<long, int> EnemyBuckets;
|
|
[ReadOnly] public NativeArray<EnemyTransformSim> EnemyTransforms;
|
|
[ReadOnly] public NativeArray<EnemyMoveConfigSim> EnemyConfigs;
|
|
[ReadOnly] public NativeArray<EnemyIdentitySim> EnemyIdentities;
|
|
public NativeList<CollisionCandidateData>.ParallelWriter Candidates;
|
|
public bool HasEnemyTargets;
|
|
public bool HasPlayerTarget;
|
|
public int PlayerTargetEntityId;
|
|
public float3 PlayerPosition;
|
|
public float CellSize;
|
|
|
|
public void Execute(int index)
|
|
{
|
|
CollisionQueryData query = Queries[index];
|
|
int centerCellX = SimulationCell.ToCell(query.Position.x, CellSize);
|
|
int centerCellZ = SimulationCell.ToCell(query.Position.z, CellSize);
|
|
int queryRange = math.max(1, (int)math.ceil(query.Radius / CellSize));
|
|
int selectedCount = 0;
|
|
bool reachedLimit = false;
|
|
|
|
if (HasPlayerTarget && query.SourceEntityId != PlayerTargetEntityId &&
|
|
query.SourceOwnerEntityId != PlayerTargetEntityId)
|
|
{
|
|
float3 playerPosition = PlayerPosition;
|
|
if (query.SourceType == SimulationCollision.SourceTypeArea)
|
|
{
|
|
playerPosition.y = query.Position.y;
|
|
}
|
|
|
|
float3 playerDelta = playerPosition - query.Position;
|
|
float playerSqrDistance = math.lengthsq(playerDelta);
|
|
float playerRadiusSqr = query.Radius * query.Radius;
|
|
if (playerSqrDistance <= playerRadiusSqr)
|
|
{
|
|
Candidates.AddNoResize(new CollisionCandidateData
|
|
{
|
|
QueryId = query.QueryId,
|
|
SourceType = query.SourceType,
|
|
SourceEntityId = query.SourceEntityId,
|
|
SourceOwnerEntityId = query.SourceOwnerEntityId,
|
|
TargetEntityId = PlayerTargetEntityId,
|
|
SqrDistance = playerSqrDistance
|
|
});
|
|
|
|
selectedCount++;
|
|
if (selectedCount >= query.MaxTargets)
|
|
{
|
|
reachedLimit = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!HasEnemyTargets || reachedLimit)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (int dx = -queryRange; dx <= queryRange && !reachedLimit; dx++)
|
|
{
|
|
for (int dz = -queryRange; dz <= queryRange && !reachedLimit; dz++)
|
|
{
|
|
long key = SimulationCell.Key(centerCellX + dx, centerCellZ + dz);
|
|
if (!EnemyBuckets.TryGetFirstValue(key, out int enemyIndex,
|
|
out NativeParallelMultiHashMapIterator<long> iterator))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
do
|
|
{
|
|
if (enemyIndex < 0 || enemyIndex >= EnemyTransforms.Length)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
EnemyIdentitySim identity = EnemyIdentities[enemyIndex];
|
|
if (identity.EntityId == query.SourceOwnerEntityId)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
EnemyTransformSim enemyTransform = EnemyTransforms[enemyIndex];
|
|
float deltaY = query.SourceType == SimulationCollision.SourceTypeArea
|
|
? 0f
|
|
: enemyTransform.Position.y - query.Position.y;
|
|
float3 delta = new float3(
|
|
enemyTransform.Position.x - query.Position.x,
|
|
deltaY,
|
|
enemyTransform.Position.z - query.Position.z);
|
|
float sqrDistance = math.lengthsq(delta);
|
|
float targetRadius = query.SourceType == SimulationCollision.SourceTypeArea
|
|
? math.max(0f, EnemyConfigs[enemyIndex].BodyRadius)
|
|
: 0f;
|
|
float effectiveRadius = query.Radius + targetRadius;
|
|
if (sqrDistance > effectiveRadius * effectiveRadius)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
Candidates.AddNoResize(new CollisionCandidateData
|
|
{
|
|
QueryId = query.QueryId,
|
|
SourceType = query.SourceType,
|
|
SourceEntityId = query.SourceEntityId,
|
|
SourceOwnerEntityId = query.SourceOwnerEntityId,
|
|
TargetEntityId = identity.EntityId,
|
|
SqrDistance = sqrDistance
|
|
});
|
|
|
|
selectedCount++;
|
|
|
|
if (selectedCount >= query.MaxTargets)
|
|
{
|
|
reachedLimit = true;
|
|
break;
|
|
}
|
|
} while (EnemyBuckets.TryGetNextValue(out enemyIndex, ref iterator));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|