Cleanup 2
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
using Definition.DataStruct;
|
||||
using Definition.DataStruct;
|
||||
using Entity;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -10,9 +10,5 @@ public abstract class EnemyBase : TargetableObject
|
||||
public virtual float AttackRange => 1f;
|
||||
|
||||
public virtual void SetTarget(Transform target) => _target = target;
|
||||
|
||||
protected bool IsSimulationMovementEnabled()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using CustomDebugger;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
@@ -8,28 +8,28 @@ namespace Simulation
|
||||
public sealed partial class SimulationWorld : GameFrameworkComponent
|
||||
{
|
||||
// Partial layout:
|
||||
// - SimulationWorld.cs: 核心状态、常量和 Unity 生命周期入口点。
|
||||
// - SimulationWorld.RuntimeModules.cs: 运行时域对象、配置和状态代理。
|
||||
// - SimulationWorld.SimEntityState.cs: 模拟状态的增删改查和生命周期注册。
|
||||
// - SimulationWorld.EntityToSimData.cs: Unity 实体到 sim data 的初始化适配。
|
||||
// - SimulationWorld.EntitySync.cs: GameFramework 实体 show/hide 事件桥。
|
||||
// - SimulationWorld.TargetSelectionSpatialIndex.cs: 最近敌空间索引查询。
|
||||
// - Presentation/SimulationWorld.TransformSync.cs: late-update transform 同步桥。
|
||||
// - Presentation/SimulationWorld.HitPresentation.cs: 投射物命中事件表现桥。
|
||||
// - DataChannel/SimulationWorld.JobDataChannel.cs: Job 通道共享字段、常量和运行时状态。
|
||||
// - DataChannel/SimulationWorld.JobDataLifecycle.cs: Native 通道初始化、清理和 clear。
|
||||
// - DataChannel/SimulationWorld.JobDataConversion.cs: sim/job 数据转换与输入输出缓冲准备。
|
||||
// - DataChannel/SimulationWorld.CollisionTransient.cs: 碰撞临时通道和运行时统计。
|
||||
// - DataChannel/SimulationWorld.EnemySeparationTemporal.cs: 敌人分离的帧间临时状态。
|
||||
// - DataChannel/SimulationWorld.JobOutputCommit.cs: Job 输出回写主容器。
|
||||
// - Jobs/SimulationWorld.EnemyJobs.cs: 模拟通道 编排 + 敌人移动/分离 顺序执行
|
||||
// - Jobs/SimulationWorld.ProjectileJobs.cs: 投射物移动与回收
|
||||
// - Jobs/SimulationWorld.CollisionPipeline.cs: 碰撞管线共享配置和状态
|
||||
// - Jobs/SimulationWorld.CollisionRequests.cs: area/sector 请求缓冲
|
||||
// - Jobs/SimulationWorld.CollisionBroadPhase.cs: broad-phase 候选构建和 Job 调度
|
||||
// - Jobs/SimulationWorld.CollisionResolve.cs: 主线程命中结算与 area settle
|
||||
// - Jobs/SimulationWorld.CollisionPresentation.cs: 命中表现事件和实体/impact 解析
|
||||
// - JobStruct/*.cs: burst job 内核和面向 job 的数据结构
|
||||
// - SimulationWorld.cs: 鏍稿績鐘舵€併€佸父閲忓拰 Unity 鐢熷懡鍛ㄦ湡鍏ュ彛鐐广€?
|
||||
// - SimulationWorld.RuntimeModules.cs: 杩愯鏃跺煙瀵硅薄銆侀厤缃拰鐘舵€佷唬鐞嗐€?
|
||||
// - SimulationWorld.SimEntityState.cs: 妯℃嫙鐘舵€佺殑澧炲垹鏀规煡鍜岀敓鍛藉懆鏈熸敞鍐屻€?
|
||||
// - SimulationWorld.EntityToSimData.cs: Unity 瀹炰綋鍒?sim data 鐨勫垵濮嬪寲閫傞厤銆?
|
||||
// - SimulationWorld.EntitySync.cs: GameFramework 瀹炰綋 show/hide 浜嬩欢妗ャ€?
|
||||
// - SimulationWorld.TargetSelectionSpatialIndex.cs: 鏈€杩戞晫绌洪棿绱㈠紩鏌ヨ銆?
|
||||
// - Presentation/SimulationWorld.TransformSync.cs: late-update transform 鍚屾妗ャ€?
|
||||
// - Presentation/SimulationWorld.HitPresentation.cs: 鎶曞皠鐗╁懡涓簨浠惰〃鐜版ˉ銆?
|
||||
// - DataChannel/SimulationWorld.JobDataChannel.cs: Job 閫氶亾鍏变韩瀛楁銆佸父閲忓拰杩愯鏃剁姸鎬併€?
|
||||
// - DataChannel/SimulationWorld.JobDataLifecycle.cs: Native 閫氶亾鍒濆鍖栥€佹竻鐞嗗拰 clear銆?
|
||||
// - DataChannel/SimulationWorld.JobDataConversion.cs: sim/job 鏁版嵁杞崲涓庤緭鍏ヨ緭鍑虹紦鍐插噯澶囥€?
|
||||
// - DataChannel/SimulationWorld.CollisionTransient.cs: 纰版挒涓存椂閫氶亾鍜岃繍琛屾椂缁熻銆?
|
||||
// - DataChannel/SimulationWorld.EnemySeparationTemporal.cs: 鏁屼汉鍒嗙鐨勫抚闂翠复鏃剁姸鎬併€?
|
||||
// - DataChannel/SimulationWorld.JobOutputCommit.cs: Job 杈撳嚭鍥炲啓涓诲鍣ㄣ€?
|
||||
// - Jobs/SimulationWorld.EnemyJobs.cs: 妯℃嫙閫氶亾 缂栨帓 + 鏁屼汉绉诲姩/鍒嗙 椤哄簭鎵ц
|
||||
// - Jobs/SimulationWorld.ProjectileJobs.cs: 鎶曞皠鐗╃Щ鍔ㄤ笌鍥炴敹
|
||||
// - Jobs/SimulationWorld.CollisionPipeline.cs: 纰版挒绠$嚎鍏变韩閰嶇疆鍜岀姸鎬?
|
||||
// - Jobs/SimulationWorld.CollisionRequests.cs: area/sector 璇锋眰缂撳啿
|
||||
// - Jobs/SimulationWorld.CollisionBroadPhase.cs: broad-phase 鍊欓€夋瀯寤哄拰 Job 璋冨害
|
||||
// - Jobs/SimulationWorld.CollisionResolve.cs: 涓荤嚎绋嬪懡涓粨绠椾笌 area settle
|
||||
// - Jobs/SimulationWorld.CollisionPresentation.cs: 鍛戒腑琛ㄧ幇浜嬩欢鍜屽疄浣?impact 瑙f瀽
|
||||
// - JobStruct/*.cs: burst job 鍐呮牳鍜岄潰鍚?job 鐨勬暟鎹粨鏋?
|
||||
private const float DefaultAttackRange = 1f;
|
||||
private const int EnemyStateIdle = 0;
|
||||
private const int EnemyStateChasing = 1;
|
||||
@@ -44,7 +44,6 @@ namespace Simulation
|
||||
public IReadOnlyList<EnemySimData> Enemies => _enemies;
|
||||
public IReadOnlyList<ProjectileSimData> Projectiles => _projectiles;
|
||||
public IReadOnlyList<PickupSimData> Pickups => _pickups;
|
||||
public bool UseSimulationMovement => true;
|
||||
|
||||
#region Lifecycle
|
||||
|
||||
@@ -92,3 +91,4 @@ namespace Simulation
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,163 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace CustomUtility
|
||||
{
|
||||
public static class EnemySeparationSolverProvider
|
||||
{
|
||||
private enum SolverType
|
||||
{
|
||||
GridBucket,
|
||||
Naive
|
||||
}
|
||||
|
||||
private struct LegacyRegistration
|
||||
{
|
||||
public int AgentId;
|
||||
public float BodyRadius;
|
||||
}
|
||||
|
||||
private static SolverType _solverType = SolverType.GridBucket;
|
||||
private static float _gridCellSize = 1f;
|
||||
private static IEnemySeparationSolver _legacySolver = CreateSolver();
|
||||
private static IEnemySeparationSolver _simulationSolver = CreateSolver();
|
||||
|
||||
private static readonly Dictionary<Transform, LegacyRegistration> LegacyRegistrations = new();
|
||||
private static readonly List<EnemySeparationAgent> LegacyAgents = new();
|
||||
private static readonly List<Transform> LegacyRecycle = new();
|
||||
private static int _legacySnapshotFrame = -1;
|
||||
private static int _nextLegacyAgentId = 1;
|
||||
|
||||
public static IEnemySeparationSolver Current => _simulationSolver;
|
||||
public static string CurrentSolverName => _simulationSolver.GetType().Name;
|
||||
|
||||
public static void SetSolver(IEnemySeparationSolver solver)
|
||||
{
|
||||
if (solver == null) return;
|
||||
|
||||
_legacySolver = solver;
|
||||
_simulationSolver = solver;
|
||||
_legacySnapshotFrame = -1;
|
||||
}
|
||||
|
||||
public static void UseGridBucketSolver(float cellSize = 1f)
|
||||
{
|
||||
_solverType = SolverType.GridBucket;
|
||||
_gridCellSize = Mathf.Max(0.1f, cellSize);
|
||||
RecreateSolvers();
|
||||
}
|
||||
|
||||
public static void UseNaiveSolver()
|
||||
{
|
||||
_solverType = SolverType.Naive;
|
||||
RecreateSolvers();
|
||||
}
|
||||
|
||||
public static void Register(Transform transform, float bodyRadius)
|
||||
{
|
||||
if (transform == null) return;
|
||||
|
||||
if (LegacyRegistrations.TryGetValue(transform, out LegacyRegistration registration))
|
||||
{
|
||||
registration.BodyRadius = bodyRadius;
|
||||
LegacyRegistrations[transform] = registration;
|
||||
}
|
||||
else
|
||||
{
|
||||
LegacyRegistrations.Add(transform, new LegacyRegistration
|
||||
{
|
||||
AgentId = _nextLegacyAgentId++,
|
||||
BodyRadius = bodyRadius
|
||||
});
|
||||
}
|
||||
|
||||
_legacySnapshotFrame = -1;
|
||||
}
|
||||
|
||||
public static void Unregister(Transform transform)
|
||||
{
|
||||
if (transform == null) return;
|
||||
if (!LegacyRegistrations.Remove(transform)) return;
|
||||
|
||||
_legacySnapshotFrame = -1;
|
||||
}
|
||||
|
||||
public static Vector3 Resolve(Transform transform, Vector3 desiredPosition, Vector3 fallbackDirection,
|
||||
int iterations)
|
||||
{
|
||||
if (transform == null) return desiredPosition;
|
||||
if (!LegacyRegistrations.TryGetValue(transform, out LegacyRegistration registration))
|
||||
{
|
||||
return desiredPosition;
|
||||
}
|
||||
|
||||
EnsureLegacySnapshot();
|
||||
return _legacySolver.Resolve(registration.AgentId, desiredPosition, fallbackDirection, iterations);
|
||||
}
|
||||
|
||||
public static void SetSimulationAgents(IReadOnlyList<EnemySeparationAgent> agents)
|
||||
{
|
||||
_simulationSolver.SetAgents(agents);
|
||||
}
|
||||
|
||||
public static Vector3 ResolveSimulation(int agentId, Vector3 desiredPosition, Vector3 fallbackDirection,
|
||||
int iterations)
|
||||
{
|
||||
return _simulationSolver.Resolve(agentId, desiredPosition, fallbackDirection, iterations);
|
||||
}
|
||||
|
||||
private static void EnsureLegacySnapshot()
|
||||
{
|
||||
int frame = Time.frameCount;
|
||||
if (_legacySnapshotFrame == frame) return;
|
||||
|
||||
_legacySnapshotFrame = frame;
|
||||
LegacyAgents.Clear();
|
||||
LegacyRecycle.Clear();
|
||||
|
||||
foreach (var pair in LegacyRegistrations)
|
||||
{
|
||||
Transform transform = pair.Key;
|
||||
if (transform == null)
|
||||
{
|
||||
LegacyRecycle.Add(pair.Key);
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector3 position = transform.position;
|
||||
position.y = 0f;
|
||||
|
||||
LegacyAgents.Add(new EnemySeparationAgent
|
||||
{
|
||||
AgentId = pair.Value.AgentId,
|
||||
Position = position,
|
||||
Radius = Mathf.Max(0.01f, pair.Value.BodyRadius)
|
||||
});
|
||||
}
|
||||
|
||||
for (int i = 0; i < LegacyRecycle.Count; i++)
|
||||
{
|
||||
LegacyRegistrations.Remove(LegacyRecycle[i]);
|
||||
}
|
||||
|
||||
_legacySolver.SetAgents(LegacyAgents);
|
||||
}
|
||||
|
||||
private static void RecreateSolvers()
|
||||
{
|
||||
_legacySolver = CreateSolver();
|
||||
_simulationSolver = CreateSolver();
|
||||
_legacySnapshotFrame = -1;
|
||||
}
|
||||
|
||||
private static IEnemySeparationSolver CreateSolver()
|
||||
{
|
||||
if (_solverType == SolverType.Naive)
|
||||
{
|
||||
return new NaiveEnemySeparationSolver();
|
||||
}
|
||||
|
||||
return new GridBucketEnemySeparationSolver(_gridCellSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3cf44095cd7c76043a8e8a44dc5a0888
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,182 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace CustomUtility
|
||||
{
|
||||
public sealed class GridBucketEnemySeparationSolver : IEnemySeparationSolver
|
||||
{
|
||||
private struct Agent
|
||||
{
|
||||
public float Radius;
|
||||
public Vector3 Position;
|
||||
public int CellX;
|
||||
public int CellZ;
|
||||
}
|
||||
|
||||
private readonly System.Collections.Generic.Dictionary<int, Agent> _agents = new();
|
||||
private readonly System.Collections.Generic.Dictionary<long, System.Collections.Generic.List<int>> _buckets = new();
|
||||
private readonly System.Collections.Generic.Stack<System.Collections.Generic.List<int>> _bucketListPool = new();
|
||||
private readonly System.Collections.Generic.List<long> _activeBucketKeys = new();
|
||||
private readonly float _cellSize;
|
||||
|
||||
private float _maxRadius = 0.45f;
|
||||
|
||||
public GridBucketEnemySeparationSolver(float cellSize = 1f)
|
||||
{
|
||||
_cellSize = Mathf.Max(0.1f, cellSize);
|
||||
}
|
||||
|
||||
public void SetAgents(System.Collections.Generic.IReadOnlyList<EnemySeparationAgent> agents)
|
||||
{
|
||||
RecycleBucketsForSnapshot();
|
||||
_agents.Clear();
|
||||
_maxRadius = 0.01f;
|
||||
|
||||
if (agents == null) return;
|
||||
|
||||
for (int i = 0; i < agents.Count; i++)
|
||||
{
|
||||
EnemySeparationAgent input = agents[i];
|
||||
Vector3 position = input.Position;
|
||||
position.y = 0f;
|
||||
float radius = Mathf.Max(0.01f, input.Radius);
|
||||
|
||||
Agent agent = new Agent
|
||||
{
|
||||
Radius = radius,
|
||||
Position = position,
|
||||
CellX = ToCell(position.x),
|
||||
CellZ = ToCell(position.z)
|
||||
};
|
||||
|
||||
_agents[input.AgentId] = agent;
|
||||
AddToBucket(input.AgentId, agent.CellX, agent.CellZ);
|
||||
|
||||
if (radius > _maxRadius)
|
||||
{
|
||||
_maxRadius = radius;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3 Resolve(int agentId, Vector3 desiredPosition, Vector3 fallbackDirection, int iterations)
|
||||
{
|
||||
if (!_agents.TryGetValue(agentId, out var self)) return desiredPosition;
|
||||
|
||||
Vector3 candidate = desiredPosition;
|
||||
candidate.y = 0f;
|
||||
|
||||
int effectiveIterations = Mathf.Max(1, iterations);
|
||||
int queryRange = Mathf.Max(1, Mathf.CeilToInt((self.Radius + _maxRadius) / _cellSize));
|
||||
Vector3 fallback = fallbackDirection.sqrMagnitude > 0.0001f ? fallbackDirection.normalized : Vector3.right;
|
||||
fallback.y = 0f;
|
||||
|
||||
for (int iter = 0; iter < effectiveIterations; iter++)
|
||||
{
|
||||
int cellX = ToCell(candidate.x);
|
||||
int cellZ = ToCell(candidate.z);
|
||||
|
||||
for (int dx = -queryRange; dx <= queryRange; dx++)
|
||||
{
|
||||
for (int dz = -queryRange; dz <= queryRange; dz++)
|
||||
{
|
||||
if (!_buckets.TryGetValue(CellKey(cellX + dx, cellZ + dz), out var bucket)) continue;
|
||||
|
||||
for (int i = 0; i < bucket.Count; i++)
|
||||
{
|
||||
int otherAgentId = bucket[i];
|
||||
if (otherAgentId == agentId) continue;
|
||||
if (!_agents.TryGetValue(otherAgentId, out var other)) continue;
|
||||
|
||||
Vector3 toSelf = candidate - other.Position;
|
||||
float minDistance = self.Radius + other.Radius;
|
||||
float minDistanceSq = minDistance * minDistance;
|
||||
float sqrDistance = toSelf.sqrMagnitude;
|
||||
|
||||
if (sqrDistance <= Mathf.Epsilon)
|
||||
{
|
||||
candidate += fallback * (self.Radius * 0.25f);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sqrDistance >= minDistanceSq) continue;
|
||||
|
||||
float distance = Mathf.Sqrt(sqrDistance);
|
||||
float penetration = minDistance - distance;
|
||||
candidate += (toSelf / distance) * penetration;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SyncAgentPosition(agentId, ref self, candidate);
|
||||
|
||||
candidate.y = desiredPosition.y;
|
||||
return candidate;
|
||||
}
|
||||
|
||||
private void RecycleBucketsForSnapshot()
|
||||
{
|
||||
for (int i = 0; i < _activeBucketKeys.Count; i++)
|
||||
{
|
||||
long key = _activeBucketKeys[i];
|
||||
if (!_buckets.TryGetValue(key, out var bucket)) continue;
|
||||
|
||||
bucket.Clear();
|
||||
_bucketListPool.Push(bucket);
|
||||
_buckets.Remove(key);
|
||||
}
|
||||
|
||||
_activeBucketKeys.Clear();
|
||||
}
|
||||
|
||||
private void SyncAgentPosition(int agentId, ref Agent agent, Vector3 position)
|
||||
{
|
||||
int newCellX = ToCell(position.x);
|
||||
int newCellZ = ToCell(position.z);
|
||||
|
||||
if (agent.CellX != newCellX || agent.CellZ != newCellZ)
|
||||
{
|
||||
RemoveFromBucket(agentId, agent.CellX, agent.CellZ);
|
||||
AddToBucket(agentId, newCellX, newCellZ);
|
||||
agent.CellX = newCellX;
|
||||
agent.CellZ = newCellZ;
|
||||
}
|
||||
|
||||
agent.Position = position;
|
||||
_agents[agentId] = agent;
|
||||
}
|
||||
|
||||
private void AddToBucket(int agentId, int cellX, int cellZ)
|
||||
{
|
||||
long key = CellKey(cellX, cellZ);
|
||||
if (!_buckets.TryGetValue(key, out var list))
|
||||
{
|
||||
list = _bucketListPool.Count > 0
|
||||
? _bucketListPool.Pop()
|
||||
: new System.Collections.Generic.List<int>(8);
|
||||
_buckets.Add(key, list);
|
||||
_activeBucketKeys.Add(key);
|
||||
}
|
||||
|
||||
list.Add(agentId);
|
||||
}
|
||||
|
||||
private void RemoveFromBucket(int agentId, int cellX, int cellZ)
|
||||
{
|
||||
long key = CellKey(cellX, cellZ);
|
||||
if (!_buckets.TryGetValue(key, out var list)) return;
|
||||
|
||||
list.Remove(agentId);
|
||||
}
|
||||
|
||||
private int ToCell(float value)
|
||||
{
|
||||
return Mathf.FloorToInt(value / _cellSize);
|
||||
}
|
||||
|
||||
private static long CellKey(int x, int z)
|
||||
{
|
||||
return ((long)x << 32) ^ (uint)z;
|
||||
}
|
||||
}
|
||||
}
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c7c10dca24b508f4fa6726eae7ac2fb1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,17 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace CustomUtility
|
||||
{
|
||||
public struct EnemySeparationAgent
|
||||
{
|
||||
public int AgentId;
|
||||
public Vector3 Position;
|
||||
public float Radius;
|
||||
}
|
||||
|
||||
public interface IEnemySeparationSolver
|
||||
{
|
||||
void SetAgents(System.Collections.Generic.IReadOnlyList<EnemySeparationAgent> agents);
|
||||
Vector3 Resolve(int agentId, Vector3 desiredPosition, Vector3 fallbackDirection, int iterations);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e3960124c8fe4304493659a13e5a9439
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,81 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace CustomUtility
|
||||
{
|
||||
public sealed class NaiveEnemySeparationSolver : IEnemySeparationSolver
|
||||
{
|
||||
private struct Agent
|
||||
{
|
||||
public float Radius;
|
||||
public Vector3 Position;
|
||||
}
|
||||
|
||||
private readonly System.Collections.Generic.Dictionary<int, Agent> _agents = new();
|
||||
private readonly System.Collections.Generic.List<int> _agentKeys = new();
|
||||
|
||||
public void SetAgents(System.Collections.Generic.IReadOnlyList<EnemySeparationAgent> agents)
|
||||
{
|
||||
_agents.Clear();
|
||||
_agentKeys.Clear();
|
||||
if (agents == null) return;
|
||||
|
||||
for (int i = 0; i < agents.Count; i++)
|
||||
{
|
||||
EnemySeparationAgent input = agents[i];
|
||||
Vector3 position = input.Position;
|
||||
position.y = 0f;
|
||||
|
||||
Agent agent = new Agent
|
||||
{
|
||||
Radius = Mathf.Max(0.01f, input.Radius),
|
||||
Position = position
|
||||
};
|
||||
|
||||
_agents[input.AgentId] = agent;
|
||||
_agentKeys.Add(input.AgentId);
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3 Resolve(int agentId, Vector3 desiredPosition, Vector3 fallbackDirection, int iterations)
|
||||
{
|
||||
if (!_agents.TryGetValue(agentId, out var self)) return desiredPosition;
|
||||
|
||||
Vector3 candidate = desiredPosition;
|
||||
candidate.y = 0f;
|
||||
|
||||
Vector3 fallback = fallbackDirection.sqrMagnitude > 0.0001f ? fallbackDirection.normalized : Vector3.right;
|
||||
fallback.y = 0f;
|
||||
|
||||
int effectiveIterations = Mathf.Max(1, iterations);
|
||||
for (int iter = 0; iter < effectiveIterations; iter++)
|
||||
{
|
||||
for (int i = 0; i < _agentKeys.Count; i++)
|
||||
{
|
||||
int otherAgentId = _agentKeys[i];
|
||||
if (otherAgentId == agentId) continue;
|
||||
if (!_agents.TryGetValue(otherAgentId, out var other)) continue;
|
||||
|
||||
Vector3 toSelf = candidate - other.Position;
|
||||
float minDistance = self.Radius + other.Radius;
|
||||
float minDistanceSq = minDistance * minDistance;
|
||||
float sqrDistance = toSelf.sqrMagnitude;
|
||||
|
||||
if (sqrDistance <= Mathf.Epsilon)
|
||||
{
|
||||
candidate += fallback * (self.Radius * 0.25f);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sqrDistance >= minDistanceSq) continue;
|
||||
|
||||
float distance = Mathf.Sqrt(sqrDistance);
|
||||
float penetration = minDistance - distance;
|
||||
candidate += (toSelf / distance) * penetration;
|
||||
}
|
||||
}
|
||||
|
||||
candidate.y = desiredPosition.y;
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ec8ec1013900437498da4613f680a898
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user