geometry-tower-defense/Assets/GameMain/Scripts/Entity/EntityLogic/EnemyEntity.cs

195 lines
5.9 KiB
C#

using Components;
using System.Collections.Generic;
using GeometryTD.Definition;
using GeometryTD.Entity.EntityData;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace GeometryTD.Entity
{
public class EnemyEntity : EntityBase, IDamageReceiver
{
private const float WaypointReachDistance = 0.05f;
private static readonly List<EnemyEntity> _activeEnemies = new();
private static readonly HashSet<int> _killedEnemyEntityIds = new();
private Transform _target;
private float _speed;
private int _maxHealth;
private int _currentHealth;
private MovementComponent _movementComponent;
private readonly List<Vector3> _pathPoints = new();
private int _pathPointIndex;
private bool _isDespawnRequested;
public static IReadOnlyList<EnemyEntity> ActiveEnemies => _activeEnemies;
public static bool TryConsumeKilledFlag(int entityId)
{
return _killedEnemyEntityIds.Remove(entityId);
}
protected override void OnInit(object userData)
{
base.OnInit(userData);
_movementComponent = GetComponent<MovementComponent>();
}
protected override void OnShow(object userData)
{
base.OnShow(userData);
_target = null;
_pathPoints.Clear();
_pathPointIndex = 0;
_isDespawnRequested = false;
_maxHealth = 1;
_currentHealth = 1;
if (userData is EnemyData enemyData)
{
_speed = enemyData.Speed;
_target = enemyData.Player;
_maxHealth = Mathf.Max(1, enemyData.MaxHealth);
_currentHealth = _maxHealth;
if (enemyData.HasPath)
{
IReadOnlyList<Vector3> pathPoints = enemyData.PathPoints;
for (int i = 0; i < pathPoints.Count; i++)
{
_pathPoints.Add(pathPoints[i]);
}
}
}
_movementComponent.OnInit(_speed, CachedTransform);
_movementComponent.SetMove(true);
_killedEnemyEntityIds.Remove(Id);
if (!_activeEnemies.Contains(this))
{
_activeEnemies.Add(this);
}
}
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(elapseSeconds, realElapseSeconds);
if (_pathPoints.Count > 0)
{
UpdatePathMovement(elapseSeconds, realElapseSeconds);
return;
}
}
protected override void OnHide(bool isShutdown, object userData)
{
_movementComponent.SetMove(false);
_pathPoints.Clear();
_pathPointIndex = 0;
_isDespawnRequested = false;
_maxHealth = 0;
_currentHealth = 0;
_activeEnemies.Remove(this);
base.OnHide(isShutdown, userData);
}
private void OnDestroy()
{
_activeEnemies.Remove(this);
}
public void TakeDamage(int damage, AttackPropertyType attackPropertyType)
{
_ = attackPropertyType;
if (_isDespawnRequested || damage <= 0 || _currentHealth <= 0)
{
return;
}
int previousHealth = _currentHealth;
_currentHealth = Mathf.Max(0, _currentHealth - damage);
if (_maxHealth > 0)
{
GameEntry.HPBar?.ShowHPBar(this, (float)previousHealth / _maxHealth,
(float)_currentHealth / _maxHealth);
Log.Info($"ShowBar: {_currentHealth}/{_maxHealth}");
}
if (_currentHealth <= 0)
{
Log.Info("Enemy Dead");
_killedEnemyEntityIds.Add(Id);
RequestDespawn();
}
}
private void UpdatePathMovement(float elapseSeconds, float realElapseSeconds)
{
if (_isDespawnRequested)
{
return;
}
if (_pathPointIndex >= _pathPoints.Count)
{
DespawnOnReachHouse();
return;
}
Vector3 direction = GetDirectionToPathPoint(_pathPoints[_pathPointIndex], out float distanceSquared);
if (distanceSquared <= WaypointReachDistance * WaypointReachDistance)
{
_pathPointIndex++;
if (_pathPointIndex >= _pathPoints.Count)
{
DespawnOnReachHouse();
return;
}
direction = GetDirectionToPathPoint(_pathPoints[_pathPointIndex], out _);
}
if (direction.sqrMagnitude <= Mathf.Epsilon)
{
_movementComponent.SetMove(false);
return;
}
_movementComponent.SetMove(true);
_movementComponent.SetDirection(direction);
_movementComponent.OnUpdate(elapseSeconds, realElapseSeconds);
}
private Vector3 GetDirectionToPathPoint(Vector3 worldPoint, out float distanceSquared)
{
Vector3 delta = worldPoint - CachedTransform.position;
distanceSquared = delta.sqrMagnitude;
if (distanceSquared <= Mathf.Epsilon)
{
return Vector3.zero;
}
return delta.normalized;
}
private void DespawnOnReachHouse()
{
RequestDespawn();
}
private void RequestDespawn()
{
if (_isDespawnRequested)
{
return;
}
_isDespawnRequested = true;
_movementComponent.SetMove(false);
GameEntry.Entity.HideEntity(Entity);
}
}
}