169 lines
4.7 KiB
C#
169 lines
4.7 KiB
C#
using GeometryTD.Definition;
|
|
using GeometryTD.Entity;
|
|
using UnityEngine;
|
|
|
|
namespace Components
|
|
{
|
|
[DisallowMultipleComponent]
|
|
public class DefenseTowerController : MonoBehaviour
|
|
{
|
|
[SerializeField] private ShooterMuzzleComp _muzzleComp;
|
|
[SerializeField] private BasicBearingComp _bearingComp;
|
|
[SerializeField] private BasicBaseComp _baseComp;
|
|
[SerializeField] private Transform _scanOrigin;
|
|
[SerializeField] [Min(0.02f)] private float _retargetInterval = 0.1f;
|
|
[SerializeField] private bool _autoUpdate = true;
|
|
|
|
private Transform _currentTarget;
|
|
private float _retargetTimer;
|
|
|
|
public Transform CurrentTarget => _currentTarget;
|
|
|
|
private void Awake()
|
|
{
|
|
ResolveComponents();
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (!_autoUpdate)
|
|
{
|
|
return;
|
|
}
|
|
|
|
OnUpdate(Time.deltaTime);
|
|
}
|
|
|
|
public void OnInit(DefenseTowerStatsData stats)
|
|
{
|
|
ResolveComponents();
|
|
if (stats == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_muzzleComp?.OnInit(stats.AttackDamage, stats.AttackMethodType);
|
|
_bearingComp?.OnInit(stats.RotateSpeed, stats.AttackRange);
|
|
_baseComp?.OnInit(stats.AttackSpeed, stats.AttackPropertyType);
|
|
_currentTarget = null;
|
|
_retargetTimer = 0f;
|
|
}
|
|
|
|
public void OnReset()
|
|
{
|
|
_currentTarget = null;
|
|
_retargetTimer = 0f;
|
|
_muzzleComp?.OnReset();
|
|
_bearingComp?.OnReset();
|
|
_baseComp?.OnReset();
|
|
}
|
|
|
|
public void SetAutoUpdate(bool autoUpdate)
|
|
{
|
|
_autoUpdate = autoUpdate;
|
|
}
|
|
|
|
public void SetTarget(Transform target)
|
|
{
|
|
_currentTarget = target;
|
|
}
|
|
|
|
public void ClearTarget()
|
|
{
|
|
_currentTarget = null;
|
|
}
|
|
|
|
public void OnUpdate(float deltaTime)
|
|
{
|
|
if (!HasCoreComponents())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!IsTargetValid(_currentTarget) || !_bearingComp.IsTargetInRange(_currentTarget, _scanOrigin))
|
|
{
|
|
TryRetarget(deltaTime);
|
|
}
|
|
|
|
if (_currentTarget == null)
|
|
{
|
|
_baseComp.Tick(deltaTime);
|
|
return;
|
|
}
|
|
|
|
_baseComp.TryAttack(_bearingComp, _muzzleComp, _currentTarget, deltaTime);
|
|
}
|
|
|
|
private void TryRetarget(float deltaTime)
|
|
{
|
|
_retargetTimer -= Mathf.Max(0f, deltaTime);
|
|
if (_retargetTimer > 0f)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_retargetTimer = _retargetInterval;
|
|
_currentTarget = FindNearestEnemyTarget();
|
|
}
|
|
|
|
private Transform FindNearestEnemyTarget()
|
|
{
|
|
Vector3 origin = _scanOrigin != null ? _scanOrigin.position : transform.position;
|
|
float maxRange = _bearingComp.AttackRange;
|
|
float maxRangeSquared = maxRange * maxRange;
|
|
|
|
EnemyEntity bestEnemy = null;
|
|
float bestDistanceSquared = float.MaxValue;
|
|
for (int i = 0; i < EnemyEntity.ActiveEnemies.Count; i++)
|
|
{
|
|
EnemyEntity enemy = EnemyEntity.ActiveEnemies[i];
|
|
if (enemy == null || !enemy.isActiveAndEnabled)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
Transform enemyTransform = enemy.transform;
|
|
Vector3 delta = enemyTransform.position - origin;
|
|
float distanceSquared = delta.sqrMagnitude;
|
|
if (distanceSquared > maxRangeSquared || distanceSquared >= bestDistanceSquared)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
bestDistanceSquared = distanceSquared;
|
|
bestEnemy = enemy;
|
|
}
|
|
|
|
return bestEnemy != null ? bestEnemy.transform : null;
|
|
}
|
|
|
|
private void ResolveComponents()
|
|
{
|
|
if (_muzzleComp == null)
|
|
{
|
|
_muzzleComp = GetComponent<ShooterMuzzleComp>();
|
|
}
|
|
|
|
if (_bearingComp == null)
|
|
{
|
|
_bearingComp = GetComponent<BasicBearingComp>();
|
|
}
|
|
|
|
if (_baseComp == null)
|
|
{
|
|
_baseComp = GetComponent<BasicBaseComp>();
|
|
}
|
|
}
|
|
|
|
private bool HasCoreComponents()
|
|
{
|
|
return _muzzleComp != null && _bearingComp != null && _baseComp != null;
|
|
}
|
|
|
|
private static bool IsTargetValid(Transform target)
|
|
{
|
|
return target != null && target.gameObject.activeInHierarchy;
|
|
}
|
|
}
|
|
}
|