diff --git a/Assets/Scenes/MainGameWithAIFixed.shade b/Assets/Scenes/MainGameWithAIFixed.shade index 4a987708..d30799bc 100644 --- a/Assets/Scenes/MainGameWithAIFixed.shade +++ b/Assets/Scenes/MainGameWithAIFixed.shade @@ -8599,6 +8599,7 @@ Arm Length: 1 Look At Camera Origin: true Target Offset: {x: 0, y: 0, z: 0} + Camera Collision: false IsActive: true Scripts: - Type: SHADE_Scripting.ThirdPersonCamera @@ -8640,7 +8641,7 @@ Components: Transform Component: Translate: {x: 2.70000005, y: 0.100000001, z: -2} - Rotate: {x: -0, y: 0, z: -0} + Rotate: {x: -0, y: -2.09439516, z: -0} Scale: {x: 1, y: 1, z: 1} IsActive: true Renderable Component: @@ -8665,7 +8666,7 @@ Colliders: - Is Trigger: false Type: Box - Half Extents: {x: 0.200000003, y: 0.899999976, z: 0.200000003} + Half Extents: {x: 0.400000006, y: 1.79999995, z: 0.400000006} Friction: 0.400000006 Bounciness: 0 Density: 1 diff --git a/Assets/Scripts/AIBehaviour/BehaviourTree/Core/BehaviourTree.cs b/Assets/Scripts/AIBehaviour/BehaviourTree/Core/BehaviourTree.cs index cdced78e..092c4087 100644 --- a/Assets/Scripts/AIBehaviour/BehaviourTree/Core/BehaviourTree.cs +++ b/Assets/Scripts/AIBehaviour/BehaviourTree/Core/BehaviourTree.cs @@ -74,6 +74,7 @@ namespace SHADE_Scripting.AIBehaviour.BehaviourTree } else { + //Debug.LogError("Cannot retrieve data " + key); return outData; } } diff --git a/Assets/Scripts/AIBehaviour/Implemented/Homeowner1.cs b/Assets/Scripts/AIBehaviour/Implemented/Homeowner1.cs index 67c1034c..ff315df2 100644 --- a/Assets/Scripts/AIBehaviour/Implemented/Homeowner1.cs +++ b/Assets/Scripts/AIBehaviour/Implemented/Homeowner1.cs @@ -104,15 +104,14 @@ public partial class Homeowner1 : BehaviourTree //Called every tick protected override void Tick() { - Debug.Log("Ticking"); + //Debug.Log("Ticking"); //Update data if (GetData("waypoints") == null) { - if (waypointsPool) + if (waypointsPool != GameObject.Null) SetData("waypoints", (List)waypointsPool.GetChildren()); - - List wpTemp = (List)GetData("waypoints"); - Debug.Log(wpTemp.Count.ToString()); + else + Debug.LogError("No waypoints, no AI"); } if (GetData("transform") == null) SetData("transform", GetComponent()); @@ -145,7 +144,7 @@ public partial class Homeowner1 : BehaviourTree Audio.PlaySFXOnce2D("event:/Homeowner/homeowner_footsteps"); footstepTimeRemaining = footstepSFXIntervalMultiplier; } - Debug.Log("Ticked"); + //Debug.Log("Ticked"); } //Define the behaviour tree here @@ -153,7 +152,7 @@ public partial class Homeowner1 : BehaviourTree //The tree is called from the root every tick protected override BehaviourTreeNode CreateTree() { - Debug.Log("Creating Tree"); + //Debug.Log("Creating Tree"); //Start from the root, structure it like this to make it look like a tree BehaviourTreeNode root = new BehaviourTreeSelector("Root", new List { @@ -168,7 +167,7 @@ public partial class Homeowner1 : BehaviourTree }), new LeafPatrol("Patrol") }); - Debug.Log("Tree Created"); + //Debug.Log("Tree Created"); return root; } } \ No newline at end of file diff --git a/Assets/Scripts/AIBehaviour/Implemented/LeafNodes/LeafAttack.cs b/Assets/Scripts/AIBehaviour/Implemented/LeafNodes/LeafAttack.cs index b2cfeb52..71a23115 100644 --- a/Assets/Scripts/AIBehaviour/Implemented/LeafNodes/LeafAttack.cs +++ b/Assets/Scripts/AIBehaviour/Implemented/LeafNodes/LeafAttack.cs @@ -21,7 +21,7 @@ using System.Threading.Tasks; //VARIABLES public partial class LeafAttack : BehaviourTreeNode { - //Holds the player game object + //Holds the player game object that is to be targeted private GameObject player; } @@ -30,12 +30,36 @@ public partial class LeafAttack : BehaviourTreeNode { public LeafAttack(string name) : base (name) { - Debug.Log("LeafAttack ctor"); + //Debug.Log("LeafAttack ctor"); + } + + //Helper, find the nearest unobstructed waypoint to return to when chase is over + public void reevaluateWaypoint() + { + List waypoints = (List)GetNodeData("waypoints"); + Transform transform = (Transform)GetNodeData("transform"); + + if (waypoints == null || transform == 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("LeafAttack"); + //Debug.LogWarning("LeafAttack"); //Fail if no target in blackboard? onEnter(BehaviourTreeNodeStatus.RUNNING); @@ -49,7 +73,7 @@ public partial class LeafAttack : BehaviourTreeNode return status; } else - captureTime = (float)GetNodeData("captureTime"); + captureTime = (float)GetNodeData("captureTimeLeft"); if (GameObject.Find("Player") == null) { @@ -69,6 +93,7 @@ public partial class LeafAttack : BehaviourTreeNode if (captureTime <= 0.0f) { //Catch player when in range for long enough + //Debug.Log("Success: Caught"); player.GetScript().currentState = PlayerController.RaccoonStates.CAUGHT; status = BehaviourTreeNodeStatus.SUCCESS; onExit(BehaviourTreeNodeStatus.SUCCESS); @@ -76,8 +101,7 @@ public partial class LeafAttack : BehaviourTreeNode } //Return running if not success - - Debug.Log("Success: Caught"); + //Debug.Log("Running: About to capture in " + captureTimeLeft); status = BehaviourTreeNodeStatus.RUNNING; onExit(BehaviourTreeNodeStatus.RUNNING); return status; diff --git a/Assets/Scripts/AIBehaviour/Implemented/LeafNodes/LeafChase.cs b/Assets/Scripts/AIBehaviour/Implemented/LeafNodes/LeafChase.cs index 6beeb851..de3352d6 100644 --- a/Assets/Scripts/AIBehaviour/Implemented/LeafNodes/LeafChase.cs +++ b/Assets/Scripts/AIBehaviour/Implemented/LeafNodes/LeafChase.cs @@ -36,21 +36,36 @@ public partial class LeafChase : BehaviourTreeNode //and hence we don't need to inherit its constructors public LeafChase(string name) : base (name) { - Debug.Log("LeafChase ctor"); + //Debug.Log("LeafChase ctor"); } - /* - * transform = t; - this.rb = rb; - chaseSpeed = cSpd; - turnSpeed = tSpd; - captureDistance = capDist; - captureTime = capTime; - */ + //Helper, find which waypoint player is closest to + private void determinePlayerWaypoint() + { + List waypoints = (List)GetNodeData("waypoints"); + Transform target = (Transform)GetNodeData("target"); + if (waypoints == null || target == null) + { + return; + } + + int nearestWaypointIndex = 0; + for (int i = 0; i < waypoints.Count; ++i) + { + if ((target.GlobalPosition - waypoints[i].GetComponent().GlobalPosition).GetSqrMagnitude() < + (target.GlobalPosition - waypoints[nearestWaypointIndex].GetComponent().GlobalPosition).GetSqrMagnitude()) + { + nearestWaypointIndex = i; + } + } + //Debug.Log("Player is nearest " + nearestWaypointIndex); + //Debug.Log("I'm at " + (int)GetNodeData("currentWaypointIndex")); + SetNodeData("playerLastSightedWaypointIndex", nearestWaypointIndex); + } public override BehaviourTreeNodeStatus Evaluate() { - Debug.LogWarning("LeafChase"); + //Debug.LogWarning("LeafChase"); onEnter(BehaviourTreeNodeStatus.RUNNING); @@ -79,7 +94,7 @@ public partial class LeafChase : BehaviourTreeNode //Fail if no target in blackboard if (GetNodeData("target") == null) { - Debug.Log("Failure: No target in blackboard"); + //Debug.Log("Failure: No target in blackboard"); return BehaviourTreeNodeStatus.FAILURE; } Transform target = (Transform)GetNodeData("target"); @@ -91,7 +106,7 @@ public partial class LeafChase : BehaviourTreeNode //Over maximum distance, stop chase if ((transform.GlobalPosition - target.GlobalPosition).GetMagnitude() > 1000.0f) { - Debug.Log("Failure: Over maximum distance"); + //Debug.Log("Failure: Over maximum distance"); ClearNodeData("target"); if (GetNodeData("isAlert") != null && (bool)GetNodeData("isAlert") == true) @@ -106,7 +121,7 @@ public partial class LeafChase : BehaviourTreeNode } else if (false) //TODO If collided against a wall { - Debug.Log("Running: Collided against wall"); + //Debug.Log("Running: Collided against wall"); SetNodeData("isPathfinding", true); status = BehaviourTreeNodeStatus.RUNNING; @@ -116,12 +131,31 @@ public partial class LeafChase : BehaviourTreeNode //Keep chasing else if ((transform.GlobalPosition - target.GlobalPosition).GetMagnitude() > captureDistance) { - Debug.Log("Running: Chasing"); + //Debug.Log("Running: Chasing"); Quaternion targetRotation = Quaternion.LookRotation(normalisedDifference, new Vector3(0.0f, 1.0f, 0.0f)); transform.LocalRotation = Quaternion.Slerp(transform.LocalRotation, targetRotation, turningSpeed * Time.DeltaTimeF); + + //Determine the player's nearest waypoint as long as the AI can see the player + //Head towards that waypoint in an attempt to chase the player + determinePlayerWaypoint(); + //TODO delete this when original intendd code above works with velocity being limited correctly - rb.LinearVelocity = normalisedDifference * chaseSpeed; + //Only chase the player directly if the player's waypoint matches the AI's own + if (GetNodeData("currentWaypointIndex") != null && GetNodeData("playerLastSightedWaypointIndex") != null) + { + if ((int)GetNodeData("currentWaypointIndex") == (int)GetNodeData("playerLastSightedWaypointIndex")) + { + //Debug.Log("Waypoint indicees matching. Chasing directly"); + rb.LinearVelocity = normalisedDifference * chaseSpeed; + } + else + { + status = BehaviourTreeNodeStatus.FAILURE; + onExit(BehaviourTreeNodeStatus.FAILURE); + return BehaviourTreeNodeStatus.FAILURE; + } + } //Reset capture timing to base SetNodeData("captureTimeLeft", baseCaptureTime); @@ -136,7 +170,7 @@ public partial class LeafChase : BehaviourTreeNode //Once player is close enough, perform attack else { - Debug.Log("Success: Near enough. Begin attack"); + //Debug.Log("Success: Near enough. Begin attack"); //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); diff --git a/Assets/Scripts/AIBehaviour/Implemented/LeafNodes/LeafPatrol.cs b/Assets/Scripts/AIBehaviour/Implemented/LeafNodes/LeafPatrol.cs index 7a29b7c4..2f9c8e1a 100644 --- a/Assets/Scripts/AIBehaviour/Implemented/LeafNodes/LeafPatrol.cs +++ b/Assets/Scripts/AIBehaviour/Implemented/LeafNodes/LeafPatrol.cs @@ -25,10 +25,12 @@ public partial class LeafPatrol : BehaviourTreeNode private List? waypoints; private RigidBody rb; private float patrolSpeed; + private float chaseSpeed; private float turningSpeed; private float retreatTimer = 0.0f; private int currentWaypointIndex = 0; private bool retreatState = false; + private bool goingForwards = true; //Small delays between waypoints private bool isWaiting = false; @@ -51,12 +53,13 @@ public partial class LeafPatrol : BehaviourTreeNode //le this node keep returning RUNNING as it is the last fallback node on tree public override BehaviourTreeNodeStatus Evaluate() { - Debug.LogWarning("LeafPatrol"); + //Debug.LogWarning("LeafPatrol"); onEnter(BehaviourTreeNodeStatus.RUNNING); //Get data if (GetNodeData("transform") == null || GetNodeData("patrolSpeed") == null || + GetNodeData("chaseSpeed") == null || GetNodeData("turningSpeed") == null || GetNodeData("rigidBody") == null) { @@ -68,6 +71,7 @@ public partial class LeafPatrol : BehaviourTreeNode { transform = (Transform)GetNodeData("transform"); patrolSpeed = (float)GetNodeData("patrolSpeed"); + chaseSpeed = (float)GetNodeData("chaseSpeed"); turningSpeed = (float)GetNodeData("turningSpeed"); rb = (RigidBody)GetNodeData("rigidBody"); } @@ -87,11 +91,14 @@ public partial class LeafPatrol : BehaviourTreeNode //Move and cycle between waypoints private void MoveToWaypoint() { - Debug.Log("MoveToWaypoint"); + //Debug.Log("MoveToWaypoint"); //Waiting, do not move if (GetNodeData("isWaiting") != null) { - waitCounter = 0.0f; + //Only wait to change waypoints if not alert + if (GetNodeData("isAlert") != null && !(bool)GetNodeData("isAlert")) + waitCounter = 0.0f; + isWaiting = true; ClearNodeData("isWaiting"); return; @@ -109,15 +116,69 @@ public partial class LeafPatrol : BehaviourTreeNode //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 - ++currentWaypointIndex; - if (currentWaypointIndex >= waypoints.Count()) - currentWaypointIndex = 0; + if (goingForwards) + { + ++currentWaypointIndex; + if (currentWaypointIndex >= waypoints.Count()) + currentWaypointIndex = 0; + } + else + { + --currentWaypointIndex; + if (currentWaypointIndex < 0) + currentWaypointIndex = waypoints.Count() - 1; + } + //Write to blackboard SetNodeData("currentWaypointIndex", currentWaypointIndex); - waitCounter = 0.0f; + //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*/) @@ -159,8 +220,30 @@ public partial class LeafPatrol : BehaviourTreeNode //TODO delete this when original intended code above works with velocity being limited correctly if (rb != null) { - //Debug.Log("Null check passed?"); - rb.LinearVelocity = normalisedDifference * patrolSpeed; + //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"); + Audio.PlaySFXOnce2D("event:/Homeowner/humming"); + } + SetNodeData("isAlert", false); + } + } } if (retreatState) { @@ -176,7 +259,6 @@ public partial class LeafPatrol : BehaviourTreeNode private void DelayAtWaypoint() { - Debug.Log("DelayAtWaypoint"); waitCounter += Time.DeltaTimeF; if (waitCounter >= waitDuration) isWaiting = false; diff --git a/Assets/Scripts/AIBehaviour/Implemented/LeafNodes/LeafSearch.cs b/Assets/Scripts/AIBehaviour/Implemented/LeafNodes/LeafSearch.cs index 68a12be7..4fa5e376 100644 --- a/Assets/Scripts/AIBehaviour/Implemented/LeafNodes/LeafSearch.cs +++ b/Assets/Scripts/AIBehaviour/Implemented/LeafNodes/LeafSearch.cs @@ -32,11 +32,11 @@ public partial class LeafSearch : BehaviourTreeNode { public LeafSearch(string name) : base(name) { - Debug.Log("LeafSearch ctor"); + //Debug.Log("LeafSearch ctor"); } //Helper, find the nearest unobstructed waypoint to return to when chase is over - private void reevaluateWaypoint() + public void reevaluateWaypoint() { List waypoints = (List)GetNodeData("waypoints"); @@ -58,6 +58,8 @@ public partial class LeafSearch : BehaviourTreeNode SetNodeData("currentWaypointIndex", nearestWaypointIndex); } + //Helper for handling being alert + //Helper for handling stopping of chases private void handleChaseStop() { @@ -71,7 +73,7 @@ public partial class LeafSearch : BehaviourTreeNode public override BehaviourTreeNodeStatus Evaluate() { - Debug.LogWarning("LeafSearch"); + //Debug.LogWarning("LeafSearch"); onEnter(BehaviourTreeNodeStatus.RUNNING); //Get data @@ -86,7 +88,6 @@ public partial class LeafSearch : BehaviourTreeNode else { transform = (Transform)GetNodeData("transform"); - Debug.Log("Transform position: " + transform.GlobalPosition.x + " " + transform.GlobalPosition.y + " " + transform.GlobalPosition.z); eyeOffset = (Vector3)GetNodeData("eyeOffset"); sightDistance = (float)GetNodeData("sightDistance"); } @@ -117,8 +118,8 @@ public partial class LeafSearch : BehaviourTreeNode //Fail if too far from vision range if ((plrT.GlobalPosition - transform.GlobalPosition).GetMagnitude() > sightDistance) { - Debug.Log("Failure: Too far"); - handleChaseStop(); + //Debug.Log("Failure: Too far"); + //handleChaseStop(); status = BehaviourTreeNodeStatus.FAILURE; onExit(BehaviourTreeNodeStatus.FAILURE); return status; @@ -132,8 +133,8 @@ public partial class LeafSearch : BehaviourTreeNode //Debug.Log("Dot: " + Vector3.Dot(difference, lookDirection)); if (Vector3.Dot(difference, lookDirection) < 0.0f) { - Debug.Log("Failure: Out of FOV"); - handleChaseStop(); + //Debug.Log("Failure: Out of FOV"); + //handleChaseStop(); status = BehaviourTreeNodeStatus.FAILURE; onExit(BehaviourTreeNodeStatus.FAILURE); return status; @@ -146,34 +147,54 @@ public partial class LeafSearch : BehaviourTreeNode //Draw a ray, succeed if ray is unobstructed Vector3 eyePosition = transform.GlobalPosition + eyeOffset; - Ray sightRay = new Ray(eyePosition, plrT.GlobalPosition - eyePosition); + BoxCollider playerCollider = player.GetValueOrDefault().GetComponent().GetCollisionShape(0); + if (playerCollider == null) + { + //Debug.Log("Failure: Player has no collider"); + status = BehaviourTreeNodeStatus.FAILURE; + onExit(BehaviourTreeNodeStatus.FAILURE); + return status; + } + //Ray destination to target the centre of the player's collider instead of transform position + //Since transform position is often the raccoon's base and the ray needs to hit somewhere higher to be more reliable + Vector3 rayDestination = plrT.GlobalPosition + plrT.GlobalScale * playerCollider.PositionOffset; + Ray sightRay = new Ray(eyePosition, rayDestination - 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); - handleChaseStop(); + //TODO sometimes the ray doesn't hit the player even if he's in plain sight because the ray hits the floor the player is on instead??? + //Debug.Log("Failure: Ray hit obstacle named " + sightRayHit.Other.GetValueOrDefault().Name + " ID" + sightRayHit.Other.GetValueOrDefault().EntityId); + //handleChaseStop(); status = BehaviourTreeNodeStatus.FAILURE; onExit(BehaviourTreeNodeStatus.FAILURE); return status; } else if (sightRayHit.Hit && sightRayHit.Other == player) { - Debug.Log("Ray hit player"); + //Debug.Log("Ray hit player"); } //All checks for now succeeded - Debug.Log("Success: Homeowner has sighted player"); + //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) + if (GetNodeData("isAlert") == null) { + SetNodeData("isAlert", true); Audio.PlaySFXOnce2D("event:/Homeowner/homeowner_detect_raccoon"); } - SetNodeData("isAlert", true); + else + { + if (GetNodeData("isAlert") != null && (bool)GetNodeData("isAlert") == false) + { + Audio.PlaySFXOnce2D("event:/Homeowner/homeowner_detect_raccoon"); + } + SetNodeData("isAlert", true); + } status = BehaviourTreeNodeStatus.SUCCESS; onExit(BehaviourTreeNodeStatus.SUCCESS);