using UnityEngine; public class EnemyVision : MonoBehaviour { [Header("References")] public EnemyStateMachine stateMachine; public Transform player; [Header("Vision")] public float viewRadius = 14f; [Range(1f, 180f)] public float viewAngle = 65f; public LayerMask obstacleMask; public LayerMask targetMask; public float eyeHeight = 1.4f; public float standingDetectionDelay = 0.12f; public float crouchDetectionDelay = 0.35f; private bool hadLineOfSight; private float visibleTimer; private void Awake() { stateMachine = stateMachine != null ? stateMachine : GetComponent(); } private void Update() { AcquirePlayer(); if (player == null || stateMachine == null) { return; } bool canSeePlayer = CanSeePlayer(out Vector3 eyePosition, out Vector3 targetPosition, out Color debugColor); Debug.DrawRay(eyePosition, targetPosition - eyePosition, debugColor); if (canSeePlayer) { PlayerStealth stealth = player.GetComponent(); float requiredTime = stealth != null && stealth.IsCrouching ? crouchDetectionDelay : standingDetectionDelay; visibleTimer += Time.deltaTime; if (visibleTimer >= requiredTime) { hadLineOfSight = true; stateMachine.OnPlayerDetected(player.position); } } else { visibleTimer = 0f; if (hadLineOfSight) { hadLineOfSight = false; stateMachine.OnPlayerLost(); } } } public float GetViewRadiusForCurrentTarget() { if (player == null) { return viewRadius; } PlayerStealth stealth = player.GetComponent(); return viewRadius * (stealth != null ? stealth.VisibilityMultiplier : 1f); } private bool CanSeePlayer(out Vector3 eyePosition, out Vector3 targetPosition, out Color debugColor) { eyePosition = transform.position + Vector3.up * eyeHeight; targetPosition = player.position + Vector3.up * 0.9f; debugColor = Color.gray; float effectiveRadius = GetViewRadiusForCurrentTarget(); Vector3 directionToPlayer = targetPosition - eyePosition; float distanceToPlayer = directionToPlayer.magnitude; if (distanceToPlayer > effectiveRadius) { return false; } float angleToPlayer = Vector3.Angle(transform.forward, directionToPlayer); if (angleToPlayer > viewAngle * 0.5f) { return false; } int combinedMask = obstacleMask | targetMask; if (Physics.Raycast(eyePosition, directionToPlayer.normalized, out RaycastHit hit, distanceToPlayer, combinedMask, QueryTriggerInteraction.Ignore)) { // Если первым попался не игрок, значит между врагом и игроком есть укрытие или стена. bool isPlayerHit = hit.transform == player || hit.transform.IsChildOf(player); debugColor = isPlayerHit ? Color.green : Color.red; return isPlayerHit; } debugColor = Color.red; return false; } private void AcquirePlayer() { if (player != null) { return; } GameObject playerObject = GameObject.FindGameObjectWithTag("Player"); if (playerObject != null) { player = playerObject.transform; } } }