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 { /// /// Class that defines something that can be seen by an AI. /// [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); } } /// /// Defines a visible enemy that the AI will attack. /// [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(); 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; } } /// /// Allows the AI to see and understand the sorroundings. /// 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 visibleTargets = new List(); public List visibleActionPoints = new List(); public List visibleAI = new List(); public List enemyTargets = new List(); // 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()); } /// /// Determines what the AI can see /// /// 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()); } else { visibleTargets.Add(new VisibleTarget(target, dstToTarget)); if (target.name == "HeadPos") { visibleAI.Add(target.GetComponentInParent()); } } } } } 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; /// /// Updates the AILookAt component in base of what the AI is perceiving /// /// 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 } }