using System; using SepCore.Definition; using SepCore.Entity; using SepCore.Simulation; using UnityEngine; using SepCore.Debugger; namespace SepCore.Components { public class MovementComponent : MonoBehaviour { [SerializeField] private bool _isMoving; [SerializeField] private Vector3 _direction; [SerializeField] private Transform _cachedTransform; [SerializeField] private bool _avoidEnemyOverlap; [SerializeField] private float _enemyBodyRadius; [SerializeField] private int _separationIterations; public float Speed => _statComponent != null ? _statComponent.GetStatValue(StatType.MoveSpeed) : _speedBase; 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; private Player _player; private EnemyBase _enemy; private Action _onStatChangedAction; public void OnInit(float speed, Transform target, StatComponent statComponent = null, bool avoidEnemyOverlap = false, float? enemyBodyRadius = null, int? separationIterations = null) { _speedBase = speed; _cachedTransform = target; _direction = Vector3.forward; _avoidEnemyOverlap = avoidEnemyOverlap; float defaultRadius = GameEntry.Luban != null && GameEntry.Luban.IsReady ? GameEntry.Luban.Global.EnemyBodyRadius / 1000f : 0.5f; int defaultIterations = GameEntry.Luban != null && GameEntry.Luban.IsReady ? GameEntry.Luban.Global.EnemySeparationIterations : 4; _enemyBodyRadius = Mathf.Max(0.01f, enemyBodyRadius ?? defaultRadius); _separationIterations = Mathf.Max(1, separationIterations ?? defaultIterations); TryGetComponent(out _player); TryGetComponent(out _enemy); _statComponent = statComponent; if (_statComponent != null) { _statComponent.Pipeline.SetBaseValue(StatType.MoveSpeed, speed); _onStatChangedAction = OnStatChanged; _statComponent.Pipeline.OnStatChanged += _onStatChangedAction; } SyncToSimulationWorld(); } public void OnUpdate(float elapseSeconds, float realElapseSeconds) { SyncToSimulationWorld(); } public void OnReset() { Transform transformToUnregister = _cachedTransform; float defaultRadius = GameEntry.Luban != null && GameEntry.Luban.IsReady ? GameEntry.Luban.Global.EnemyBodyRadius / 1000f : 0.5f; int defaultIterations = GameEntry.Luban != null && GameEntry.Luban.IsReady ? GameEntry.Luban.Global.EnemySeparationIterations : 4; _speedBase = 0; _cachedTransform = null; _direction = Vector3.zero; _isMoving = false; _avoidEnemyOverlap = false; _enemyBodyRadius = defaultRadius; _separationIterations = defaultIterations; _player = null; _enemy = null; if (_statComponent != null && _onStatChangedAction != null) { _statComponent.Pipeline.OnStatChanged -= _onStatChangedAction; _onStatChangedAction = null; } _statComponent = null; if (transformToUnregister != null && GameEntry.SimulationWorld != null) { GameEntry.SimulationWorld.UnregisterPlayerMovement(transformToUnregister); } } public void SetMove(bool isMoving) { _isMoving = isMoving; SyncToSimulationWorld(); } public void SetDirection(Vector3 direction) { _direction = direction; SyncToSimulationWorld(); } private void SyncToSimulationWorld() { using (CustomProfilerMarker.Movement_Update.Auto()) { if (_cachedTransform == null || GameEntry.SimulationWorld == null) { return; } Vector3 direction = _direction; direction.y = 0f; if (direction.sqrMagnitude > Mathf.Epsilon) { direction.Normalize(); } if (_player != null) { GameEntry.SimulationWorld.SyncPlayerMovementInput(_cachedTransform, _isMoving, direction, Speed); return; } if (_enemy != null) { GameEntry.SimulationWorld.SyncEnemyMovementInput(_enemy.Id, _isMoving, direction, Speed, _avoidEnemyOverlap, _enemyBodyRadius, _separationIterations); } } } private void OnStatChanged(StatType statType, float newValue) { if (statType == StatType.MoveSpeed) { SyncToSimulationWorld(); } } } }