Firstborn/Assets/RPG Creation Kit/Scripts/AI/AI Scripts/AIPerception.cs

326 lines
10 KiB
C#
Raw Permalink Normal View History

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using RPGCreationKit;
using UnityEditor;
using RPGCreationKit.Player;
using RPGCreationKit.BehaviourTree.Data;
using RPGCreationKit.CellsSystem;
namespace RPGCreationKit.AI
{
/// <summary>
/// Class that defines something that can be seen by an AI.
/// </summary>
[System.Serializable]
public class VisibleTarget
{
public Transform tr;
public float distance;
public VisibleTarget(Transform _transform, float _distance)
{
tr = _transform;
distance = _distance;
}
public static int SortByDistance(VisibleTarget fVT, VisibleTarget sVT)
{
return fVT.distance.CompareTo(sVT.distance);
}
}
/// <summary>
/// Defines a visible enemy that the AI will attack.
/// </summary>
[System.Serializable]
public class VisibleEnemy
{
public VisibleEnemy(Entity _entity, EntityAttributes _attributes, AggroInfo _aggro)
{
m_entity = _entity;
m_entityAttributes = _attributes;
m_aggro = _aggro;
}
public Entity m_entity;
public EntityAttributes m_entityAttributes;
public AggroInfo m_aggro;
}
[System.Serializable]
public class VisibleNPCActionPoint
{
public NPCActionPoint actionPoint;
public float distance;
public VisibleNPCActionPoint(GameObject _gameObject, float _distance)
{
actionPoint = _gameObject.GetComponent<NPCActionPoint>();
distance = _distance;
}
public VisibleNPCActionPoint(NPCActionPoint _actionPoint, float _distance)
{
actionPoint = _actionPoint;
distance = _distance;
}
public static int SortByDistance(VisibleTarget fAP, VisibleTarget sAP)
{
return fAP.distance.CompareTo(sAP.distance);
}
}
[System.Serializable]
public class VisibleDoor
{
public Door door;
public float distance;
public VisibleDoor(Door door, float distance)
{
this.door = door;
this.distance = distance;
}
}
/// <summary>
/// Allows the AI to see and understand the sorroundings.
/// </summary>
public class AIPerception : AIInventoryEquipment
{
[SerializeField] public bool perceptionEnabled = true;
public bool PerceptionEnabled
{
get { return perceptionEnabled; }
set
{
perceptionEnabled = value;
if (value)
OnEnable();
}
}
public Transform headPos;
[SerializeField] float viewAngle = 360;
[SerializeField] float lookAtDistance = 3.5f;
//aiLookAt is defined in AITalkative
public LayerMask targetMask;
public LayerMask obstacleMask;
// Define what the AI sees
public List<VisibleTarget> visibleTargets = new List<VisibleTarget>();
public List<VisibleNPCActionPoint> visibleActionPoints = new List<VisibleNPCActionPoint>();
public List<RckAI> visibleAI = new List<RckAI>();
public List<VisibleEnemy> enemyTargets = new List<VisibleEnemy>();
// Defines how many times a second a check for sight needs to be made
[SerializeField] public float checkTick = 1.25f;
// Information about the Sphere that defines the FOV of the AI
public float radius = 3.3f;
public float sphereYOffset = 1.25f;
public float sphereForwardOffset = 1.63f;
[SerializeField] private bool visualizeSphere = true;
public AIOnlineComponents onlineComponents;
public override void Start()
{
base.Start();
}
private void OnEnable()
{
if (!perceptionEnabled)
return;
StopAllCoroutines();
visibleTargets.Clear();
StartCoroutine(UpdateVision());
if(aiLookAt != null)
StartCoroutine(UpdateLookAt());
}
/// <summary>
/// Determines what the AI can see
/// </summary>
/// <returns></returns>
IEnumerator UpdateVision()
{
while (perceptionEnabled)
{
if (!perceptionEnabled)
yield return null;
visibleTargets.Clear();
visibleActionPoints.Clear();
visibleAI.Clear();
if (!isAlive || isUnconscious || isInConversation || isInOfflineMode)
yield return null;
Collider[] hitColliders = Physics.OverlapSphere(transform.position + (Vector3.up * sphereYOffset) + (transform.forward * sphereForwardOffset), radius, targetMask);
for (int i = 0; i < hitColliders.Length; i++)
{
Transform target = hitColliders[i].gameObject.transform;
Vector3 dirToTarget = (target.position - transform.position).normalized;
if (Vector3.Angle(transform.forward, dirToTarget) < viewAngle / 2)
{
float dstToTarget = Vector3.Distance(transform.position, target.position);
if (!Physics.Raycast(headPos.position, dirToTarget, dstToTarget, obstacleMask) && !RCKTransform.IsChildOfRecursive(this.transform, target))
{
if (target.CompareTag("Player"))
target = RckPlayer.instance.mainCamera.transform.parent;
if (target.CompareTag("RPG Creation Kit/NPCActionPoint"))
{
visibleActionPoints.Add(new VisibleNPCActionPoint(target.gameObject, dstToTarget));
}
else if(target.CompareTag("RPG Creation Kit/AI"))
{
visibleAI.Add(target.GetComponent<RckAI>());
}
else
{
visibleTargets.Add(new VisibleTarget(target, dstToTarget));
if (target.name == "HeadPos")
{
visibleAI.Add(target.GetComponentInParent<RckAI>());
}
}
}
}
}
if (mainTarget != null)
{
if (visibleTargets.Count > 0)
{
// Check if we can see the main target
foreach (VisibleTarget target in visibleTargets)
{
if (RCKTransform.IsChildOfRecursive(mainTarget, target.tr, false))
{
canSeeTarget = true;
break;
}
canSeeTarget = false;
}
}
else
canSeeTarget = false;
}
OnUpdatingVisionEnds();
yield return new WaitForSeconds(checkTick);
}
yield return null;
}
public virtual void OnUpdatingVisionEnds()
{
// Check if the path is interrupted by a door
}
public VisibleTarget currentLookingAt;
/// <summary>
/// Updates the AILookAt component in base of what the AI is perceiving
/// </summary>
/// <returns></returns>
IEnumerator UpdateLookAt()
{
while(perceptionEnabled)
{
visibleTargets.Sort(VisibleTarget.SortByDistance);
for (int i = 0; i < visibleTargets.Count; i++)
{
if (visibleTargets[i].distance <= lookAtDistance)
{
if (!aiLookAt.IsLookingAtSomeone)
{
aiLookAt.SetTarget(visibleTargets[i].tr);
currentLookingAt = visibleTargets[i];
}
break;
}
}
if (currentLookingAt != null && currentLookingAt.tr != null && Vector3.Distance(transform.position, currentLookingAt.tr.position) > lookAtDistance)
{
aiLookAt.ClearTarget();
currentLookingAt = null;
}
yield return new WaitForSeconds(checkTick);
}
}
[AIInvokable]
public void DisableAILookAt()
{
aiLookAt.isEnabled = false;
}
[AIInvokable]
public void EnableAILookAt()
{
aiLookAt.isEnabled = true;
}
public override void Die()
{
base.Die();
PerceptionEnabled = false;
}
[AIInvokable]
public void GetDistanceFromPlayerBT(BTVariable _f)
{
_f.SetValue(Vector3.Distance(this.transform.position, RckPlayer.instance.transform.position));
}
#if UNITY_EDITOR
public override void OnDrawGizmos()
{
base.OnDrawGizmos();
if(visualizeSphere)
UnityEngine.Gizmos.DrawWireSphere(transform.position + (Vector3.up * sphereYOffset) + (transform.forward * sphereForwardOffset), radius);
Color previousColor = Handles.color;
foreach (VisibleTarget visibleTarget in visibleTargets)
{
if (visibleTarget != null && visibleTarget.tr != null && headPos != null)
{
Handles.color = Color.red;
Handles.DrawLine(headPos.position, visibleTarget.tr.position, 1f);
}
}
Handles.color = previousColor;
}
#endif
}
}