diff --git a/Assets/GameMain/Scripts/Simulation.meta b/Assets/GameMain/Scripts/Simulation.meta new file mode 100644 index 0000000..089d035 --- /dev/null +++ b/Assets/GameMain/Scripts/Simulation.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7f519a6e4d7b4cb4ab5eabf95fb55e1b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Simulation/EnemySimData.cs b/Assets/GameMain/Scripts/Simulation/EnemySimData.cs new file mode 100644 index 0000000..165074b --- /dev/null +++ b/Assets/GameMain/Scripts/Simulation/EnemySimData.cs @@ -0,0 +1,15 @@ +using UnityEngine; + +namespace Simulation +{ + public struct EnemySimData + { + public int EntityId; + public Vector3 Position; + public Vector3 Forward; + public float Speed; + public float AttackRange; + public int TargetType; + public int State; + } +} diff --git a/Assets/GameMain/Scripts/Simulation/EnemySimData.cs.meta b/Assets/GameMain/Scripts/Simulation/EnemySimData.cs.meta new file mode 100644 index 0000000..448e55c --- /dev/null +++ b/Assets/GameMain/Scripts/Simulation/EnemySimData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ef2eea85685544189eef2d9f2cece080 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Simulation/EntityBinding.cs b/Assets/GameMain/Scripts/Simulation/EntityBinding.cs new file mode 100644 index 0000000..836fb16 --- /dev/null +++ b/Assets/GameMain/Scripts/Simulation/EntityBinding.cs @@ -0,0 +1,80 @@ +using System.Collections.Generic; + +namespace Simulation +{ + public sealed class EntityBinding + { + private readonly Dictionary _entityIdToSimulationIndex = new Dictionary(); + private readonly Dictionary _simulationIndexToEntityId = new Dictionary(); + + public int Count => _entityIdToSimulationIndex.Count; + + public void Bind(int entityId, int simulationIndex) + { + if (_entityIdToSimulationIndex.TryGetValue(entityId, out int oldSimulationIndex)) + { + _simulationIndexToEntityId.Remove(oldSimulationIndex); + } + + if (_simulationIndexToEntityId.TryGetValue(simulationIndex, out int oldEntityId)) + { + _entityIdToSimulationIndex.Remove(oldEntityId); + } + + _entityIdToSimulationIndex[entityId] = simulationIndex; + _simulationIndexToEntityId[simulationIndex] = entityId; + } + + public void RemapIndex(int entityId, int newSimulationIndex) + { + if (!_entityIdToSimulationIndex.TryGetValue(entityId, out int oldSimulationIndex)) + { + return; + } + + _simulationIndexToEntityId.Remove(oldSimulationIndex); + _entityIdToSimulationIndex[entityId] = newSimulationIndex; + _simulationIndexToEntityId[newSimulationIndex] = entityId; + } + + public bool TryGetSimulationIndex(int entityId, out int simulationIndex) + { + return _entityIdToSimulationIndex.TryGetValue(entityId, out simulationIndex); + } + + public bool TryGetEntityId(int simulationIndex, out int entityId) + { + return _simulationIndexToEntityId.TryGetValue(simulationIndex, out entityId); + } + + public bool UnbindByEntityId(int entityId) + { + if (!_entityIdToSimulationIndex.TryGetValue(entityId, out int simulationIndex)) + { + return false; + } + + _entityIdToSimulationIndex.Remove(entityId); + _simulationIndexToEntityId.Remove(simulationIndex); + return true; + } + + public bool UnbindBySimulationIndex(int simulationIndex) + { + if (!_simulationIndexToEntityId.TryGetValue(simulationIndex, out int entityId)) + { + return false; + } + + _simulationIndexToEntityId.Remove(simulationIndex); + _entityIdToSimulationIndex.Remove(entityId); + return true; + } + + public void Clear() + { + _entityIdToSimulationIndex.Clear(); + _simulationIndexToEntityId.Clear(); + } + } +} diff --git a/Assets/GameMain/Scripts/Simulation/EntityBinding.cs.meta b/Assets/GameMain/Scripts/Simulation/EntityBinding.cs.meta new file mode 100644 index 0000000..b3aafca --- /dev/null +++ b/Assets/GameMain/Scripts/Simulation/EntityBinding.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c4f6d93561db4b9092ab61182f2983d7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Simulation/PickupSimData.cs b/Assets/GameMain/Scripts/Simulation/PickupSimData.cs new file mode 100644 index 0000000..61dbf77 --- /dev/null +++ b/Assets/GameMain/Scripts/Simulation/PickupSimData.cs @@ -0,0 +1,12 @@ +using UnityEngine; + +namespace Simulation +{ + public struct PickupSimData + { + public int EntityId; + public Vector3 Position; + public float PickupRadius; + public int State; + } +} diff --git a/Assets/GameMain/Scripts/Simulation/PickupSimData.cs.meta b/Assets/GameMain/Scripts/Simulation/PickupSimData.cs.meta new file mode 100644 index 0000000..695d645 --- /dev/null +++ b/Assets/GameMain/Scripts/Simulation/PickupSimData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ff6e8b75e4af4c3ca9e3b732b1383f95 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Simulation/ProjectileSimData.cs b/Assets/GameMain/Scripts/Simulation/ProjectileSimData.cs new file mode 100644 index 0000000..fa23db6 --- /dev/null +++ b/Assets/GameMain/Scripts/Simulation/ProjectileSimData.cs @@ -0,0 +1,15 @@ +using UnityEngine; + +namespace Simulation +{ + public struct ProjectileSimData + { + public int EntityId; + public int OwnerEntityId; + public Vector3 Position; + public Vector3 Forward; + public float Speed; + public float RemainingLifetime; + public int State; + } +} diff --git a/Assets/GameMain/Scripts/Simulation/ProjectileSimData.cs.meta b/Assets/GameMain/Scripts/Simulation/ProjectileSimData.cs.meta new file mode 100644 index 0000000..54e9a43 --- /dev/null +++ b/Assets/GameMain/Scripts/Simulation/ProjectileSimData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2eec1f4389004d69bdce8c4dd95d255e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Simulation/SimulationTickContext.cs b/Assets/GameMain/Scripts/Simulation/SimulationTickContext.cs new file mode 100644 index 0000000..2428dcd --- /dev/null +++ b/Assets/GameMain/Scripts/Simulation/SimulationTickContext.cs @@ -0,0 +1,18 @@ +using UnityEngine; + +namespace Simulation +{ + public readonly struct SimulationTickContext + { + public SimulationTickContext(float deltaTime, float realDeltaTime, Vector3 playerPosition) + { + DeltaTime = deltaTime; + RealDeltaTime = realDeltaTime; + PlayerPosition = playerPosition; + } + + public float DeltaTime { get; } + public float RealDeltaTime { get; } + public Vector3 PlayerPosition { get; } + } +} diff --git a/Assets/GameMain/Scripts/Simulation/SimulationTickContext.cs.meta b/Assets/GameMain/Scripts/Simulation/SimulationTickContext.cs.meta new file mode 100644 index 0000000..6516687 --- /dev/null +++ b/Assets/GameMain/Scripts/Simulation/SimulationTickContext.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c4c57fcf285f488b821c9141a6d0ad09 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Simulation/SimulationWorld.cs b/Assets/GameMain/Scripts/Simulation/SimulationWorld.cs new file mode 100644 index 0000000..856d916 --- /dev/null +++ b/Assets/GameMain/Scripts/Simulation/SimulationWorld.cs @@ -0,0 +1,119 @@ +using System.Collections.Generic; + +namespace Simulation +{ + public sealed class SimulationWorld + { + private readonly List _enemies = new List(); + private readonly List _projectiles = new List(); + private readonly List _pickups = new List(); + + public EntityBinding EnemyBinding { get; } = new EntityBinding(); + public EntityBinding ProjectileBinding { get; } = new EntityBinding(); + public EntityBinding PickupBinding { get; } = new EntityBinding(); + + public IReadOnlyList Enemies => _enemies; + public IReadOnlyList Projectiles => _projectiles; + public IReadOnlyList Pickups => _pickups; + + public int AddEnemy(in EnemySimData simData) + { + int simulationIndex = _enemies.Count; + _enemies.Add(simData); + EnemyBinding.Bind(simData.EntityId, simulationIndex); + return simulationIndex; + } + + public bool RemoveEnemyByEntityId(int entityId) + { + if (!EnemyBinding.TryGetSimulationIndex(entityId, out int simulationIndex)) + { + return false; + } + + int lastIndex = _enemies.Count - 1; + if (simulationIndex != lastIndex) + { + EnemySimData movedData = _enemies[lastIndex]; + _enemies[simulationIndex] = movedData; + EnemyBinding.RemapIndex(movedData.EntityId, simulationIndex); + } + + _enemies.RemoveAt(lastIndex); + EnemyBinding.UnbindByEntityId(entityId); + return true; + } + + public int AddProjectile(in ProjectileSimData simData) + { + int simulationIndex = _projectiles.Count; + _projectiles.Add(simData); + ProjectileBinding.Bind(simData.EntityId, simulationIndex); + return simulationIndex; + } + + public bool RemoveProjectileByEntityId(int entityId) + { + if (!ProjectileBinding.TryGetSimulationIndex(entityId, out int simulationIndex)) + { + return false; + } + + int lastIndex = _projectiles.Count - 1; + if (simulationIndex != lastIndex) + { + ProjectileSimData movedData = _projectiles[lastIndex]; + _projectiles[simulationIndex] = movedData; + ProjectileBinding.RemapIndex(movedData.EntityId, simulationIndex); + } + + _projectiles.RemoveAt(lastIndex); + ProjectileBinding.UnbindByEntityId(entityId); + return true; + } + + public int AddPickup(in PickupSimData simData) + { + int simulationIndex = _pickups.Count; + _pickups.Add(simData); + PickupBinding.Bind(simData.EntityId, simulationIndex); + return simulationIndex; + } + + public bool RemovePickupByEntityId(int entityId) + { + if (!PickupBinding.TryGetSimulationIndex(entityId, out int simulationIndex)) + { + return false; + } + + int lastIndex = _pickups.Count - 1; + if (simulationIndex != lastIndex) + { + PickupSimData movedData = _pickups[lastIndex]; + _pickups[simulationIndex] = movedData; + PickupBinding.RemapIndex(movedData.EntityId, simulationIndex); + } + + _pickups.RemoveAt(lastIndex); + PickupBinding.UnbindByEntityId(entityId); + return true; + } + + public void Tick(in SimulationTickContext context) + { + _ = context; + } + + public void Clear() + { + _enemies.Clear(); + _projectiles.Clear(); + _pickups.Clear(); + + EnemyBinding.Clear(); + ProjectileBinding.Clear(); + PickupBinding.Clear(); + } + } +} diff --git a/Assets/GameMain/Scripts/Simulation/SimulationWorld.cs.meta b/Assets/GameMain/Scripts/Simulation/SimulationWorld.cs.meta new file mode 100644 index 0000000..0b8861a --- /dev/null +++ b/Assets/GameMain/Scripts/Simulation/SimulationWorld.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8a558ebbc9cb4d94946ac9f4f27914d8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/docs/TodoList.md b/docs/TodoList.md index 1e8b3ef..b6e8885 100644 --- a/docs/TodoList.md +++ b/docs/TodoList.md @@ -25,7 +25,7 @@ - 以上问题修正后,核心流程可稳定连续跑 10 分钟无异常日志。 ## 2. P1 Simulation 分层(为 Job/Burst 做结构准备) -- [ ] Checkpoint 1:搭建 Simulation 基础骨架(仅新增,不改行为) +- [x] Checkpoint 1:搭建 Simulation 基础骨架(仅新增,不改行为) - 新建目录:`Assets/GameMain/Scripts/Simulation`。 - 新建 `SimulationWorld`,统一持有 `EnemySimData / ProjectileSimData / PickupSimData` 容器。 - 新建 `EntityBinding`,维护 `EntityId <-> SimulationIndex` 双向映射。