From 6494ebc5fdfa29ebe61b233120f01060e39d9ca8 Mon Sep 17 00:00:00 2001 From: SepComet <202308010230@stu.csust.edu.cn> Date: Fri, 20 Feb 2026 19:54:44 +0800 Subject: [PATCH] =?UTF-8?q?Checkpoint=203=20&=20Checkpoint=204=EF=BC=9A=20?= =?UTF-8?q?-=20GameStateBattle=20=E6=8E=A5=E5=85=A5=20Simulation=20?= =?UTF-8?q?=E4=B8=BB=E6=9B=B4=E6=96=B0=E5=85=A5=E5=8F=A3=20-=20SimulationW?= =?UTF-8?q?orld=20=E5=A2=9E=E5=8A=A0=E5=BC=80=E5=85=B3=20UseSimulationMove?= =?UTF-8?q?ment=EF=BC=88=E9=BB=98=E8=AE=A4=20false=EF=BC=89=20-=20Simulati?= =?UTF-8?q?onWorld.Tick(...)=20=E7=8E=B0=E5=9C=A8=E5=9C=A8=E5=BC=80?= =?UTF-8?q?=E5=85=B3=E5=BC=80=E5=90=AF=E6=97=B6=E6=89=A7=E8=A1=8C=E6=95=8C?= =?UTF-8?q?=E4=BA=BA=E8=BF=BD=E8=B8=AA/=E7=A7=BB=E5=8A=A8=E6=A8=A1?= =?UTF-8?q?=E6=8B=9F=EF=BC=8C=E5=9F=BA=E6=9C=AC=E8=BF=98=E5=8E=9F=20Moveme?= =?UTF-8?q?ntComponent=20=E5=8A=9F=E8=83=BD=EF=BC=88=E6=95=8C=E4=BA=BA?= =?UTF-8?q?=E4=BA=92=E6=96=A5=EF=BC=89=20-=20=E8=B0=83=E6=95=B4=20IEnemySe?= =?UTF-8?q?parationSolver=20=E7=B3=BB=E5=88=97=E6=96=B9=E6=B3=95=EF=BC=8C?= =?UTF-8?q?=E4=B8=8D=E5=86=8D=E4=BE=9D=E8=B5=96=20MovementComponent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/GameMain/Entities/MeleeEnemy.prefab | 5 +- Assets/GameMain/Entities/Player.prefab | 4 +- .../Scripts/Components/MovementComponent.cs | 15 ++- .../EnemyManager/EnemyManagerComponent.cs | 6 + .../Entity/EntityLogic/Enemy/EnemyBase.cs | 8 +- .../Entity/EntityLogic/Enemy/MeleeEnemy.cs | 5 + .../Entity/EntityLogic/Enemy/RemoteEnemy.cs | 5 + .../Scripts/Procedure/Game/GameStateBattle.cs | 18 ++- .../Scripts/Simulation/EnemySimData.cs | 3 + .../Scripts/Simulation/SimulationWorld.cs | 109 +++++++++++++++++- .../EnemySeparationSolverProvider.cs | 31 +++-- .../GridBucketEnemySeparationSolver.cs | 63 +++++----- .../EnemySeperator/IEnemySeparationSolver.cs | 9 +- .../NaiveEnemySeparationSolver.cs | 31 +++-- docs/TodoList.md | 4 +- 15 files changed, 229 insertions(+), 87 deletions(-) diff --git a/Assets/GameMain/Entities/MeleeEnemy.prefab b/Assets/GameMain/Entities/MeleeEnemy.prefab index 4c3a5ed..6099332 100644 --- a/Assets/GameMain/Entities/MeleeEnemy.prefab +++ b/Assets/GameMain/Entities/MeleeEnemy.prefab @@ -151,7 +151,10 @@ MonoBehaviour: m_EditorClassIdentifier: _isMoving: 0 _direction: {x: 0, y: 0, z: 0} - _cachedTransform: {fileID: 0} + _cachedTransform: {fileID: 7683855655592166216} + _avoidEnemyOverlap: 0 + _enemyBodyRadius: 0.45 + _separationIterations: 2 _speedBase: 0 --- !u!114 &6353753365317756414 MonoBehaviour: diff --git a/Assets/GameMain/Entities/Player.prefab b/Assets/GameMain/Entities/Player.prefab index 1b69932..bc58f51 100644 --- a/Assets/GameMain/Entities/Player.prefab +++ b/Assets/GameMain/Entities/Player.prefab @@ -191,8 +191,8 @@ Camera: near clip plane: 0.3 far clip plane: 100 field of view: 80 - orthographic: 0 - orthographic size: 5 + orthographic: 1 + orthographic size: 15 m_Depth: 0 m_CullingMask: serializedVersion: 2 diff --git a/Assets/GameMain/Scripts/Components/MovementComponent.cs b/Assets/GameMain/Scripts/Components/MovementComponent.cs index 51c618c..2fc3696 100644 --- a/Assets/GameMain/Scripts/Components/MovementComponent.cs +++ b/Assets/GameMain/Scripts/Components/MovementComponent.cs @@ -18,6 +18,9 @@ namespace Components [SerializeField] private int _separationIterations = 2; public float Speed => (_speedBase + _movementStat.Value) * _movementStat.Percent; + public bool AvoidEnemyOverlap => _avoidEnemyOverlap; + public float EnemyBodyRadius => _enemyBodyRadius; + public int SeparationIterations => _separationIterations; [SerializeField] private float _speedBase; private StatComponent _statComponent; @@ -61,6 +64,8 @@ namespace Components public void OnReset() { + Transform transformToUnregister = _cachedTransform; + _speedBase = 0; _cachedTransform = null; _direction = Vector3.zero; @@ -77,7 +82,7 @@ namespace Components _statComponent = null; - UnregisterEnemyMover(); + UnregisterEnemyMover(transformToUnregister); } private void Move(float deltaTime = 0) @@ -91,7 +96,7 @@ namespace Components if (_avoidEnemyOverlap) { nextPosition = EnemySeparationSolverProvider.Resolve( - this, + _cachedTransform, nextPosition, _direction, _separationIterations); @@ -108,12 +113,12 @@ namespace Components { UnregisterEnemyMover(); if (!_avoidEnemyOverlap) return; - EnemySeparationSolverProvider.Register(this, _cachedTransform, _enemyBodyRadius); + EnemySeparationSolverProvider.Register(_cachedTransform, _enemyBodyRadius); } - private void UnregisterEnemyMover() + private void UnregisterEnemyMover(Transform transform = null) { - EnemySeparationSolverProvider.Unregister(this); + EnemySeparationSolverProvider.Unregister(transform ?? _cachedTransform); } } } diff --git a/Assets/GameMain/Scripts/CustomComponent/EnemyManager/EnemyManagerComponent.cs b/Assets/GameMain/Scripts/CustomComponent/EnemyManager/EnemyManagerComponent.cs index ac2e413..f917d59 100644 --- a/Assets/GameMain/Scripts/CustomComponent/EnemyManager/EnemyManagerComponent.cs +++ b/Assets/GameMain/Scripts/CustomComponent/EnemyManager/EnemyManagerComponent.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Components; using DataTable; using Definition.Enum; using Entity; @@ -252,6 +253,8 @@ namespace CustomComponent private static EnemySimData CreateEnemySimData(EnemyBase enemy, EnemyData enemyData) { + MovementComponent movementComponent = enemy != null ? enemy.GetComponent() : null; + return new EnemySimData { EntityId = enemy.Id, @@ -259,6 +262,9 @@ namespace CustomComponent Forward = enemy.CachedTransform.forward, Speed = enemyData != null ? enemyData.SpeedBase : 0f, AttackRange = 1f, + AvoidEnemyOverlap = movementComponent != null && movementComponent.AvoidEnemyOverlap, + EnemyBodyRadius = movementComponent != null ? movementComponent.EnemyBodyRadius : 0.45f, + SeparationIterations = movementComponent != null ? movementComponent.SeparationIterations : 2, TargetType = 0, State = 0 }; diff --git a/Assets/GameMain/Scripts/Entity/EntityLogic/Enemy/EnemyBase.cs b/Assets/GameMain/Scripts/Entity/EntityLogic/Enemy/EnemyBase.cs index a835d96..f7dadeb 100644 --- a/Assets/GameMain/Scripts/Entity/EntityLogic/Enemy/EnemyBase.cs +++ b/Assets/GameMain/Scripts/Entity/EntityLogic/Enemy/EnemyBase.cs @@ -9,4 +9,10 @@ public abstract class EnemyBase : TargetableObject public abstract override ImpactData GetImpactData(); public virtual void SetTarget(Transform target) => _target = target; -} \ No newline at end of file + + protected bool IsSimulationMovementEnabled() + { + var simulationWorld = GameEntry.SimulationWorld; + return simulationWorld != null && simulationWorld.UseSimulationMovement; + } +} diff --git a/Assets/GameMain/Scripts/Entity/EntityLogic/Enemy/MeleeEnemy.cs b/Assets/GameMain/Scripts/Entity/EntityLogic/Enemy/MeleeEnemy.cs index e85e709..948599f 100644 --- a/Assets/GameMain/Scripts/Entity/EntityLogic/Enemy/MeleeEnemy.cs +++ b/Assets/GameMain/Scripts/Entity/EntityLogic/Enemy/MeleeEnemy.cs @@ -54,6 +54,11 @@ namespace Entity protected override void OnUpdate(float elapseSeconds, float realElapseSeconds) { + if (IsSimulationMovementEnabled()) + { + return; + } + base.OnUpdate(elapseSeconds, realElapseSeconds); if (_target == null) diff --git a/Assets/GameMain/Scripts/Entity/EntityLogic/Enemy/RemoteEnemy.cs b/Assets/GameMain/Scripts/Entity/EntityLogic/Enemy/RemoteEnemy.cs index b3472e6..f0fdf07 100644 --- a/Assets/GameMain/Scripts/Entity/EntityLogic/Enemy/RemoteEnemy.cs +++ b/Assets/GameMain/Scripts/Entity/EntityLogic/Enemy/RemoteEnemy.cs @@ -50,6 +50,11 @@ namespace Entity protected override void OnUpdate(float elapseSeconds, float realElapseSeconds) { + if (IsSimulationMovementEnabled()) + { + return; + } + base.OnUpdate(elapseSeconds, realElapseSeconds); if (_target == null) diff --git a/Assets/GameMain/Scripts/Procedure/Game/GameStateBattle.cs b/Assets/GameMain/Scripts/Procedure/Game/GameStateBattle.cs index 89a68c0..e7785b3 100644 --- a/Assets/GameMain/Scripts/Procedure/Game/GameStateBattle.cs +++ b/Assets/GameMain/Scripts/Procedure/Game/GameStateBattle.cs @@ -5,7 +5,8 @@ using DataTable; using Entity; using GameFramework.Fsm; using GameFramework.Procedure; -using UnityGameFramework.Runtime; +using Simulation; +using UnityEngine; namespace Procedure { @@ -13,15 +14,15 @@ namespace Procedure { public override GameStateType GameStateType => GameStateType.Battle; - private EnemyManagerComponent _enemyManager = null; + private EnemyManagerComponent _enemyManager; - private int _currentLevel = 0; + private int _currentLevel; - private float _levelTimeLeft = 0; + private float _levelTimeLeft; private Player Player => _procedureGame.Player; - private ProcedureGame _procedureGame = null; + private ProcedureGame _procedureGame; public void AddBattleDuration(float seconds) { @@ -65,6 +66,13 @@ namespace Procedure _enemyManager.OnUpdate(elapseSeconds, realElapseSeconds); + SimulationWorld simulationWorld = GameEntry.SimulationWorld; + if (simulationWorld != null) + { + Vector3 playerPosition = Player != null ? Player.CachedTransform.position : Vector3.zero; + simulationWorld.Tick(new SimulationTickContext(elapseSeconds, realElapseSeconds, playerPosition)); + } + _levelTimeLeft -= elapseSeconds; GameEntry.Event.Fire(this, LevelProcessEventArgs.Create((int)_levelTimeLeft)); } diff --git a/Assets/GameMain/Scripts/Simulation/EnemySimData.cs b/Assets/GameMain/Scripts/Simulation/EnemySimData.cs index 165074b..8d0d1e2 100644 --- a/Assets/GameMain/Scripts/Simulation/EnemySimData.cs +++ b/Assets/GameMain/Scripts/Simulation/EnemySimData.cs @@ -9,6 +9,9 @@ namespace Simulation public Vector3 Forward; public float Speed; public float AttackRange; + public bool AvoidEnemyOverlap; + public float EnemyBodyRadius; + public int SeparationIterations; public int TargetType; public int State; } diff --git a/Assets/GameMain/Scripts/Simulation/SimulationWorld.cs b/Assets/GameMain/Scripts/Simulation/SimulationWorld.cs index 3c0fb1a..86c6f34 100644 --- a/Assets/GameMain/Scripts/Simulation/SimulationWorld.cs +++ b/Assets/GameMain/Scripts/Simulation/SimulationWorld.cs @@ -1,10 +1,20 @@ using System.Collections.Generic; +using CustomUtility; +using Entity; +using UnityEngine; using UnityGameFramework.Runtime; namespace Simulation { public sealed class SimulationWorld : GameFrameworkComponent { + private const float DefaultAttackRange = 1f; + private const int EnemyStateIdle = 0; + private const int EnemyStateChasing = 1; + private const int EnemyStateInAttackRange = 2; + + [SerializeField] private bool _useSimulationMovement; + private readonly List _enemies = new List(); private readonly List _projectiles = new List(); private readonly List _pickups = new List(); @@ -16,6 +26,12 @@ namespace Simulation public IReadOnlyList Enemies => _enemies; public IReadOnlyList Projectiles => _projectiles; public IReadOnlyList Pickups => _pickups; + public bool UseSimulationMovement => _useSimulationMovement; + + public void SetUseSimulationMovement(bool enabled) + { + _useSimulationMovement = enabled; + } public int AddEnemy(in EnemySimData simData) { @@ -136,7 +152,12 @@ namespace Simulation public void Tick(in SimulationTickContext context) { - _ = context; + if (!_useSimulationMovement) + { + return; + } + + TickEnemies(in context); } public void Clear() @@ -149,5 +170,91 @@ namespace Simulation ProjectileBinding.Clear(); PickupBinding.Clear(); } + + private void TickEnemies(in SimulationTickContext context) + { + if (_enemies.Count == 0 || context.DeltaTime <= 0f) + { + return; + } + + Vector3 playerPosition = context.PlayerPosition; + playerPosition.y = 0f; + EntityComponent entityComponent = GameEntry.Entity; + + for (int i = 0; i < _enemies.Count; i++) + { + EnemySimData enemy = _enemies[i]; + EnemyBase enemyEntity = null; + Transform enemyTransform = null; + + if (entityComponent != null && + entityComponent.GetGameEntity(enemy.EntityId) is EnemyBase runtimeEnemy && + runtimeEnemy.Available) + { + enemyEntity = runtimeEnemy; + enemyTransform = runtimeEnemy.CachedTransform; + } + + Vector3 currentPosition = enemy.Position; + currentPosition.y = 0f; + + Vector3 toPlayer = playerPosition - currentPosition; + float sqrDistance = toPlayer.sqrMagnitude; + float attackRange = enemy.AttackRange > 0f ? enemy.AttackRange : DefaultAttackRange; + float attackRangeSqr = attackRange * attackRange; + + if (sqrDistance <= attackRangeSqr) + { + enemy.State = EnemyStateInAttackRange; + } + else if (enemy.Speed <= 0f || sqrDistance <= float.Epsilon) + { + enemy.State = EnemyStateIdle; + } + else + { + Vector3 forward = toPlayer.normalized; + enemy.Forward = forward; + Vector3 desiredPosition = enemy.Position + forward * enemy.Speed * context.DeltaTime; + if (enemy.AvoidEnemyOverlap && enemyTransform != null) + { + int separationIterations = enemy.SeparationIterations > 0 ? enemy.SeparationIterations : 1; + desiredPosition = EnemySeparationSolverProvider.Resolve( + enemyTransform, + desiredPosition, + forward, + separationIterations); + } + + enemy.Position = desiredPosition; + enemy.State = EnemyStateChasing; + } + + _enemies[i] = enemy; + + if (enemyEntity != null) + { + ApplyEnemyPresentation(enemyEntity, enemy); + } + } + } + + private static void ApplyEnemyPresentation(EnemyBase enemyEntity, in EnemySimData enemyData) + { + if (enemyEntity == null || !enemyEntity.Available) + { + return; + } + + enemyEntity.CachedTransform.position = enemyData.Position; + + Vector3 forward = enemyData.Forward; + forward.y = 0f; + if (forward.sqrMagnitude > float.Epsilon) + { + enemyEntity.CachedTransform.forward = forward.normalized; + } + } } } diff --git a/Assets/GameMain/Scripts/Utility/EnemySeperator/EnemySeparationSolverProvider.cs b/Assets/GameMain/Scripts/Utility/EnemySeperator/EnemySeparationSolverProvider.cs index 67f160a..619b08b 100644 --- a/Assets/GameMain/Scripts/Utility/EnemySeperator/EnemySeparationSolverProvider.cs +++ b/Assets/GameMain/Scripts/Utility/EnemySeperator/EnemySeparationSolverProvider.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; -using Components; using UnityEngine; namespace CustomUtility @@ -9,12 +8,11 @@ namespace CustomUtility { private struct Registration { - public Transform Transform; public float BodyRadius; } private static IEnemySeparationSolver _current = new GridBucketEnemySeparationSolver(); - private static readonly Dictionary Registrations = new(); + private static readonly Dictionary Registrations = new(); public static IEnemySeparationSolver Current => _current; public static string CurrentSolverName => _current.GetType().Name; @@ -36,42 +34,41 @@ namespace CustomUtility SetSolver(new NaiveEnemySeparationSolver()); } - public static void Register(MovementComponent mover, Transform transform, float bodyRadius) + public static void Register(Transform transform, float bodyRadius) { - if (mover == null || transform == null) return; + if (transform == null) return; var registration = new Registration { - Transform = transform, BodyRadius = bodyRadius }; - Registrations[mover] = registration; - _current.Register(mover, transform, bodyRadius); + Registrations[transform] = registration; + _current.Register(transform, bodyRadius); } - public static void Unregister(MovementComponent mover) + public static void Unregister(Transform transform) { - if (mover == null) return; + if (transform == null) return; - _current.Unregister(mover); - Registrations.Remove(mover); + _current.Unregister(transform); + Registrations.Remove(transform); } - public static Vector3 Resolve(MovementComponent mover, Vector3 desiredPosition, Vector3 fallbackDirection, + public static Vector3 Resolve(Transform transform, Vector3 desiredPosition, Vector3 fallbackDirection, int iterations) { - return _current.Resolve(mover, desiredPosition, fallbackDirection, iterations); + return _current.Resolve(transform, desiredPosition, fallbackDirection, iterations); } private static void ReRegisterAll() { foreach (var pair in Registrations) { - MovementComponent mover = pair.Key; + Transform transform = pair.Key; Registration registration = pair.Value; - if (mover == null || registration.Transform == null) continue; + if (transform == null) continue; - _current.Register(mover, registration.Transform, registration.BodyRadius); + _current.Register(transform, registration.BodyRadius); } } } diff --git a/Assets/GameMain/Scripts/Utility/EnemySeperator/GridBucketEnemySeparationSolver.cs b/Assets/GameMain/Scripts/Utility/EnemySeperator/GridBucketEnemySeparationSolver.cs index a233214..1b66b03 100644 --- a/Assets/GameMain/Scripts/Utility/EnemySeperator/GridBucketEnemySeparationSolver.cs +++ b/Assets/GameMain/Scripts/Utility/EnemySeperator/GridBucketEnemySeparationSolver.cs @@ -1,4 +1,3 @@ -using Components; using UnityEngine; namespace CustomUtility @@ -14,12 +13,12 @@ namespace CustomUtility public int CellZ; } - private readonly System.Collections.Generic.Dictionary _agents = new(); + private readonly System.Collections.Generic.Dictionary _agents = new(); - private readonly System.Collections.Generic.Dictionary> + private readonly System.Collections.Generic.Dictionary> _buckets = new(); - private readonly System.Collections.Generic.List _recycle = new(); + private readonly System.Collections.Generic.List _recycle = new(); private readonly float _cellSize; private int _snapshotFrame = -1; @@ -30,14 +29,14 @@ namespace CustomUtility _cellSize = Mathf.Max(0.1f, cellSize); } - public void Register(MovementComponent mover, Transform transform, float bodyRadius) + public void Register(Transform transform, float bodyRadius) { - if (mover == null || transform == null) return; + if (transform == null) return; - if (!_agents.TryGetValue(mover, out var agent)) + if (!_agents.TryGetValue(transform, out var agent)) { agent = new Agent(); - _agents.Add(mover, agent); + _agents.Add(transform, agent); } agent.Transform = transform; @@ -50,22 +49,22 @@ namespace CustomUtility _snapshotFrame = -1; } - public void Unregister(MovementComponent mover) + public void Unregister(Transform transform) { - if (mover == null) return; - if (!_agents.TryGetValue(mover, out var agent)) return; + if (transform == null) return; + if (!_agents.TryGetValue(transform, out var agent)) return; - RemoveFromBucket(mover, agent.CellX, agent.CellZ); - _agents.Remove(mover); + RemoveFromBucket(transform, agent.CellX, agent.CellZ); + _agents.Remove(transform); RecalculateMaxRadius(); _snapshotFrame = -1; } - public Vector3 Resolve(MovementComponent mover, Vector3 desiredPosition, Vector3 fallbackDirection, + public Vector3 Resolve(Transform transform, Vector3 desiredPosition, Vector3 fallbackDirection, int iterations) { - if (mover == null) return desiredPosition; - if (!_agents.TryGetValue(mover, out var self)) return desiredPosition; + if (transform == null) return desiredPosition; + if (!_agents.TryGetValue(transform, out var self)) return desiredPosition; EnsureSnapshot(); @@ -90,9 +89,9 @@ namespace CustomUtility for (int i = 0; i < bucket.Count; i++) { - MovementComponent otherMover = bucket[i]; - if (otherMover == mover) continue; - if (!_agents.TryGetValue(otherMover, out var other)) continue; + Transform otherTransform = bucket[i]; + if (otherTransform == transform) continue; + if (!_agents.TryGetValue(otherTransform, out var other)) continue; Vector3 toSelf = candidate - other.Position; float minDistance = self.Radius + other.Radius; @@ -115,7 +114,7 @@ namespace CustomUtility } } - SyncAgentPosition(mover, self, candidate); + SyncAgentPosition(transform, self, candidate); candidate.y = desiredPosition.y; return candidate; @@ -132,11 +131,11 @@ namespace CustomUtility foreach (var pair in _agents) { - MovementComponent mover = pair.Key; + Transform transform = pair.Key; Agent agent = pair.Value; - if (mover == null || agent.Transform == null) + if (transform == null || agent.Transform == null) { - _recycle.Add(mover); + _recycle.Add(transform); continue; } @@ -146,7 +145,7 @@ namespace CustomUtility agent.CellX = ToCell(position.x); agent.CellZ = ToCell(position.z); - AddToBucket(mover, agent.CellX, agent.CellZ); + AddToBucket(transform, agent.CellX, agent.CellZ); } for (int i = 0; i < _recycle.Count; i++) @@ -155,15 +154,15 @@ namespace CustomUtility } } - private void SyncAgentPosition(MovementComponent mover, Agent agent, Vector3 position) + private void SyncAgentPosition(Transform transform, Agent agent, Vector3 position) { int newCellX = ToCell(position.x); int newCellZ = ToCell(position.z); if (agent.CellX != newCellX || agent.CellZ != newCellZ) { - RemoveFromBucket(mover, agent.CellX, agent.CellZ); - AddToBucket(mover, newCellX, newCellZ); + RemoveFromBucket(transform, agent.CellX, agent.CellZ); + AddToBucket(transform, newCellX, newCellZ); agent.CellX = newCellX; agent.CellZ = newCellZ; } @@ -171,24 +170,24 @@ namespace CustomUtility agent.Position = position; } - private void AddToBucket(MovementComponent mover, int cellX, int cellZ) + private void AddToBucket(Transform transform, int cellX, int cellZ) { long key = CellKey(cellX, cellZ); if (!_buckets.TryGetValue(key, out var list)) { - list = new System.Collections.Generic.List(8); + list = new System.Collections.Generic.List(8); _buckets.Add(key, list); } - list.Add(mover); + list.Add(transform); } - private void RemoveFromBucket(MovementComponent mover, int cellX, int cellZ) + private void RemoveFromBucket(Transform transform, int cellX, int cellZ) { long key = CellKey(cellX, cellZ); if (!_buckets.TryGetValue(key, out var list)) return; - list.Remove(mover); + list.Remove(transform); if (list.Count == 0) { _buckets.Remove(key); diff --git a/Assets/GameMain/Scripts/Utility/EnemySeperator/IEnemySeparationSolver.cs b/Assets/GameMain/Scripts/Utility/EnemySeperator/IEnemySeparationSolver.cs index 3e8ec6e..a304a49 100644 --- a/Assets/GameMain/Scripts/Utility/EnemySeperator/IEnemySeparationSolver.cs +++ b/Assets/GameMain/Scripts/Utility/EnemySeperator/IEnemySeparationSolver.cs @@ -1,12 +1,11 @@ -using Components; using UnityEngine; namespace CustomUtility { public interface IEnemySeparationSolver { - void Register(MovementComponent mover, Transform transform, float bodyRadius); - void Unregister(MovementComponent mover); - Vector3 Resolve(MovementComponent mover, Vector3 desiredPosition, Vector3 fallbackDirection, int iterations); + void Register(Transform transform, float bodyRadius); + void Unregister(Transform transform); + Vector3 Resolve(Transform transform, Vector3 desiredPosition, Vector3 fallbackDirection, int iterations); } -} \ No newline at end of file +} diff --git a/Assets/GameMain/Scripts/Utility/EnemySeperator/NaiveEnemySeparationSolver.cs b/Assets/GameMain/Scripts/Utility/EnemySeperator/NaiveEnemySeparationSolver.cs index 466f878..63e9256 100644 --- a/Assets/GameMain/Scripts/Utility/EnemySeperator/NaiveEnemySeparationSolver.cs +++ b/Assets/GameMain/Scripts/Utility/EnemySeperator/NaiveEnemySeparationSolver.cs @@ -1,4 +1,3 @@ -using Components; using UnityEngine; namespace CustomUtility @@ -11,33 +10,33 @@ namespace CustomUtility public float Radius; } - private readonly System.Collections.Generic.Dictionary _agents = new(); - private readonly System.Collections.Generic.List _agentKeys = new(); + private readonly System.Collections.Generic.Dictionary _agents = new(); + private readonly System.Collections.Generic.List _agentKeys = new(); - public void Register(MovementComponent mover, Transform transform, float bodyRadius) + public void Register(Transform transform, float bodyRadius) { - if (mover == null || transform == null) return; + if (transform == null) return; - if (!_agents.TryGetValue(mover, out var agent)) + if (!_agents.TryGetValue(transform, out var agent)) { agent = new Agent(); - _agents.Add(mover, agent); + _agents.Add(transform, agent); } agent.Transform = transform; agent.Radius = Mathf.Max(0.01f, bodyRadius); } - public void Unregister(MovementComponent mover) + public void Unregister(Transform transform) { - if (mover == null) return; - _agents.Remove(mover); + if (transform == null) return; + _agents.Remove(transform); } - public Vector3 Resolve(MovementComponent mover, Vector3 desiredPosition, Vector3 fallbackDirection, int iterations) + public Vector3 Resolve(Transform transform, Vector3 desiredPosition, Vector3 fallbackDirection, int iterations) { - if (mover == null) return desiredPosition; - if (!_agents.TryGetValue(mover, out var self)) return desiredPosition; + if (transform == null) return desiredPosition; + if (!_agents.TryGetValue(transform, out var self)) return desiredPosition; Vector3 candidate = desiredPosition; candidate.y = 0f; @@ -56,9 +55,9 @@ namespace CustomUtility { for (int i = 0; i < _agentKeys.Count; i++) { - MovementComponent otherMover = _agentKeys[i]; - if (otherMover == mover) continue; - if (!_agents.TryGetValue(otherMover, out var other)) continue; + Transform otherTransform = _agentKeys[i]; + if (otherTransform == transform) continue; + if (!_agents.TryGetValue(otherTransform, out var other)) continue; if (other.Transform == null) continue; Vector3 otherPosition = other.Transform.position; diff --git a/docs/TodoList.md b/docs/TodoList.md index 387be5d..f5c5570 100644 --- a/docs/TodoList.md +++ b/docs/TodoList.md @@ -37,13 +37,13 @@ - `EnemyManagerComponent` 继续负责刷怪与实体显隐,不改外部调用方式。 - 完成标准:敌人数量统计与当前一致,无重复注册、无悬空索引。 -- [ ] Checkpoint 3:建立 Simulation 主更新入口并接入 Battle 状态 +- [x] Checkpoint 3:建立 Simulation 主更新入口并接入 Battle 状态 - 在 `GameStateBattle.OnUpdate` 中增加 `SimulationWorld.Tick(...)` 调用。 - 先只接“敌人移动/追踪”系统,其他逻辑保持原路径。 - 增加开关(建议 `UseSimulationMovement`)用于 A/B 对比与回滚。 - 完成标准:关闭开关与当前行为一致;开启开关后敌人仍能正常追踪玩家。 -- [ ] Checkpoint 4:迁移敌人核心移动逻辑到 Simulation(去 MonoBehaviour 核心逻辑) +- [x] Checkpoint 4:迁移敌人核心移动逻辑到 Simulation(去 MonoBehaviour 核心逻辑) - 将 `MeleeEnemy/RemoteEnemy` 的目标追踪、移动方向、攻击距离判定迁至 Simulation。 - `EnemySimData` 至少包含:`position`、`forward`、`speed`、`attackRange`、`targetType`、`state`。 - `MeleeEnemy/RemoteEnemy.OnUpdate` 仅保留表现层或空实现(不再做核心移动计算)。