using UnityEngine; using GeometryTD.Definition; using GeometryTD.Entity.EntityData; namespace Components { [DisallowMultipleComponent] public class ShooterBullet : MonoBehaviour { [SerializeField] [Min(0.1f)] private float _defaultSpeed = 12f; [SerializeField] [Min(0.02f)] private float _hitDistance = 0.15f; [SerializeField] [Min(0.1f)] private float _maxLifetime = 3f; private Transform _target; private float _speed; private AttackPayload _attackPayload; private float _lifetime; private float _runtimeMaxLifetime; private bool _isRunning; private bool _despawnRequested; public void OnShow(BulletData bulletData) { _target = bulletData != null ? bulletData.Target : null; _attackPayload = bulletData?.AttackPayload?.Clone() ?? new AttackPayload(); _speed = bulletData != null && bulletData.Speed > 0f ? bulletData.Speed : _defaultSpeed; _runtimeMaxLifetime = bulletData != null && bulletData.MaxLifetime > 0f ? bulletData.MaxLifetime : _maxLifetime; _lifetime = 0f; _despawnRequested = false; _isRunning = _target != null; if (_isRunning) { Vector2 initialDirection = (Vector2)(_target.position - transform.position); RotateToDirection(initialDirection); } if (!_isRunning) { _despawnRequested = true; } } public void OnReset() { _target = null; _speed = 0f; _attackPayload = new AttackPayload(); _lifetime = 0f; _runtimeMaxLifetime = _maxLifetime; _isRunning = false; _despawnRequested = false; } public void Tick(float deltaTime) { if (!_isRunning) { return; } _lifetime += Mathf.Max(0f, deltaTime); if (_lifetime >= _runtimeMaxLifetime || _target == null) { _isRunning = false; _despawnRequested = true; return; } Vector3 currentPosition = transform.position; Vector2 toTarget = (Vector2)(_target.position - currentPosition); float hitDistanceSquared = _hitDistance * _hitDistance; if (toTarget.sqrMagnitude <= hitDistanceSquared) { HitTarget(); return; } Vector2 direction = toTarget.normalized; RotateToDirection(direction); float moveDistance = _speed * Mathf.Max(0f, deltaTime); if (moveDistance * moveDistance >= toTarget.sqrMagnitude) { Vector3 targetPosition = _target.position; transform.position = new Vector3(targetPosition.x, targetPosition.y, currentPosition.z); HitTarget(); return; } transform.position += (Vector3)(direction * moveDistance); } public bool TryConsumeDespawnRequest() { if (!_despawnRequested) { return false; } _despawnRequested = false; return true; } private void HitTarget() { if (_target == null) { _isRunning = false; _despawnRequested = true; return; } MonoBehaviour[] components = _target.GetComponentsInParent(); foreach (var mono in components) { if (mono is IDamageReceiver damageReceiver) { damageReceiver.TakeDamage(_attackPayload); break; } } _isRunning = false; _despawnRequested = true; } private void RotateToDirection(Vector2 direction) { if (direction.sqrMagnitude <= Mathf.Epsilon) { return; } float targetZAngle = Vector2.SignedAngle(Vector2.up, direction); transform.rotation = Quaternion.Euler(0f, 0f, targetZAngle); } } }