geometry-tower-defense/Assets/GameMain/Scripts/Components/ShooterBullet.cs

138 lines
4.2 KiB
C#

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<MonoBehaviour>();
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);
}
}
}