Kursach_StealthAction/Assets/Scripts/PlayerAttack.cs

221 lines
8.2 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 System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerAttack : MonoBehaviour
{
[Header("Параметры атаки")]
public float attackDamage = 25f;
public float attackCooldown = 0.8f;
public float attackRange = 2.8f; // Достаточная дистанция, превышающая атаку мутанта (1.8м)
public float attackRadius = 1.4f; // Радиус зоны поражения удара
public float attackNoiseRadius = 10f;
[Header("Эффекты (опционально)")]
public GameObject hitEffectPrefab;
[Header("Аудио")]
public AudioSource audioSource;
public AudioClip punchSound;
public AudioClip hitSound;
[Header("Управление")]
public bool allowLeftClick = true; // Разрешить ЛКМ для удара кулаком, когда огнестрел убран
[Header("Анимация")]
public Animator animator;
public string attackTrigger = "Attack";
private float nextAttackTime = 0f;
private Camera mainCamera;
void Start()
{
mainCamera = Camera.main;
if (animator == null)
{
animator = GetComponentInChildren<Animator>();
}
if (audioSource == null)
{
audioSource = GetComponent<AudioSource>();
}
}
void Update()
{
if (Time.timeScale == 0f) return;
bool attackPressed = false;
// 1. Быстрый рукопашный удар на клавишу F (доступен всегда)
if (Keyboard.current != null && Keyboard.current.fKey.wasPressedThisFrame)
{
attackPressed = true;
}
// 2. Рукопашный удар на ЛКМ (когда огнестрел убран в кобуру или отсутствует)
else if (allowLeftClick && Mouse.current != null && Mouse.current.leftButton.wasPressedThisFrame)
{
PlayerGun gun = GetComponent<PlayerGun>();
if (gun == null || !gun.IsEquipped)
{
attackPressed = true;
}
}
if (attackPressed && Time.time >= nextAttackTime)
{
PerformAttack();
}
}
private void PerformAttack()
{
nextAttackTime = Time.time + attackCooldown;
if (mainCamera == null) mainCamera = Camera.main;
// 1. Определяем направление атаки по прицелу камеры
Vector3 attackDir = transform.forward;
if (mainCamera != null)
{
Vector3 camFwd = mainCamera.transform.forward;
camFwd.y = 0f;
if (camFwd.sqrMagnitude > 0.001f)
{
attackDir = camFwd.normalized;
// Быстро и плавно поворачиваем персонажа лицом к направлению атаки (прицелу)
GetComponent<PlayerMovement>()?.SnapToAim(attackCooldown);
}
}
if (audioSource != null && punchSound != null)
{
audioSource.PlayOneShot(punchSound);
}
// Запуск анимации удара
if (animator != null && !string.IsNullOrEmpty(attackTrigger))
{
animator.SetTrigger(attackTrigger);
}
Debug.Log("Игрок наносит рукопашный удар!");
HashSet<MutantHealth> damagedEnemies = new HashSet<MutantHealth>();
// 2. Метод А: Проверка по лучу прицела (SphereCast из камеры)
if (mainCamera != null)
{
Ray aimRay = new Ray(mainCamera.transform.position, mainCamera.transform.forward);
float maxCamDist = 12f; // дистанция луча от камеры (камера позади игрока на ~4м)
RaycastHit[] aimHits = Physics.SphereCastAll(aimRay, 0.4f, maxCamDist);
foreach (var hit in aimHits)
{
if (hit.collider.gameObject == gameObject) continue;
MutantHealth target = hit.collider.GetComponentInParent<MutantHealth>();
if (target != null && !damagedEnemies.Contains(target))
{
// Проверяем, что мутант находится в пределах досягаемости игрока
float distFromPlayer = Vector3.Distance(transform.position, target.transform.position);
if (distFromPlayer <= attackRange + 0.5f)
{
damagedEnemies.Add(target);
ApplyDamage(target, hit.point);
}
}
}
}
// 3. Метод Б: Зона поражения перед персонажем (OverlapSphere)
Vector3 attackCenter = transform.position + attackDir * (attackRange * 0.5f) + Vector3.up * 1.0f;
Collider[] hits = Physics.OverlapSphere(attackCenter, attackRadius);
foreach (Collider hit in hits)
{
if (hit.gameObject == gameObject) continue;
MutantHealth mutantHealth = hit.GetComponentInParent<MutantHealth>();
if (mutantHealth != null && !damagedEnemies.Contains(mutantHealth))
{
damagedEnemies.Add(mutantHealth);
ApplyDamage(mutantHealth, hit.ClosestPoint(attackCenter));
}
}
// 4. Метод В: Прямая проверка расстояния в секторе обзора (если мутант в упор бьет игрока)
if (damagedEnemies.Count == 0)
{
var allMutants = FindObjectsByType<MutantHealth>(FindObjectsInactive.Exclude);
foreach (var mutant in allMutants)
{
if (mutant == null || mutant.IsDead) continue;
Vector3 toMutant = mutant.transform.position - transform.position;
float dist = toMutant.magnitude;
toMutant.y = 0f;
// Если мутант в радиусе ближнего боя и перед игроком (угол менее 80 градусов)
if (dist <= attackRange && Vector3.Dot(attackDir, toMutant.normalized) > 0.15f)
{
damagedEnemies.Add(mutant);
ApplyDamage(mutant, mutant.transform.position + Vector3.up);
}
}
}
if (damagedEnemies.Count == 0)
{
Debug.Log("Удар промахнулся (врагов в зоне досягаемости нет).");
}
// Шум от рукопашного боя привлекает ближайших мутантов
MakeAttackNoise();
}
private void ApplyDamage(MutantHealth target, Vector3 hitPoint)
{
target.TakeDamage(attackDamage, transform);
Debug.Log($"<color=green>Удар попал по мутанту: {target.name}! Нанесено {attackDamage} урона.</color>");
if (audioSource != null && hitSound != null)
{
audioSource.PlayOneShot(hitSound);
}
if (hitEffectPrefab != null)
{
Instantiate(hitEffectPrefab, hitPoint, Quaternion.identity);
}
}
private void MakeAttackNoise()
{
Collider[] colliders = Physics.OverlapSphere(transform.position, attackNoiseRadius);
foreach (Collider col in colliders)
{
BlindMutantAI mutant = col.GetComponentInParent<BlindMutantAI>();
if (mutant != null)
{
mutant.HearSound(transform.position);
}
}
}
private void OnDrawGizmosSelected()
{
Vector3 dir = transform.forward;
if (mainCamera != null)
{
Vector3 camFwd = mainCamera.transform.forward;
camFwd.y = 0f;
if (camFwd.sqrMagnitude > 0.001f) dir = camFwd.normalized;
}
Vector3 attackCenter = transform.position + dir * (attackRange * 0.5f) + Vector3.up * 1.0f;
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(attackCenter, attackRadius);
}
}