Cleanup 1
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+3
-22
@@ -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
Reference in New Issue
Block a user