Kursach_StealthAction/Assets/Scripts/BlindMutantAI.cs

318 lines
10 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using UnityEngine;
using UnityEngine.AI;
public class BlindMutantAI : MonoBehaviour
{
private NavMeshAgent agent;
private Animator animator;
[Header("Настройки скорости")]
public float walkSpeed = 2f;
public float runSpeed = 5f;
[Header("Патрулирование")]
public Transform[] patrolPoints;
[Tooltip("Время паузы на точке патрулирования для прислушивания")]
public float waitTimeAtPoint = 1.5f;
private float waitTimer = 0f;
private int currentPatrolIndex;
private bool isInvestigating = false;
[Header("Обнаружение игрока")]
public float detectionRadius = 3.5f; // Радиус обостренных чувств (слух/запах в упор)
public float losePlayerDistance = 14f; // Дистанция потери цели
private Transform targetPlayer;
private PlayerHealth targetPlayerHealth;
[Header("Настройки атаки")]
public float attackRange = 1.8f; // Дистанция ближнего боя
public float attackDamage = 20f; // Урон за один удар
public float attackCooldown = 1.2f; // Кулдаун между ударами
private float nextAttackTime = 0f;
void Start()
{
agent = GetComponent<NavMeshAgent>();
if (agent != null)
{
agent.speed = walkSpeed;
// Если агент чуть выше пола или не на сетке, привязываем его к ближайшей точке NavMesh
if (!agent.isOnNavMesh)
{
if (NavMesh.SamplePosition(transform.position, out NavMeshHit hit, 5.0f, NavMesh.AllAreas))
{
agent.Warp(hit.position);
}
}
}
animator = GetComponentInChildren<Animator>();
// Автоматически находим игрока на сцене
var playerObj = FindAnyObjectByType<PlayerHealth>();
if (playerObj != null)
{
targetPlayerHealth = playerObj;
}
// Если точки патрулирования не заданы или массив состоит из null, ищем PatrolWaypoints на сцене
if (IsPatrolArrayEmptyOrNull())
{
AutoAssignPatrolWaypoints();
}
GoToNextPatrolPoint();
}
private bool IsPatrolArrayEmptyOrNull()
{
if (patrolPoints == null || patrolPoints.Length == 0) return true;
for (int i = 0; i < patrolPoints.Length; i++)
{
if (patrolPoints[i] != null) return false;
}
return true;
}
private void AutoAssignPatrolWaypoints()
{
var patrolRoot = GameObject.Find("PatrolWaypoints");
if (patrolRoot != null && patrolRoot.transform.childCount > 0)
{
patrolPoints = new Transform[patrolRoot.transform.childCount];
for (int i = 0; i < patrolRoot.transform.childCount; i++)
{
patrolPoints[i] = patrolRoot.transform.GetChild(i);
}
Debug.Log($"<color=cyan>[BlindMutantAI] Автоматически обнаружено {patrolPoints.Length} точек патрулирования в объекте 'PatrolWaypoints'.</color>");
}
}
void Update()
{
UpdateAnimator();
// 1. Поведение при активном преследовании игрока
if (targetPlayer != null)
{
HandlePlayerChaseAndAttack();
return;
}
// 2. Проверка близости игрока (если подошел слишком близко — чует даже слепой)
CheckProximityDetection();
// 3. Патрулирование и расследование звуков
HandlePatrolAndInvestigation();
}
private void UpdateAnimator()
{
if (animator == null) return;
float currentSpeed = agent != null && agent.enabled && agent.isOnNavMesh ? agent.velocity.magnitude : 0f;
animator.SetFloat("Speed", currentSpeed);
}
private void HandlePlayerChaseAndAttack()
{
if (agent == null || !agent.isOnNavMesh) return;
if (targetPlayerHealth != null && targetPlayerHealth.IsDead)
{
LosePlayerTarget();
return;
}
float distanceToPlayer = Vector3.Distance(transform.position, targetPlayer.position);
// Игрок убежал слишком далеко
if (distanceToPlayer > losePlayerDistance)
{
LosePlayerTarget();
return;
}
// Игрок в зоне удара
if (distanceToPlayer <= attackRange)
{
agent.isStopped = true;
// Разворачиваемся к игроку
Vector3 lookDirection = targetPlayer.position - transform.position;
lookDirection.y = 0f;
if (lookDirection.sqrMagnitude > 0.001f)
{
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(lookDirection), Time.deltaTime * 10f);
}
// Наносим урон по кулдауну
if (Time.time >= nextAttackTime)
{
nextAttackTime = Time.time + attackCooldown;
if (animator != null)
{
animator.SetTrigger("Attack");
}
if (targetPlayerHealth != null)
{
targetPlayerHealth.TakeDamage(attackDamage);
Debug.Log($"Мутант нанес удар игроку! Урон: {attackDamage}");
}
}
}
else
{
// Бежим к игроку
agent.isStopped = false;
agent.speed = runSpeed;
agent.SetDestination(targetPlayer.position);
}
}
private void CheckProximityDetection()
{
if (targetPlayerHealth == null || targetPlayerHealth.IsDead) return;
float distance = Vector3.Distance(transform.position, targetPlayerHealth.transform.position);
if (distance <= detectionRadius)
{
EngagePlayer(targetPlayerHealth.transform);
}
}
private void HandlePatrolAndInvestigation()
{
if (agent == null || !agent.isOnNavMesh) return;
if (!agent.pathPending && agent.remainingDistance < 0.6f)
{
if (isInvestigating)
{
isInvestigating = false;
agent.speed = walkSpeed;
waitTimer = waitTimeAtPoint;
}
if (waitTimer > 0f)
{
waitTimer -= Time.deltaTime;
agent.isStopped = true;
return;
}
agent.isStopped = false;
waitTimer = waitTimeAtPoint;
GoToNextPatrolPoint();
}
}
private void EngagePlayer(Transform playerTransform)
{
if (agent == null || !agent.isOnNavMesh) return;
targetPlayer = playerTransform;
if (targetPlayerHealth == null)
{
targetPlayerHealth = playerTransform.GetComponent<PlayerHealth>();
}
isInvestigating = false;
waitTimer = 0f;
agent.isStopped = false;
agent.speed = runSpeed;
agent.SetDestination(targetPlayer.position);
Debug.Log("Мутант обнаружил игрока и начинает атаку!");
}
private void LosePlayerTarget()
{
targetPlayer = null;
if (agent != null && agent.isOnNavMesh)
{
agent.isStopped = false;
agent.speed = walkSpeed;
}
isInvestigating = false;
waitTimer = 0f;
GoToNextPatrolPoint();
Debug.Log("Мутант потерял игрока и возвращается к патрулированию.");
}
private void GoToNextPatrolPoint()
{
if (agent == null || !agent.isOnNavMesh) return;
if (patrolPoints != null && patrolPoints.Length > 0)
{
for (int attempts = 0; attempts < patrolPoints.Length; attempts++)
{
var wp = patrolPoints[currentPatrolIndex];
currentPatrolIndex = (currentPatrolIndex + 1) % patrolPoints.Length;
if (wp != null)
{
agent.isStopped = false;
agent.speed = walkSpeed;
agent.SetDestination(wp.position);
return;
}
}
}
// Если точек нет вообще — свободное блуждание по комплексу
WanderRandomly();
}
private void WanderRandomly()
{
if (agent == null || !agent.isOnNavMesh) return;
Vector3 randomDir = Random.insideUnitSphere * 15f;
randomDir += transform.position;
if (NavMesh.SamplePosition(randomDir, out NavMeshHit hit, 15f, NavMesh.AllAreas))
{
agent.isStopped = false;
agent.speed = walkSpeed;
agent.SetDestination(hit.position);
}
}
public void HearSound(Vector3 soundPosition)
{
// Если уже бьем игрока в упор, не отвлекаемся на случайный шум вдалеке
if (targetPlayer != null && Vector3.Distance(transform.position, targetPlayer.position) <= attackRange)
{
return;
}
if (agent == null || !agent.isOnNavMesh) return;
isInvestigating = true;
waitTimer = 0f;
agent.isStopped = false;
agent.speed = runSpeed;
agent.SetDestination(soundPosition);
}
public void OnDamagedByPlayer(Transform player)
{
if (animator != null)
{
animator.SetTrigger("GetHit");
}
EngagePlayer(player);
}
private void OnDrawGizmosSelected()
{
// Радиус ближнего обнаружения
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(transform.position, detectionRadius);
// Радиус атаки
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, attackRange);
}
}