312 lines
11 KiB
C#
312 lines
11 KiB
C#
/*********************************************************************
|
|
* \file LeafPatrol.cs
|
|
* \author Ryan Wang Nian Jing
|
|
* \brief Leaf node implementation for patrolling AI
|
|
*
|
|
*
|
|
* \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 SHADE_Scripting.Audio;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
//VARIABLES HERE
|
|
public partial class LeafPatrol : BehaviourTreeNode
|
|
{
|
|
//Waypoints and movement
|
|
private Transform transform;
|
|
private List<GameObject>? waypoints;
|
|
private RigidBody rb;
|
|
private float patrolSpeed;
|
|
private float chaseSpeed;
|
|
private float turningSpeed;
|
|
private float retreatTimer = 0.0f;
|
|
private int currentWaypointIndex = -1;
|
|
private bool retreatState = false;
|
|
private bool goingForwards = true;
|
|
|
|
//Small delays between waypoints
|
|
private bool isWaiting = false;
|
|
private const float waitDuration = 2.0f;
|
|
private float waitCounter = 0.0f;
|
|
}
|
|
|
|
//FUNCTIONS HERE
|
|
public partial class LeafPatrol : BehaviourTreeNode
|
|
{
|
|
//Constructor, establish values here
|
|
//Despite inheriting from BehaviourTreeNode, we don't have children to this
|
|
//node, and hence we do not need to inherit its constructors
|
|
public LeafPatrol(string name) : base(name)
|
|
{
|
|
currentWaypointIndex = -1;
|
|
}
|
|
|
|
//When it comes to evaluating,
|
|
//le this node keep returning RUNNING as it is the last fallback node on tree
|
|
public override BehaviourTreeNodeStatus Evaluate()
|
|
{
|
|
//Debug.LogWarning("LeafPatrol");
|
|
onEnter(BehaviourTreeNodeStatus.RUNNING);
|
|
|
|
//Initialise home waypoint
|
|
waypoints = (List<GameObject>)GetNodeData("waypoints");
|
|
if (currentWaypointIndex == -1)
|
|
{
|
|
if (waypoints != null)
|
|
{
|
|
//Debug.Log("Waypoints not null");
|
|
if (GetNodeData("startWaypoint") != null)
|
|
{
|
|
//Debug.Log("Getting start waypoint");
|
|
GameObject startWaypoint = (GameObject)(GetNodeData("startWaypoint"));
|
|
for (int i = 0; i < waypoints.Count; ++i)
|
|
{
|
|
if (startWaypoint == waypoints[i])
|
|
{
|
|
//Debug.Log("Leaf Patrol Constructor: Start Waypoint Index: " + i.ToString());
|
|
currentWaypointIndex = i;
|
|
SetNodeData("currentWaypointIndex", i);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = BehaviourTreeNodeStatus.FAILURE;
|
|
onExit(BehaviourTreeNodeStatus.FAILURE);
|
|
return status;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = BehaviourTreeNodeStatus.FAILURE;
|
|
onExit(BehaviourTreeNodeStatus.FAILURE);
|
|
return status;
|
|
}
|
|
}
|
|
|
|
//Get data
|
|
if (GetNodeData("transform") == null ||
|
|
GetNodeData("patrolSpeed") == null ||
|
|
GetNodeData("chaseSpeed") == null ||
|
|
GetNodeData("turningSpeed") == null ||
|
|
GetNodeData("rigidBody") == null)
|
|
{
|
|
status = BehaviourTreeNodeStatus.FAILURE;
|
|
onExit(BehaviourTreeNodeStatus.FAILURE);
|
|
return status;
|
|
}
|
|
else
|
|
{
|
|
transform = (Transform)GetNodeData("transform");
|
|
patrolSpeed = (float)GetNodeData("patrolSpeed");
|
|
chaseSpeed = (float)GetNodeData("chaseSpeed");
|
|
turningSpeed = (float)GetNodeData("turningSpeed");
|
|
rb = (RigidBody)GetNodeData("rigidBody");
|
|
}
|
|
|
|
if (GetNodeData("currentWaypointIndex") == null)
|
|
{
|
|
SetNodeData("currentWaypointIndex", 0);
|
|
}
|
|
if (isWaiting) DelayAtWaypoint();
|
|
else MoveToWaypoint();
|
|
|
|
status = BehaviourTreeNodeStatus.RUNNING;
|
|
onExit(BehaviourTreeNodeStatus.RUNNING);
|
|
return status;
|
|
}
|
|
|
|
//Move and cycle between waypoints
|
|
private void MoveToWaypoint()
|
|
{
|
|
//Debug.Log("MoveToWaypoint");
|
|
//Waiting, do not move
|
|
if (GetNodeData("isWaiting") != null)
|
|
{
|
|
//Only wait to change waypoints if not alert
|
|
if (GetNodeData("isAlert") != null && !(bool)GetNodeData("isAlert"))
|
|
waitCounter = 0.0f;
|
|
|
|
isWaiting = true;
|
|
ClearNodeData("isWaiting");
|
|
return;
|
|
}
|
|
if (GetNodeData("currentWaypointIndex") != null)
|
|
{
|
|
//2023 Mar 4, 0400, this is needed when the AI teleports back to the first spot after catching
|
|
currentWaypointIndex = (int)GetNodeData("currentWaypointIndex");
|
|
}
|
|
//Modulo operator to prevent out of range exceptions
|
|
Vector3 targetPosition = waypoints[(currentWaypointIndex % waypoints.Count)].GetComponent<Transform>().GlobalPosition;
|
|
//Reach waypoint by X and Z being near enough
|
|
//Do not consider Y of waypoints yet
|
|
Vector3 remainingDistance = targetPosition - transform.GlobalPosition;
|
|
remainingDistance.y = 0.0f;
|
|
//Reached waypoint, cycle
|
|
if (remainingDistance.GetSqrMagnitude() < 0.1f)
|
|
{
|
|
//If alert, may reverse
|
|
if (GetNodeData("isAlert") != null && (bool)GetNodeData("isAlert"))
|
|
{
|
|
//If alert, may reverse if it's closer to the player
|
|
if (GetNodeData("playerLastSightedWaypointIndex") != null)
|
|
{
|
|
int playerWaypoint = (int)GetNodeData("playerLastSightedWaypointIndex");
|
|
int forwardDistance = 0;
|
|
int backDistance = 0;
|
|
if (playerWaypoint < currentWaypointIndex)
|
|
{
|
|
//Player waypoint is behind current waypoint
|
|
forwardDistance = playerWaypoint + waypoints.Count() - currentWaypointIndex;
|
|
backDistance = currentWaypointIndex - playerWaypoint;
|
|
}
|
|
else
|
|
{
|
|
//Player waypoint is ahead of current waypoint (or same)
|
|
forwardDistance = playerWaypoint - currentWaypointIndex;
|
|
backDistance = currentWaypointIndex + waypoints.Count() - playerWaypoint;
|
|
}
|
|
|
|
if (backDistance < forwardDistance)
|
|
{
|
|
//Go backwards
|
|
goingForwards = false;
|
|
|
|
}
|
|
else
|
|
{
|
|
//Go forward
|
|
goingForwards = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Fallback if no player waypoint data, go forward
|
|
goingForwards = true;
|
|
}
|
|
}
|
|
|
|
//Cycle waypoints
|
|
if (goingForwards)
|
|
{
|
|
++currentWaypointIndex;
|
|
if (currentWaypointIndex >= waypoints.Count())
|
|
currentWaypointIndex = 0;
|
|
}
|
|
else
|
|
{
|
|
--currentWaypointIndex;
|
|
if (currentWaypointIndex < 0)
|
|
currentWaypointIndex = waypoints.Count() - 1;
|
|
}
|
|
|
|
|
|
//Write to blackboard
|
|
SetNodeData("currentWaypointIndex", currentWaypointIndex);
|
|
|
|
//Only wait to change waypoints if not alert
|
|
if (GetNodeData("isAlert") != null && !(bool)GetNodeData("isAlert"))
|
|
waitCounter = 0.0f;
|
|
|
|
isWaiting = true;
|
|
}
|
|
else if (false /*Physics.OverlapSphere(_selfTransform.position, 0.3f, 1 << 8).Length > 0 && retreatState == false*/)
|
|
{
|
|
//TODO
|
|
//This main segment is to check if the NPC is walking into a solid wall
|
|
//If they are, do a raycast to find the nearest unobstructed waypoint and head there instead
|
|
}
|
|
else //Proceed to waypoint as usual
|
|
{
|
|
//Get the difference vector to the waypoint
|
|
//Debug.Log("Current Waypoint " + waypoints[currentWaypointIndex].x.ToString() + " " + waypoints[currentWaypointIndex].y.ToString() + " " + waypoints[currentWaypointIndex].z.ToString());
|
|
//Debug.Log("AI is at " + transform.GlobalPosition.x.ToString() + " " + transform.GlobalPosition.y.ToString() + " " + transform.GlobalPosition.z.ToString());
|
|
Vector3 normalisedDifference = targetPosition - transform.GlobalPosition;
|
|
normalisedDifference.y = 0.0f; //Do not move vertically
|
|
//Debug.Log("Leaf Patrol Current Waypoint Index: " + currentWaypointIndex.ToString());
|
|
//Debug.Log("True Difference x " + normalisedDifference.x.ToString() + " z " + normalisedDifference.z.ToString());
|
|
normalisedDifference /= normalisedDifference.GetMagnitude();
|
|
//Debug.Log("Normalised Difference x " + normalisedDifference.x.ToString() + " z " + normalisedDifference.z.ToString());
|
|
|
|
//Look at the correct direction
|
|
Quaternion targetRotation = Quaternion.LookRotation(normalisedDifference, new Vector3(0.0f, 1.0f, 0.0f));
|
|
transform.LocalRotation = Quaternion.Slerp(transform.LocalRotation, targetRotation, turningSpeed * Time.DeltaTimeF);
|
|
|
|
//transform.GlobalPosition += normalisedDifference * moveSpeed * (float)Time.DeltaTime;
|
|
//rb.LinearVelocity = normalisedDifference * patrolSpeed;
|
|
|
|
//ORIGINAL INTENDED CODE
|
|
/*rb.AddForce(new Vector3(normalisedDifference.x, 0.0f, normalisedDifference.z) * movementForceMultiplier);
|
|
float currentSpeed = MathF.Sqrt(rb.LinearVelocity.x * rb.LinearVelocity.x + rb.LinearVelocity.z * rb.LinearVelocity.z);
|
|
if (currentSpeed > patrolSpeed)
|
|
{
|
|
float adjustmentFactor = patrolSpeed / currentSpeed;
|
|
Vector3 adjustedVelocity = rb.LinearVelocity;
|
|
//adjustedVelocity *= adjustmentFactor;
|
|
adjustedVelocity.x = patrolSpeed;
|
|
adjustedVelocity.z = patrolSpeed;
|
|
rb.LinearVelocity = adjustedVelocity;
|
|
}*/
|
|
|
|
//TODO delete this when original intended code above works with velocity being limited correctly
|
|
if (rb != null)
|
|
{
|
|
//Move quickly if alert
|
|
if (GetNodeData("isAlert") != null && (bool)GetNodeData("isAlert"))
|
|
{
|
|
//Debug.Log("Fast Patrol");
|
|
rb.LinearVelocity = normalisedDifference * chaseSpeed;
|
|
}
|
|
else
|
|
{
|
|
rb.LinearVelocity = normalisedDifference * patrolSpeed;
|
|
}
|
|
|
|
//Unalert if AI reaches player nearest
|
|
if (GetNodeData("currentWaypointIndex") != null && GetNodeData("playerLastSightedWaypointIndex") != null)
|
|
{
|
|
if ((int)GetNodeData("currentWaypointIndex") == (int)GetNodeData("playerLastSightedWaypointIndex"))
|
|
{
|
|
if (GetNodeData("isAlert") != null && (bool)GetNodeData("isAlert"))
|
|
{
|
|
//Debug.Log("Unalert");
|
|
//AudioHandler.audioClipHandlers["BGMAlert"].Stop(true);
|
|
//Audio.PlaySFXOnce2D("event:/Homeowner/humming");
|
|
AudioHandler.audioClipHandlers["SFXHumming"].Play();
|
|
//AudioHandler.audioClipHandlers["BGMUnalert"].Play();
|
|
//AudioHandler.audioClipHandlers["BGMAdaptive"].SetParameter("Detected", 0.0f);
|
|
//Audio.SetParameter("Detected", 0.0f);
|
|
Audio.SetParameterWithLabel("PlayerDetection", "Undetected");
|
|
}
|
|
SetNodeData("isAlert", false);
|
|
}
|
|
}
|
|
}
|
|
if (retreatState)
|
|
{
|
|
if (retreatTimer < 1.0f) retreatTimer += Time.DeltaTimeF;
|
|
else
|
|
{
|
|
retreatState = false;
|
|
retreatTimer = 1.0f;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void DelayAtWaypoint()
|
|
{
|
|
waitCounter += Time.DeltaTimeF;
|
|
if (waitCounter >= waitDuration)
|
|
isWaiting = false;
|
|
}
|
|
} |