Cleanup 1

This commit is contained in:
SepComet
2026-04-02 11:21:07 +08:00
parent 5ed792b09f
commit 1052cc0136
26 changed files with 600 additions and 2057 deletions
@@ -2,6 +2,8 @@ using System;
using CustomUtility;
using Definition.DataStruct;
using Definition.Enum;
using Entity;
using Simulation;
using UnityEngine;
using CustomDebugger;
@@ -20,6 +22,9 @@ namespace Components
public bool AvoidEnemyOverlap => _avoidEnemyOverlap;
public float EnemyBodyRadius => _enemyBodyRadius;
public int SeparationIterations => _separationIterations;
public bool IsMoving => _isMoving;
public Vector3 Direction => _direction;
public Transform CachedTransform => _cachedTransform;
[SerializeField] private float _speedBase;
private StatComponent _statComponent;
@@ -42,7 +47,10 @@ namespace Components
{
_movementStat = _statComponent.GetStat(StatType.MovementSpeed);
_movementStatCallback = (modifier, isApply) =>
{
_statComponent.UpdateStat(_movementStat, modifier, isApply);
SyncToSimulationWorld();
};
_statComponent.Subscribe(StatType.MovementSpeed, _movementStatCallback);
}
else
@@ -50,15 +58,12 @@ namespace Components
_movementStat = new StatProperty();
}
RefreshEnemyRegistration();
SyncToSimulationWorld();
}
public void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
if (_isMoving && _cachedTransform != null)
{
Move(elapseSeconds);
}
SyncToSimulationWorld();
}
public void OnReset()
@@ -81,43 +86,59 @@ namespace Components
_statComponent = null;
UnregisterEnemyMover(transformToUnregister);
}
private void Move(float deltaTime = 0)
{
using (CustomProfilerMarker.Movement_Update.Auto())
if (transformToUnregister != null)
{
if (_cachedTransform == null) return;
Vector3 displacement = Speed * deltaTime * _direction;
Vector3 nextPosition = _cachedTransform.position + displacement;
if (_avoidEnemyOverlap)
{
nextPosition = EnemySeparationSolverProvider.Resolve(
_cachedTransform,
nextPosition,
_direction,
_separationIterations);
}
_cachedTransform.position = nextPosition;
var simulationWorld = GameEntry.SimulationWorld;
simulationWorld?.UnregisterPlayerMovement(transformToUnregister);
}
}
public void SetMove(bool isMoving) => _isMoving = isMoving;
public void SetDirection(Vector3 direction) => _direction = direction;
private void RefreshEnemyRegistration()
public void SetMove(bool isMoving)
{
UnregisterEnemyMover();
if (!_avoidEnemyOverlap) return;
EnemySeparationSolverProvider.Register(_cachedTransform, _enemyBodyRadius);
_isMoving = isMoving;
SyncToSimulationWorld();
}
private void UnregisterEnemyMover(Transform transform = null)
public void SetDirection(Vector3 direction)
{
EnemySeparationSolverProvider.Unregister(transform ?? _cachedTransform);
_direction = direction;
SyncToSimulationWorld();
}
private void SyncToSimulationWorld()
{
using (CustomProfilerMarker.Movement_Update.Auto())
{
if (_cachedTransform == null)
{
return;
}
SimulationWorld simulationWorld = GameEntry.SimulationWorld;
if (simulationWorld == null)
{
return;
}
Vector3 direction = _direction;
direction.y = 0f;
if (direction.sqrMagnitude > Mathf.Epsilon)
{
direction.Normalize();
}
if (TryGetComponent(out Player _))
{
simulationWorld.SyncPlayerMovementInput(_cachedTransform, _isMoving, direction, Speed);
return;
}
if (TryGetComponent(out EnemyBase enemy))
{
simulationWorld.SyncEnemyMovementInput(enemy.Id, _isMoving, direction, Speed,
_avoidEnemyOverlap, _enemyBodyRadius, _separationIterations);
}
}
}
}
}
@@ -29,7 +29,6 @@ namespace CustomComponent
[SerializeField] private bool _showCollisionStats = true;
[SerializeField] private bool _showSpawnControls = true;
[SerializeField] private bool _showBattleDurationControls = true;
[SerializeField] private bool _showSeparationSolverControls = true;
[SerializeField] private bool _showPlayerWeaponControls = true;
[SerializeField] private bool _showPlayerHealthControls = true;
[SerializeField] private bool _showTips = true;
@@ -283,24 +282,6 @@ namespace CustomComponent
GUILayout.EndHorizontal();
}
if (_showSeparationSolverControls)
{
GUILayout.Space(4f);
GUILayout.Label($"Enemy Separation Solver: {EnemySeparationSolverProvider.CurrentSolverName}");
GUILayout.BeginHorizontal();
if (GUILayout.Button("Use Naive O(N^2)", GUILayout.Height(24f)))
{
EnemySeparationSolverProvider.UseNaiveSolver();
}
if (GUILayout.Button("Use Grid Bucket", GUILayout.Height(24f)))
{
EnemySeparationSolverProvider.UseGridBucketSolver();
}
GUILayout.EndHorizontal();
}
if (_showPlayerWeaponControls)
{
GUILayout.Label(
@@ -353,7 +334,6 @@ namespace CustomComponent
_showCollisionStats ||
_showSpawnControls ||
_showBattleDurationControls ||
_showSeparationSolverControls ||
_showPlayerWeaponControls ||
_showPlayerHealthControls;
}
@@ -13,7 +13,6 @@ public abstract class EnemyBase : TargetableObject
protected bool IsSimulationMovementEnabled()
{
var simulationWorld = GameEntry.SimulationWorld;
return simulationWorld != null && simulationWorld.UseSimulationMovement;
return true;
}
}
@@ -10,9 +10,7 @@ namespace Entity
{
private EnemyProjectileData _projectileData;
private Vector3 _direction = Vector3.forward;
private float _elapsedTime;
private bool _isActive;
private bool _isSimulationDriven;
private ImpactData _impactData;
private Collider[] _cachedColliders;
@@ -33,7 +31,6 @@ namespace Entity
}
_isActive = true;
_elapsedTime = 0f;
_impactData = new ImpactData(_projectileData.OwnerCamp, _projectileData.AttackDamage);
_direction = _projectileData.Direction;
@@ -64,8 +61,7 @@ namespace Entity
gameObject.layer = LayerMask.NameToLayer("EnemyWeapon");
}
_isSimulationDriven = IsDrivenBySimulationWorld();
SetColliderEnabled(!_isSimulationDriven);
SetColliderEnabled(false);
}
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
@@ -73,34 +69,12 @@ namespace Entity
base.OnUpdate(elapseSeconds, realElapseSeconds);
if (!_isActive || _projectileData == null) return;
bool isSimulationDriven = IsDrivenBySimulationWorld();
if (isSimulationDriven != _isSimulationDriven)
{
_isSimulationDriven = isSimulationDriven;
SetColliderEnabled(!_isSimulationDriven);
}
if (_isSimulationDriven) return;
if (_projectileData.Speed > 0f)
{
CachedTransform.position += _direction * (_projectileData.Speed * elapseSeconds);
}
_elapsedTime += elapseSeconds;
if (_projectileData.LifeTime > 0f && _elapsedTime >= _projectileData.LifeTime)
{
Expire();
}
}
protected override void OnHide(bool isShutdown, object userData)
{
_isActive = false;
_projectileData = null;
_elapsedTime = 0f;
_isSimulationDriven = false;
_impactData = default;
_direction = Vector3.forward;
@@ -114,12 +88,6 @@ namespace Entity
GameEntry.Entity.HideEntity(this);
}
private static bool IsDrivenBySimulationWorld()
{
var simulationWorld = GameEntry.SimulationWorld;
return simulationWorld != null && simulationWorld.UseSimulationMovement;
}
private void SetColliderEnabled(bool enabled)
{
_cachedColliders ??= GetComponentsInChildren<Collider>(true);
@@ -137,4 +105,4 @@ namespace Entity
}
}
}
}
}
@@ -82,13 +82,6 @@ namespace Entity
base.OnUpdate(elapseSeconds, realElapseSeconds);
UpdateAttackState(elapseSeconds);
if (IsSimulationMovementEnabled())
{
return;
}
_movementComponent.OnUpdate(elapseSeconds, realElapseSeconds);
}
protected override void OnDead(EntityBase attacker)
@@ -95,11 +95,6 @@ namespace Entity
if (_target == null)
{
_movementComponent.SetMove(false);
if (!IsSimulationMovementEnabled())
{
_movementComponent.OnUpdate(elapseSeconds, realElapseSeconds);
}
return;
}
@@ -116,11 +111,6 @@ namespace Entity
_movementComponent.SetMove(true);
_movementComponent.SetDirection(GetTargetDirection());
}
if (!IsSimulationMovementEnabled())
{
_movementComponent.OnUpdate(elapseSeconds, realElapseSeconds);
}
}
protected override void OnHide(bool isShutdown, object userData)
@@ -212,7 +212,6 @@ namespace Entity
_inputComponent.OnUpdate(elapseSeconds, realElapseSeconds);
_movementComponent.SetDirection(_inputComponent.Direction);
_movementComponent.OnUpdate(elapseSeconds, realElapseSeconds);
_absorbComponent.OnUpdate(elapseSeconds, realElapseSeconds);
}
@@ -13,26 +13,7 @@ namespace Entity.Weapon
return null;
}
if (TrySelectFromSpatialIndex(weapon, maxSqrRange, out EntityBase indexedTarget))
{
return indexedTarget;
}
EntityBase target = null;
float minSqrMagnitude = maxSqrRange > 0f ? maxSqrRange : float.MaxValue;
foreach (var candidate in candidates)
{
if (candidate == null || !candidate.Available) continue;
float sqrMagnitude = AIUtility.GetSqrMagnitudeXZ(weapon, candidate);
if (sqrMagnitude >= minSqrMagnitude) continue;
minSqrMagnitude = sqrMagnitude;
target = candidate;
}
return target;
return TrySelectFromSpatialIndex(weapon, maxSqrRange, out EntityBase indexedTarget) ? indexedTarget : null;
}
private static bool TrySelectFromSpatialIndex(WeaponBase weapon, float maxSqrRange, out EntityBase target)
@@ -44,7 +25,7 @@ namespace Entity.Weapon
}
var simulationWorld = GameEntry.SimulationWorld;
if (simulationWorld == null || !simulationWorld.UseSimulationMovement)
if (simulationWorld == null)
{
return false;
}
@@ -65,4 +46,4 @@ namespace Entity.Weapon
return true;
}
}
}
}
@@ -36,11 +36,6 @@ namespace Simulation
in Vector3 center, float radius, int maxTargets, int shapeType, in Vector3 direction, float halfAngleDeg,
float halfWidth, float halfLength)
{
if (!_useSimulationMovement)
{
return false;
}
if (sourceEntityId == 0 || radius <= 0f || maxTargets <= 0)
{
return false;
@@ -16,7 +16,7 @@ namespace Simulation
public void OnLateUpdate()
{
if (_world == null || !_world.UseSimulationMovement)
if (_world == null)
{
return;
}
@@ -104,4 +104,4 @@ namespace Simulation
}
}
}
}
}
@@ -0,0 +1,82 @@
using UnityEngine;
namespace Simulation
{
public sealed partial class SimulationWorld
{
private Transform _playerMovementTransform;
private Vector3 _playerMovementPosition;
private Vector3 _playerMovementDirection = Vector3.zero;
private float _playerMovementSpeed;
private bool _playerMovementActive;
public void SyncPlayerMovementInput(Transform playerTransform, bool isMoving, in Vector3 direction, float speed)
{
if (playerTransform == null)
{
ClearPlayerMovementState();
return;
}
if (_playerMovementTransform != playerTransform)
{
_playerMovementTransform = playerTransform;
_playerMovementPosition = playerTransform.position;
}
Vector3 planarDirection = direction;
planarDirection.y = 0f;
bool hasDirection = planarDirection.sqrMagnitude > Mathf.Epsilon;
if (hasDirection)
{
_playerMovementDirection = planarDirection.normalized;
}
else
{
_playerMovementDirection = Vector3.zero;
}
_playerMovementActive = isMoving && hasDirection;
_playerMovementSpeed = _playerMovementActive ? Mathf.Max(0f, speed) : 0f;
}
public void UnregisterPlayerMovement(Transform playerTransform)
{
if (playerTransform == null || _playerMovementTransform != playerTransform)
{
return;
}
ClearPlayerMovementState();
}
private Vector3 ResolvePlayerPositionForTick(in SimulationTickContext context)
{
if (_playerMovementTransform == null)
{
return context.PlayerPosition;
}
if (!_playerMovementActive || _playerMovementSpeed <= 0f ||
_playerMovementDirection.sqrMagnitude <= Mathf.Epsilon || context.DeltaTime <= 0f)
{
_playerMovementPosition = _playerMovementTransform.position;
return _playerMovementPosition;
}
_playerMovementPosition += _playerMovementDirection * (_playerMovementSpeed * context.DeltaTime);
_playerMovementPosition.y = _playerMovementTransform.position.y;
_playerMovementTransform.position = _playerMovementPosition;
return _playerMovementPosition;
}
private void ClearPlayerMovementState()
{
_playerMovementTransform = null;
_playerMovementPosition = Vector3.zero;
_playerMovementDirection = Vector3.zero;
_playerMovementSpeed = 0f;
_playerMovementActive = false;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 417eac38d86047d096eb9f74647b6cf7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -22,6 +22,7 @@ namespace Simulation
EnemyBinding.Clear();
ProjectileBinding.Clear();
PickupBinding.Clear();
ClearPlayerMovementState();
}
#endregion
@@ -101,6 +102,31 @@ namespace Simulation
return true;
}
public void SyncEnemyMovementInput(int entityId, bool isMoving, in UnityEngine.Vector3 direction, float speed,
bool avoidEnemyOverlap, float enemyBodyRadius, int separationIterations)
{
if (!EnemyBinding.TryGetSimulationIndex(entityId, out int simulationIndex) || simulationIndex < 0 ||
simulationIndex >= _enemies.Count)
{
return;
}
EnemySimData enemyData = _enemies[simulationIndex];
enemyData.Speed = isMoving ? UnityEngine.Mathf.Max(0f, speed) : 0f;
enemyData.AvoidEnemyOverlap = avoidEnemyOverlap;
enemyData.EnemyBodyRadius = UnityEngine.Mathf.Max(0.01f, enemyBodyRadius);
enemyData.SeparationIterations = UnityEngine.Mathf.Max(1, separationIterations);
UnityEngine.Vector3 planarDirection = direction;
planarDirection.y = 0f;
if (planarDirection.sqrMagnitude > UnityEngine.Mathf.Epsilon)
{
enemyData.Forward = planarDirection.normalized;
}
_enemies[simulationIndex] = enemyData;
}
#endregion
#region Projectile Simulation State
@@ -219,4 +245,4 @@ namespace Simulation
#endregion
}
}
}
@@ -14,11 +14,6 @@ namespace Simulation
return false;
}
if (!_useSimulationMovement)
{
return false;
}
BuildEnemyTargetSpatialIndexIfNeeded();
float cellSize = GetTargetSelectionCellSize();
@@ -37,9 +37,6 @@ namespace Simulation
private const int ProjectileStateActive = 0;
private const int ProjectileStateExpired = 1;
[Header("模拟世界全局设置")] [Tooltip("是否启用世界模拟")] [SerializeField]
private bool _useSimulationMovement = true;
private EntitySync _entitySync;
private TransformSync _transformSync;
private HitPresentation _hitPresentation;
@@ -47,7 +44,7 @@ namespace Simulation
public IReadOnlyList<EnemySimData> Enemies => _enemies;
public IReadOnlyList<ProjectileSimData> Projectiles => _projectiles;
public IReadOnlyList<PickupSimData> Pickups => _pickups;
public bool UseSimulationMovement => _useSimulationMovement;
public bool UseSimulationMovement => true;
#region Lifecycle
@@ -68,14 +65,12 @@ namespace Simulation
public void Tick(in SimulationTickContext context)
{
if (!_useSimulationMovement)
{
return;
}
Vector3 playerPosition = ResolvePlayerPositionForTick(in context);
SimulationTickContext resolvedContext =
new SimulationTickContext(context.DeltaTime, context.RealDeltaTime, playerPosition);
using (CustomProfilerMarker.TickEnemies.Auto())
{
TickSimulationPipeline(in context);
TickSimulationPipeline(in resolvedContext);
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff