Cleanup 2

This commit is contained in:
SepComet
2026-04-02 12:36:01 +08:00
parent 1052cc0136
commit ffcd4e6b54
22 changed files with 246 additions and 598 deletions
@@ -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;
}
}
}
@@ -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:
@@ -1,4 +1,4 @@
using System.Reflection;
using System.Reflection;
using Components;
using NUnit.Framework;
using UnityEngine;
@@ -88,7 +88,7 @@ namespace Simulation.Tests.Editor
}
[Test]
public void SyncEnemyMovementInput_DisablesEnemyMovementOnSimulationPath()
public void SyncEnemyMovementInput_DisablesEnemyMovement()
{
UpsertEnemy(new EnemySimData
{
@@ -166,3 +166,4 @@ namespace Simulation.Tests.Editor
}
}
}
@@ -1,4 +1,4 @@
using System.Collections;
using System.Collections;
using System.Reflection;
using Entity;
using Entity.EntityData;
@@ -99,7 +99,7 @@ namespace Simulation.Tests.PlayMode
}
[UnityTest]
public IEnumerator Tick_RespectsEnemyMovementSyncFromComponentShell()
public IEnumerator Tick_RespectsEnemyMovementSync()
{
_world.SyncEnemyMovementInput(4001, false, Vector3.left, 5f, true, 0.45f, 2);
@@ -133,3 +133,4 @@ namespace Simulation.Tests.PlayMode
}
}
}