This commit is contained in:
Alex38Lyon
2025-06-03 12:00:47 +02:00
parent ed8041abcd
commit 878ea46cac
1300 changed files with 527178 additions and 0 deletions
+141
View File
@@ -0,0 +1,141 @@
using System.Linq;
using Unity.FPS.Game;
using UnityEngine;
using UnityEngine.Events;
namespace Unity.FPS.AI
{
public class DetectionModule : MonoBehaviour
{
[Tooltip("The point representing the source of target-detection raycasts for the enemy AI")]
public Transform DetectionSourcePoint;
[Tooltip("The max distance at which the enemy can see targets")]
public float DetectionRange = 20f;
[Tooltip("The max distance at which the enemy can attack its target")]
public float AttackRange = 10f;
[Tooltip("Time before an enemy abandons a known target that it can't see anymore")]
public float KnownTargetTimeout = 4f;
[Tooltip("Optional animator for OnShoot animations")]
public Animator Animator;
public UnityAction onDetectedTarget;
public UnityAction onLostTarget;
public GameObject KnownDetectedTarget { get; private set; }
public bool IsTargetInAttackRange { get; private set; }
public bool IsSeeingTarget { get; private set; }
public bool HadKnownTarget { get; private set; }
protected float TimeLastSeenTarget = Mathf.NegativeInfinity;
ActorsManager m_ActorsManager;
const string k_AnimAttackParameter = "Attack";
const string k_AnimOnDamagedParameter = "OnDamaged";
protected virtual void Start()
{
m_ActorsManager = FindAnyObjectByType<ActorsManager>();
DebugUtility.HandleErrorIfNullFindObject<ActorsManager, DetectionModule>(m_ActorsManager, this);
}
public virtual void HandleTargetDetection(Actor actor, Collider[] selfColliders)
{
// Handle known target detection timeout
if (KnownDetectedTarget && !IsSeeingTarget && (Time.time - TimeLastSeenTarget) > KnownTargetTimeout)
{
KnownDetectedTarget = null;
}
// Find the closest visible hostile actor
float sqrDetectionRange = DetectionRange * DetectionRange;
IsSeeingTarget = false;
float closestSqrDistance = Mathf.Infinity;
foreach (Actor otherActor in m_ActorsManager.Actors)
{
if (otherActor.Affiliation != actor.Affiliation)
{
float sqrDistance = (otherActor.transform.position - DetectionSourcePoint.position).sqrMagnitude;
if (sqrDistance < sqrDetectionRange && sqrDistance < closestSqrDistance)
{
// Check for obstructions
RaycastHit[] hits = Physics.RaycastAll(DetectionSourcePoint.position,
(otherActor.AimPoint.position - DetectionSourcePoint.position).normalized, DetectionRange,
-1, QueryTriggerInteraction.Ignore);
RaycastHit closestValidHit = new RaycastHit();
closestValidHit.distance = Mathf.Infinity;
bool foundValidHit = false;
foreach (var hit in hits)
{
if (!selfColliders.Contains(hit.collider) && hit.distance < closestValidHit.distance)
{
closestValidHit = hit;
foundValidHit = true;
}
}
if (foundValidHit)
{
Actor hitActor = closestValidHit.collider.GetComponentInParent<Actor>();
if (hitActor == otherActor)
{
IsSeeingTarget = true;
closestSqrDistance = sqrDistance;
TimeLastSeenTarget = Time.time;
KnownDetectedTarget = otherActor.AimPoint.gameObject;
}
}
}
}
}
IsTargetInAttackRange = KnownDetectedTarget != null &&
Vector3.Distance(transform.position, KnownDetectedTarget.transform.position) <=
AttackRange;
// Detection events
if (!HadKnownTarget &&
KnownDetectedTarget != null)
{
OnDetect();
}
if (HadKnownTarget &&
KnownDetectedTarget == null)
{
OnLostTarget();
}
// Remember if we already knew a target (for next frame)
HadKnownTarget = KnownDetectedTarget != null;
}
public virtual void OnLostTarget() => onLostTarget?.Invoke();
public virtual void OnDetect() => onDetectedTarget?.Invoke();
public virtual void OnDamaged(GameObject damageSource)
{
TimeLastSeenTarget = Time.time;
KnownDetectedTarget = damageSource;
if (Animator)
{
Animator.SetTrigger(k_AnimOnDamagedParameter);
}
}
public virtual void OnAttack()
{
if (Animator)
{
Animator.SetTrigger(k_AnimAttackParameter);
}
}
}
}