Files
Alex38Lyon 878ea46cac update
2025-06-03 12:00:47 +02:00

185 lines
6.4 KiB
C#

using Unity.FPS.Game;
using UnityEngine;
namespace Unity.FPS.AI
{
[RequireComponent(typeof(EnemyController))]
public class EnemyMobile : MonoBehaviour
{
public enum AIState
{
Patrol,
Follow,
Attack,
}
public Animator Animator;
[Tooltip("Fraction of the enemy's attack range at which it will stop moving towards target while attacking")]
[Range(0f, 1f)]
public float AttackStopDistanceRatio = 0.5f;
[Tooltip("The random hit damage effects")]
public ParticleSystem[] RandomHitSparks;
public ParticleSystem[] OnDetectVfx;
public AudioClip OnDetectSfx;
[Header("Sound")] public AudioClip MovementSound;
public MinMaxFloat PitchDistortionMovementSpeed;
public AIState AiState { get; private set; }
EnemyController m_EnemyController;
AudioSource m_AudioSource;
const string k_AnimMoveSpeedParameter = "MoveSpeed";
const string k_AnimAttackParameter = "Attack";
const string k_AnimAlertedParameter = "Alerted";
const string k_AnimOnDamagedParameter = "OnDamaged";
void Start()
{
m_EnemyController = GetComponent<EnemyController>();
DebugUtility.HandleErrorIfNullGetComponent<EnemyController, EnemyMobile>(m_EnemyController, this,
gameObject);
m_EnemyController.onAttack += OnAttack;
m_EnemyController.onDetectedTarget += OnDetectedTarget;
m_EnemyController.onLostTarget += OnLostTarget;
m_EnemyController.SetPathDestinationToClosestNode();
m_EnemyController.onDamaged += OnDamaged;
// Start patrolling
AiState = AIState.Patrol;
// adding a audio source to play the movement sound on it
m_AudioSource = GetComponent<AudioSource>();
DebugUtility.HandleErrorIfNullGetComponent<AudioSource, EnemyMobile>(m_AudioSource, this, gameObject);
m_AudioSource.clip = MovementSound;
m_AudioSource.Play();
}
void Update()
{
UpdateAiStateTransitions();
UpdateCurrentAiState();
float moveSpeed = m_EnemyController.NavMeshAgent.velocity.magnitude;
// Update animator speed parameter
Animator.SetFloat(k_AnimMoveSpeedParameter, moveSpeed);
// changing the pitch of the movement sound depending on the movement speed
m_AudioSource.pitch = Mathf.Lerp(PitchDistortionMovementSpeed.Min, PitchDistortionMovementSpeed.Max,
moveSpeed / m_EnemyController.NavMeshAgent.speed);
}
void UpdateAiStateTransitions()
{
// Handle transitions
switch (AiState)
{
case AIState.Follow:
// Transition to attack when there is a line of sight to the target
if (m_EnemyController.IsSeeingTarget && m_EnemyController.IsTargetInAttackRange)
{
AiState = AIState.Attack;
m_EnemyController.SetNavDestination(transform.position);
}
break;
case AIState.Attack:
// Transition to follow when no longer a target in attack range
if (!m_EnemyController.IsTargetInAttackRange)
{
AiState = AIState.Follow;
}
break;
}
}
void UpdateCurrentAiState()
{
// Handle logic
switch (AiState)
{
case AIState.Patrol:
m_EnemyController.UpdatePathDestination();
// m_EnemyController.SetNavDestination(m_EnemyController.GetDestinationOnPath());
break;
case AIState.Follow:
m_EnemyController.SetNavDestination(m_EnemyController.KnownDetectedTarget.transform.position);
m_EnemyController.OrientTowards(m_EnemyController.KnownDetectedTarget.transform.position);
m_EnemyController.OrientWeaponsTowards(m_EnemyController.KnownDetectedTarget.transform.position);
break;
case AIState.Attack:
if (Vector3.Distance(m_EnemyController.KnownDetectedTarget.transform.position,
m_EnemyController.DetectionModule.DetectionSourcePoint.position)
>= (AttackStopDistanceRatio * m_EnemyController.DetectionModule.AttackRange))
{
m_EnemyController.SetNavDestination(m_EnemyController.KnownDetectedTarget.transform.position);
}
else
{
m_EnemyController.SetNavDestination(transform.position);
}
m_EnemyController.OrientTowards(m_EnemyController.KnownDetectedTarget.transform.position);
m_EnemyController.TryAtack(m_EnemyController.KnownDetectedTarget.transform.position);
break;
}
}
void OnAttack()
{
Animator.SetTrigger(k_AnimAttackParameter);
}
void OnDetectedTarget()
{
if (AiState == AIState.Patrol)
{
AiState = AIState.Follow;
}
for (int i = 0; i < OnDetectVfx.Length; i++)
{
OnDetectVfx[i].Play();
}
if (OnDetectSfx)
{
AudioUtility.CreateSFX(OnDetectSfx, transform.position, AudioUtility.AudioGroups.EnemyDetection, 1f);
}
Animator.SetBool(k_AnimAlertedParameter, true);
}
void OnLostTarget()
{
if (AiState == AIState.Follow || AiState == AIState.Attack)
{
AiState = AIState.Patrol;
}
for (int i = 0; i < OnDetectVfx.Length; i++)
{
OnDetectVfx[i].Stop();
}
Animator.SetBool(k_AnimAlertedParameter, false);
}
void OnDamaged()
{
if (RandomHitSparks.Length > 0)
{
int n = Random.Range(0, RandomHitSparks.Length - 1);
RandomHitSparks[n].Play();
}
Animator.SetTrigger(k_AnimOnDamagedParameter);
}
}
}