第一批重构,主要是物理迁移脚本和部分命名空间的调整
- 结构上迁移文件,将脚本分成 Base/Runtime/Presentation/Procedure/Editor 五大文件夹 - 调整 DataTable 和 Definition 下脚本的命名空间 - 依据 UI-5层架构设计规范 拆分原先的 UI 到 Runtime 和 Presentation 下
This commit is contained in:
+363
@@ -0,0 +1,363 @@
|
||||
using Components;
|
||||
using CustomDebugger;
|
||||
using CustomUtility;
|
||||
using SepCore.Definition;
|
||||
using Entity;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Simulation
|
||||
{
|
||||
public sealed partial class SimulationWorld
|
||||
{
|
||||
#region Collision Resolve
|
||||
|
||||
private void ResolveCollisionCandidatesOnMainThread()
|
||||
{
|
||||
if (!_collisionCandidates.IsCreated)
|
||||
{
|
||||
_lastResolvedAreaHitCount = 0;
|
||||
ClearAreaCollisionTransientBuffers();
|
||||
return;
|
||||
}
|
||||
|
||||
_projectileResolvedEntityIds.Clear();
|
||||
_areaCollisionHitEvents.Clear();
|
||||
_areaCollisionHitDedupKeys.Clear();
|
||||
|
||||
if (_collisionCandidates.Length == 0)
|
||||
{
|
||||
_lastResolvedAreaHitCount = 0;
|
||||
ClearAreaCollisionTransientBuffers();
|
||||
return;
|
||||
}
|
||||
|
||||
using (CustomProfilerMarker.Collision_ResolveProjectile.Auto())
|
||||
{
|
||||
for (int i = 0; i < _collisionCandidates.Length; i++)
|
||||
{
|
||||
CollisionCandidateData candidate = _collisionCandidates[i];
|
||||
if (candidate.SourceType == CollisionSourceTypeProjectile)
|
||||
{
|
||||
int projectileEntityId = candidate.SourceEntityId;
|
||||
if (_projectileResolvedEntityIds.Contains(projectileEntityId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!TryGetActiveProjectileSimData(projectileEntityId, out _, out ProjectileSimData projectile))
|
||||
{
|
||||
_projectileResolvedEntityIds.Add(projectileEntityId);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool shouldExpireProjectile = true;
|
||||
bool shouldDispatchPresentation = false;
|
||||
int damage = 0;
|
||||
Vector3 hitPosition = projectile.Position;
|
||||
if (TryGetAliveTargetableEntity(candidate.TargetEntityId, out TargetableObject target))
|
||||
{
|
||||
EntityBase sourceEntity = TryGetEntityById(candidate.SourceEntityId);
|
||||
EntityBase ownerEntity = TryGetEntityById(candidate.SourceOwnerEntityId);
|
||||
shouldExpireProjectile = ResolveProjectileHitAgainstTarget(target, sourceEntity,
|
||||
ownerEntity,
|
||||
in projectile,
|
||||
out damage, out hitPosition, out shouldDispatchPresentation);
|
||||
}
|
||||
|
||||
if (shouldDispatchPresentation)
|
||||
{
|
||||
DispatchProjectileHitPresentationEvent(projectileEntityId, candidate.SourceEntityId,
|
||||
candidate.SourceOwnerEntityId, candidate.TargetEntityId, damage, in hitPosition);
|
||||
}
|
||||
|
||||
if (shouldExpireProjectile)
|
||||
{
|
||||
MarkProjectileAsExpired(projectileEntityId);
|
||||
_projectileResolvedEntityIds.Add(projectileEntityId);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (candidate.SourceType == CollisionSourceTypeArea)
|
||||
{
|
||||
long dedupKey = (((long)candidate.QueryId) << 32) ^ (uint)candidate.TargetEntityId;
|
||||
if (!_areaCollisionHitDedupKeys.Add(dedupKey))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_areaCollisionHitEvents.Add(new AreaCollisionHitEventData
|
||||
{
|
||||
QueryId = candidate.QueryId,
|
||||
SourceEntityId = candidate.SourceEntityId,
|
||||
SourceOwnerEntityId = candidate.SourceOwnerEntityId,
|
||||
TargetEntityId = candidate.TargetEntityId,
|
||||
SqrDistance = candidate.SqrDistance
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int resolvedAreaHitCount;
|
||||
using (CustomProfilerMarker.Collision_ResolveArea.Auto())
|
||||
{
|
||||
resolvedAreaHitCount = ResolveAreaCollisionHitsOnMainThread();
|
||||
}
|
||||
|
||||
_lastResolvedAreaHitCount = resolvedAreaHitCount;
|
||||
_projectileResolvedEntityIds.Clear();
|
||||
ClearAreaCollisionTransientBuffers();
|
||||
}
|
||||
|
||||
private bool ResolveProjectileHitAgainstTarget(TargetableObject target, EntityBase sourceEntity,
|
||||
EntityBase ownerEntity,
|
||||
in ProjectileSimData projectile, out int damage, out Vector3 hitPosition,
|
||||
out bool shouldDispatchPresentation)
|
||||
{
|
||||
damage = 0;
|
||||
hitPosition = projectile.Position;
|
||||
shouldDispatchPresentation = false;
|
||||
|
||||
if (target == null || !target.Available || target.IsDead)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (target.CachedTransform != null)
|
||||
{
|
||||
hitPosition = target.CachedTransform.position;
|
||||
}
|
||||
|
||||
if (!TryResolveImpactSource(sourceEntity, ownerEntity, out EntityBase attacker,
|
||||
out ImpactData sourceImpact))
|
||||
{
|
||||
shouldDispatchPresentation = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
ImpactData targetImpact = target.GetImpactData();
|
||||
if (AIUtility.GetRelation(targetImpact.Camp, sourceImpact.Camp) == RelationType.Friendly)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
damage = AIUtility.CalcDamageHP(sourceImpact.AttackBase, sourceImpact.AttackStat,
|
||||
targetImpact.DefenseStat,
|
||||
targetImpact.DodgeStat);
|
||||
shouldDispatchPresentation = true;
|
||||
if (damage <= 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
target.ApplyDamage(attacker ?? sourceEntity ?? ownerEntity, damage);
|
||||
return true;
|
||||
}
|
||||
|
||||
private int ResolveAreaCollisionHitsOnMainThread()
|
||||
{
|
||||
if (_areaCollisionHitEvents.Count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int resolvedHitCount = 0;
|
||||
for (int i = 0; i < _areaCollisionHitEvents.Count; i++)
|
||||
{
|
||||
AreaCollisionHitEventData hitEvent = _areaCollisionHitEvents[i];
|
||||
if (!TryGetCollisionQueryByQueryId(hitEvent.QueryId, out CollisionQueryData query) ||
|
||||
query.SourceType != CollisionSourceTypeArea)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!query.SourceWasActiveAtQueryTime)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
EntityBase sourceEntity = TryGetEntityById(hitEvent.SourceEntityId);
|
||||
if (sourceEntity == null || !sourceEntity.Available)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!TryGetAliveTargetableEntity(hitEvent.TargetEntityId, out TargetableObject target))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
float targetRadius = ResolveAreaTargetRadius(target);
|
||||
if (!IsAreaTargetInsidePreciseShape(in query, target, targetRadius))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
AIUtility.PerformCollision(target, sourceEntity, true);
|
||||
resolvedHitCount++;
|
||||
}
|
||||
|
||||
return resolvedHitCount;
|
||||
}
|
||||
|
||||
private bool TryGetCollisionQueryByQueryId(int queryId, out CollisionQueryData query)
|
||||
{
|
||||
query = default;
|
||||
if (!_collisionQueryInputs.IsCreated || queryId < 0 || queryId >= _collisionQueryInputs.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CollisionQueryData direct = _collisionQueryInputs[queryId];
|
||||
if (direct.QueryId == queryId)
|
||||
{
|
||||
query = direct;
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _collisionQueryInputs.Length; i++)
|
||||
{
|
||||
CollisionQueryData candidate = _collisionQueryInputs[i];
|
||||
if (candidate.QueryId != queryId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
query = candidate;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private float ResolveAreaTargetRadius(TargetableObject target)
|
||||
{
|
||||
if (target == null)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
if (target is EnemyBase && TryGetEnemyData(target.Id, out EnemySimData enemyData))
|
||||
{
|
||||
return Mathf.Max(0f, enemyData.EnemyBodyRadius);
|
||||
}
|
||||
|
||||
MovementComponent movementComponent = target.GetComponent<MovementComponent>();
|
||||
return movementComponent != null ? Mathf.Max(0f, movementComponent.EnemyBodyRadius) : 0f;
|
||||
}
|
||||
|
||||
private static bool IsAreaTargetInsidePreciseShape(in CollisionQueryData query, TargetableObject target,
|
||||
float targetRadius)
|
||||
{
|
||||
if (target == null || target.CachedTransform == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector3 center = new Vector3(query.Position.x, query.Position.y, query.Position.z);
|
||||
Vector3 toTarget = target.CachedTransform.position - center;
|
||||
toTarget.y = 0f;
|
||||
|
||||
float radius = Mathf.Max(0.01f, query.Radius + Mathf.Max(0f, targetRadius));
|
||||
float radiusSqr = radius * radius;
|
||||
float sqrDistance = toTarget.sqrMagnitude;
|
||||
if (sqrDistance > radiusSqr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (query.ShapeType == CollisionShapeRectangle)
|
||||
{
|
||||
Vector3 forwardRect = new Vector3(query.Direction.x, query.Direction.y, query.Direction.z);
|
||||
forwardRect.y = 0f;
|
||||
if (forwardRect.sqrMagnitude <= Mathf.Epsilon)
|
||||
{
|
||||
forwardRect = Vector3.forward;
|
||||
}
|
||||
else
|
||||
{
|
||||
forwardRect.Normalize();
|
||||
}
|
||||
|
||||
Vector3 rightRect = Vector3.Cross(Vector3.up, forwardRect);
|
||||
float halfWidth = Mathf.Max(0.01f, query.HalfWidth + Mathf.Max(0f, targetRadius));
|
||||
float halfLength = Mathf.Max(0.01f, query.HalfLength + Mathf.Max(0f, targetRadius));
|
||||
float forwardDistance = Vector3.Dot(toTarget, forwardRect);
|
||||
float lateralDistance = Vector3.Dot(toTarget, rightRect);
|
||||
return Mathf.Abs(forwardDistance) <= halfLength && Mathf.Abs(lateralDistance) <= halfWidth;
|
||||
}
|
||||
|
||||
if (query.ShapeType != CollisionShapeSector)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sqrDistance <= Mathf.Epsilon)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Vector3 forward = new Vector3(query.Direction.x, query.Direction.y, query.Direction.z);
|
||||
forward.y = 0f;
|
||||
if (forward.sqrMagnitude <= Mathf.Epsilon)
|
||||
{
|
||||
forward = Vector3.forward;
|
||||
}
|
||||
else
|
||||
{
|
||||
forward.Normalize();
|
||||
}
|
||||
|
||||
float halfAngle = Mathf.Clamp(query.HalfAngleDeg, 0f, 180f);
|
||||
float angle = Vector3.Angle(forward, toTarget.normalized);
|
||||
return angle <= halfAngle;
|
||||
}
|
||||
|
||||
private bool TryGetActiveProjectileSimData(int projectileEntityId, out int simulationIndex,
|
||||
out ProjectileSimData projectile)
|
||||
{
|
||||
simulationIndex = -1;
|
||||
projectile = default;
|
||||
|
||||
if (!ProjectileBinding.TryGetSimulationIndex(projectileEntityId, out int foundIndex) || foundIndex < 0 ||
|
||||
foundIndex >= _projectiles.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ProjectileSimData data = _projectiles[foundIndex];
|
||||
if (!data.Active || data.State != ProjectileStateActive)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
simulationIndex = foundIndex;
|
||||
projectile = data;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool MarkProjectileAsExpired(int projectileEntityId)
|
||||
{
|
||||
if (!ProjectileBinding.TryGetSimulationIndex(projectileEntityId, out int simulationIndex) ||
|
||||
simulationIndex < 0 || simulationIndex >= _projectiles.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ProjectileSimData projectile = _projectiles[simulationIndex];
|
||||
projectile.Active = false;
|
||||
projectile.State = ProjectileStateExpired;
|
||||
projectile.RemainingLifetime = 0f;
|
||||
if (projectile.LifeTime > 0f && projectile.Age < projectile.LifeTime)
|
||||
{
|
||||
projectile.Age = projectile.LifeTime;
|
||||
}
|
||||
|
||||
_projectiles[simulationIndex] = projectile;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user