/********************************************************************* * \file LeafSearch.cs * \author Ryan Wang Nian Jing * \brief Leaf node implementation for AI searching for player * * * \copyright Copyright (c) 2022 DigiPen Institute of Technology. Reproduction or disclosure of this file or its contents without the prior written consent of DigiPen Institute of Technology is prohibited. *********************************************************************/ using SHADE; using SHADE_Scripting.AIBehaviour.BehaviourTree; using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; //VARIABLES HERE public partial class LeafSearch : BehaviourTreeNode { private Transform transform; private Vector3 eyeOffset; private float sightDistance; private GameObject? player; //To be searched for and marked } //FUNCTIONS HERE public partial class LeafSearch : BehaviourTreeNode { public LeafSearch(string name, Transform t, Vector3 eo, float sDist) : base(name) { transform = t; eyeOffset = eo; sightDistance = sDist; player = null; } //Helper, find the nearest unobstructed waypoint to return to when chase is over private void reevaluateWaypoint() { Debug.Log("Reevaluating Waypoints"); List waypoints = (List)GetNodeData("waypoints"); if (waypoints == null) { SetNodeData("currentWaypointIndex", 0); return; } int nearestWaypointIndex = 0; for (int i = 0; i < waypoints.Count; ++i) { if ((transform.GlobalPosition - waypoints[i].GetComponent().GlobalPosition).GetSqrMagnitude() < (transform.GlobalPosition - waypoints[nearestWaypointIndex].GetComponent().GlobalPosition).GetSqrMagnitude()) { nearestWaypointIndex = i; } } SetNodeData("currentWaypointIndex", nearestWaypointIndex); } public override BehaviourTreeNodeStatus Evaluate() { //Debug.LogWarning("LeafSearch"); onEnter(BehaviourTreeNodeStatus.RUNNING); //Search for player player = GameObject.Find("Player"); //Automatically fail if no player is found if (player == null) { SetNodeData("isAlert", false); status = BehaviourTreeNodeStatus.FAILURE; onExit(BehaviourTreeNodeStatus.FAILURE); return status; } else { //Fail if unable to find a player //Get player's transform Transform plrT = player.GetValueOrDefault().GetComponent(); //DELETE THIS //Debug.Log("X " + MathF.Sin(transform.LocalEulerAngles.y).ToString() + " Z " + MathF.Cos(transform.LocalEulerAngles.y).ToString()); //Debug.Log("Looking at: " + transform.LocalRotation.y.ToString() + " To player is: " + temporary.ToString()); //Debug.Log("Look difference is: " + (transform.LocalRotation.y - differenceDirection.y).ToString()); //Debug.Log("Dot: " + Quaternion.Dot(differenceDirection, transform.GlobalRotation)); //Fail if too far from vision range if ((plrT.GlobalPosition - transform.GlobalPosition).GetMagnitude() > sightDistance) { //Debug.Log("Failure: Too far"); if (GetNodeData("isAlert") != null && (bool)GetNodeData("isAlert") == true) { Debug.Log("AI play unalert hmm"); reevaluateWaypoint(); } SetNodeData("isAlert", false); status = BehaviourTreeNodeStatus.FAILURE; onExit(BehaviourTreeNodeStatus.FAILURE); return status; } //Fail if player is out of FOV //TODO currently a simple dot product against negative is done, this makes it essentially be a semicircle in front at which AI can see Vector3 difference = plrT.GlobalPosition - transform.GlobalPosition; difference.y = 0.0f; //Disregard Y axis Vector3 lookDirection = new Vector3(MathF.Sin(transform.LocalEulerAngles.y), 0.0f, MathF.Cos(transform.LocalEulerAngles.y)); //Debug.Log("Dot: " + Vector3.Dot(difference, lookDirection)); if (Vector3.Dot(difference, lookDirection) < 0.0f) { //Debug.Log("Failure: Out of FOV"); if (GetNodeData("isAlert") != null && (bool)GetNodeData("isAlert") == true) { Debug.Log("AI play unalert hmm"); reevaluateWaypoint(); } SetNodeData("isAlert", false); status = BehaviourTreeNodeStatus.FAILURE; onExit(BehaviourTreeNodeStatus.FAILURE); return status; } //LocalRotation is between -1 and 1, which are essentially the same. //0 and -1/1 are 180 deg apart //Quaternion differenceDirection = Quaternion.FromToRotation(Vector3.Forward, plrT.GlobalPosition - transform.GlobalPosition); //Debug.Log("Looking at: " + transform.LocalRotation.y.ToString() + " To player is: " + differenceDirection.y.ToString()); //Draw a ray, succeed if ray is unobstructed Vector3 eyePosition = transform.GlobalPosition + eyeOffset; Ray sightRay = new Ray(eyePosition, plrT.GlobalPosition - eyePosition); RaycastHit sightRayHit = Physics.Raycast(sightRay); //As of November 2022, RaycastHit contains only the FIRST object hit by //the ray in the Other GameObject data member //Diren may likely add ALL objects hit by the ray over December if (sightRayHit.Hit && sightRayHit.Other != player) { //Debug.Log("Failure: Ray hit obstacle named " + sightRayHit.Other.GetValueOrDefault().Name + " ID" + sightRayHit.Other.GetValueOrDefault().EntityId); if (GetNodeData("isAlert") != null && (bool)GetNodeData("isAlert") == true) { Debug.Log("AI play unalert hmm"); reevaluateWaypoint(); } SetNodeData("isAlert", false); status = BehaviourTreeNodeStatus.FAILURE; onExit(BehaviourTreeNodeStatus.FAILURE); return status; } else if (sightRayHit.Hit && sightRayHit.Other == player) { //Debug.Log("Ray hit player"); } //All checks for now succeeded //Debug.Log("Success: Homeowner has sighted player"); //Write player's transform into the blackboard SetNodeData("target", plrT); if (GetNodeData("isAlert") != null && (bool)GetNodeData("isAlert") == false) { Debug.Log("AI Play Alerted Yell here"); } SetNodeData("isAlert", true); status = BehaviourTreeNodeStatus.SUCCESS; onExit(BehaviourTreeNodeStatus.SUCCESS); return status; } } }