Files
PIM_Laba3/Assets/Scripts/EnemyTarget.cs
T
2026-05-25 23:10:02 +03:00

271 lines
8.0 KiB
C#

using System.Collections;
using UnityEngine;
public class EnemyTarget : MonoBehaviour, IDamageable
{
public static int RemainingObjectives { get; private set; }
public static event System.Action<int> ObjectiveCountChanged;
[Header("Health")]
public float maxHealth = 60f;
public bool countsAsObjective = true;
[Header("Combat")]
public bool canAttack = true;
public float attackRange = 28f;
public float attackDamage = 12f;
public float attackCooldown = 1.3f;
public Color tracerColor = new Color(1f, 0.35f, 0.25f);
[Header("Movement")]
public Vector3 patrolAxis = Vector3.zero;
public float patrolAmplitude = 0f;
public float patrolSpeed = 1f;
[Header("References")]
public Transform muzzlePoint;
private static Material _sharedTracerMaterial;
private float _currentHealth;
private float _nextAttackTime;
private Vector3 _startPosition;
private PlayerHealth _playerHealth;
private Transform _playerTarget;
private Renderer[] _renderers;
private Color[] _baseColors;
private bool _destroyed;
public static void ResetObjectives()
{
RemainingObjectives = 0;
ObjectiveCountChanged?.Invoke(RemainingObjectives);
}
public static void RecalculateObjectivesInScene()
{
EnemyTarget[] targets = FindObjectsOfType<EnemyTarget>(true);
int total = 0;
for (int i = 0; i < targets.Length; i++)
{
if (targets[i] != null && targets[i].countsAsObjective)
{
total++;
}
}
RemainingObjectives = total;
ObjectiveCountChanged?.Invoke(RemainingObjectives);
}
public void Initialize(PlayerHealth playerHealth, Transform playerTarget)
{
_playerHealth = playerHealth;
_playerTarget = playerTarget;
}
private void Awake()
{
_currentHealth = maxHealth;
_startPosition = transform.position;
_renderers = GetComponentsInChildren<Renderer>();
_baseColors = new Color[_renderers.Length];
for (int i = 0; i < _renderers.Length; i++)
{
_baseColors[i] = _renderers[i].material.color;
}
if (countsAsObjective)
{
RemainingObjectives++;
ObjectiveCountChanged?.Invoke(RemainingObjectives);
}
}
private void Start()
{
SnapToSurface();
_startPosition = transform.position;
if (_playerHealth == null)
{
_playerHealth = FindObjectOfType<PlayerHealth>();
}
if (_playerTarget == null && _playerHealth != null)
{
Camera playerCamera = _playerHealth.GetComponentInChildren<Camera>(true);
_playerTarget = playerCamera != null ? playerCamera.transform : _playerHealth.transform;
}
}
private void SnapToSurface()
{
if (_renderers == null || _renderers.Length == 0)
{
return;
}
Bounds bounds = _renderers[0].bounds;
for (int i = 1; i < _renderers.Length; i++)
{
bounds.Encapsulate(_renderers[i].bounds);
}
float currentBottomY = bounds.min.y;
Vector3 rayOrigin = bounds.center + Vector3.up * 20f;
RaycastHit[] hits = Physics.RaycastAll(rayOrigin, Vector3.down, 60f, ~0, QueryTriggerInteraction.Ignore);
if (hits.Length == 0)
{
return;
}
System.Array.Sort(hits, (a, b) => a.distance.CompareTo(b.distance));
for (int i = 0; i < hits.Length; i++)
{
if (hits[i].collider == null || hits[i].collider.transform.IsChildOf(transform))
{
continue;
}
float offsetY = hits[i].point.y - currentBottomY;
if (Mathf.Abs(offsetY) > 0.01f)
{
transform.position += Vector3.up * offsetY;
}
break;
}
}
private void Update()
{
if (_destroyed)
{
return;
}
if (patrolAmplitude > 0.01f && patrolAxis.sqrMagnitude > 0.001f)
{
Vector3 offset = patrolAxis.normalized * Mathf.Sin(Time.time * patrolSpeed) * patrolAmplitude;
transform.position = _startPosition + offset;
}
if (!canAttack || _playerHealth == null || _playerHealth.IsDead || _playerTarget == null)
{
return;
}
Vector3 aimPoint = _playerTarget.position;
Vector3 flatDirection = aimPoint - transform.position;
flatDirection.y = 0f;
if (flatDirection.sqrMagnitude > 0.001f)
{
Quaternion targetRotation = Quaternion.LookRotation(flatDirection.normalized, Vector3.up);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * 5f);
}
Vector3 fireOrigin = muzzlePoint != null ? muzzlePoint.position : transform.position + transform.forward * 0.8f + Vector3.up * 1.2f;
Vector3 direction = (aimPoint - fireOrigin).normalized;
if (Vector3.Distance(fireOrigin, aimPoint) > attackRange || Time.time < _nextAttackTime)
{
return;
}
if (Physics.Raycast(fireOrigin, direction, out RaycastHit hit, attackRange, ~0, QueryTriggerInteraction.Ignore))
{
PlayerHealth hitPlayer = hit.collider.GetComponentInParent<PlayerHealth>();
if (hitPlayer != null)
{
_nextAttackTime = Time.time + attackCooldown;
hitPlayer.TakeDamage(attackDamage);
ShowTracer(fireOrigin, hit.point, tracerColor);
}
}
}
public void TakeDamage(float amount, Vector3 hitPoint, Vector3 hitNormal)
{
if (_destroyed)
{
return;
}
_currentHealth -= amount;
StartCoroutine(FlashHit());
if (_currentHealth <= 0f)
{
DestroyTarget();
}
}
private IEnumerator FlashHit()
{
for (int i = 0; i < _renderers.Length; i++)
{
_renderers[i].material.color = Color.white;
}
yield return new WaitForSeconds(0.08f);
for (int i = 0; i < _renderers.Length; i++)
{
if (_renderers[i] != null)
{
_renderers[i].material.color = _baseColors[i];
}
}
}
private void DestroyTarget()
{
_destroyed = true;
if (countsAsObjective)
{
RemainingObjectives = Mathf.Max(0, RemainingObjectives - 1);
ObjectiveCountChanged?.Invoke(RemainingObjectives);
}
GameObject burst = GameObject.CreatePrimitive(PrimitiveType.Sphere);
Destroy(burst.GetComponent<Collider>());
burst.transform.position = transform.position + Vector3.up;
burst.transform.localScale = Vector3.one * 0.75f;
Renderer burstRenderer = burst.GetComponent<Renderer>();
burstRenderer.material = new Material(Shader.Find("Standard"));
burstRenderer.material.color = new Color(1f, 0.45f, 0.15f);
Destroy(burst, 0.18f);
Destroy(gameObject);
}
private void ShowTracer(Vector3 start, Vector3 end, Color color)
{
GameObject tracer = new GameObject("EnemyTracer");
LineRenderer renderer = tracer.AddComponent<LineRenderer>();
renderer.material = GetTracerMaterial();
renderer.positionCount = 2;
renderer.SetPosition(0, start);
renderer.SetPosition(1, end);
renderer.startWidth = 0.045f;
renderer.endWidth = 0.015f;
renderer.startColor = color;
renderer.endColor = new Color(color.r, color.g, color.b, 0.05f);
renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
renderer.receiveShadows = false;
Destroy(tracer, 0.07f);
}
private static Material GetTracerMaterial()
{
if (_sharedTracerMaterial == null)
{
_sharedTracerMaterial = new Material(Shader.Find("Sprites/Default"));
}
return _sharedTracerMaterial;
}
}