Fixed AI for the Gameplay Scene #275
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,3 @@
|
|||
Name: MainGameWithAIFixed
|
||||
ID: 89830755
|
||||
Type: 5
|
|
@ -40,15 +40,7 @@ namespace SHADE_Scripting.AIBehaviour.BehaviourTree
|
|||
|
||||
//awake and update functions
|
||||
//the only segment in the entire AI that is dependent on the engine
|
||||
|
||||
private bool test = false;
|
||||
|
||||
protected override void awake()
|
||||
{
|
||||
AwakeCall();
|
||||
}
|
||||
|
||||
protected override void start()
|
||||
{
|
||||
_root = CreateTree();
|
||||
_root.InitialiseNode(this);
|
||||
|
@ -59,7 +51,7 @@ namespace SHADE_Scripting.AIBehaviour.BehaviourTree
|
|||
_root?.Evaluate();
|
||||
Tick();
|
||||
}
|
||||
protected abstract void AwakeCall();
|
||||
|
||||
protected abstract void Initialise();
|
||||
protected abstract void Tick();
|
||||
}
|
||||
|
|
|
@ -28,14 +28,13 @@ public partial class Homeowner1 : BehaviourTree
|
|||
private BehaviourTreeEvents _events { get; set; }
|
||||
public override BehaviourTreeEvents events { get => _events; }
|
||||
|
||||
[Tooltip("The player the AI should chase and attempt to capture")]
|
||||
public GameObject player;
|
||||
|
||||
//PATROL FIELDS///////////////////////////////////////////////////////////////
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("The list of waypoints for the AI to cycle around")]
|
||||
private List<Vector3> waypoints = new List<Vector3>();
|
||||
private GameObject waypointsPool;
|
||||
|
||||
private List<GameObject> waypoints;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("The AI will patrol at this speed")]
|
||||
|
@ -79,32 +78,18 @@ public partial class Homeowner1 : BehaviourTree
|
|||
//AI tree
|
||||
public partial class Homeowner1 : BehaviourTree
|
||||
{
|
||||
Transform _thisTransform = null;
|
||||
RigidBody _thisRigidbody = null;
|
||||
GameObject _playerObject;
|
||||
private LeafPatrol leafPatrol;
|
||||
|
||||
protected override void AwakeCall()
|
||||
{
|
||||
_thisTransform = GetComponent<Transform>();
|
||||
if (!_thisTransform)
|
||||
Debug.LogError("EMPTY TRANSFORM");
|
||||
|
||||
_thisRigidbody = GetComponent<RigidBody>();
|
||||
if (!_thisRigidbody)
|
||||
Debug.LogError("EMPTY RIGIDBODY");
|
||||
|
||||
if (!player)
|
||||
Debug.Log("PLAYER MISSING!");
|
||||
|
||||
//_playerObject = GameObject.Find("Player").GetValueOrDefault();
|
||||
}
|
||||
|
||||
//Called at the start
|
||||
protected override void Initialise()
|
||||
{
|
||||
_events = new Homeowner1Events(this);
|
||||
events.Initialise();
|
||||
|
||||
//Initialise the waypoints here
|
||||
if (waypointsPool)
|
||||
{
|
||||
waypoints = (List<GameObject>)waypointsPool.GetChildren();
|
||||
SetData("waypoints", waypoints);
|
||||
}
|
||||
}
|
||||
|
||||
//Called every tick
|
||||
|
@ -112,8 +97,8 @@ public partial class Homeowner1 : BehaviourTree
|
|||
{
|
||||
events.Tick();
|
||||
|
||||
//Footsteps SFX, move them somewhere else soon
|
||||
float velocity = GetComponent<RigidBody>().LinearVelocity.GetMagnitude();
|
||||
leafPatrol.waypoints = waypoints;
|
||||
|
||||
footstepTimeRemaining -= velocity * Time.DeltaTimeF;
|
||||
if (footstepTimeRemaining < 0.0f)
|
||||
|
@ -128,22 +113,20 @@ public partial class Homeowner1 : BehaviourTree
|
|||
//The tree is called from the root every tick
|
||||
protected override BehaviourTreeNode CreateTree()
|
||||
{
|
||||
leafPatrol = new LeafPatrol("Patrol", _thisTransform, waypoints, patrolSpeed, turningSpeed, _thisRigidbody);
|
||||
//Start from the root, structure it like this to make it look like a tree
|
||||
BehaviourTreeNode root = new BehaviourTreeSelector("Root", new List<BehaviourTreeNode>
|
||||
{
|
||||
/* new BehaviourTreeSequence("Alerted", new List<BehaviourTreeNode>
|
||||
new BehaviourTreeSequence("Alerted", new List<BehaviourTreeNode>
|
||||
{
|
||||
new LeafSearch("SearchFOV", _thisTransform, eyeOffset, sightDistance),
|
||||
new LeafSearch("SearchFOV", GetComponent<Transform>(), eyeOffset, sightDistance),
|
||||
new BehaviourTreeSequence("CatchPlayer", new List<BehaviourTreeNode>
|
||||
{
|
||||
new LeafChase("Chasing", _thisTransform, _thisRigidbody, chaseSpeed, turningSpeed, distanceToCapture, captureTime),
|
||||
new LeafAttack("Attacking")
|
||||
new LeafChase("Chasing", GetComponent<Transform>(), GetComponent<RigidBody>(), chaseSpeed, turningSpeed, distanceToCapture, captureTime),
|
||||
new LeafAttack("Attacking", GameObject.Find("Player").GetValueOrDefault())
|
||||
})
|
||||
}),*/
|
||||
leafPatrol
|
||||
}),
|
||||
new LeafPatrol("Patrol", GetComponent<Transform>(), patrolSpeed, turningSpeed, GetComponent<RigidBody>())
|
||||
});
|
||||
|
||||
return root;
|
||||
}
|
||||
}
|
|
@ -28,22 +28,13 @@ public partial class LeafAttack : BehaviourTreeNode
|
|||
//FUNCTIONS
|
||||
public partial class LeafAttack : BehaviourTreeNode
|
||||
{
|
||||
public LeafAttack(string name) : base (name)
|
||||
public LeafAttack(string name, GameObject p) : base (name)
|
||||
{
|
||||
//player = p;
|
||||
player = p;
|
||||
}
|
||||
|
||||
public override BehaviourTreeNodeStatus Evaluate()
|
||||
{
|
||||
{
|
||||
if (!player)
|
||||
{
|
||||
player = GameObject.Find("Player").GetValueOrDefault();
|
||||
Debug.Log("HERE2");
|
||||
if (!player) { return BehaviourTreeNodeStatus.FAILURE; }
|
||||
}
|
||||
}
|
||||
|
||||
//Debug.LogWarning("LeafAttack");
|
||||
//Fail if no target in blackboard?
|
||||
|
||||
|
|
|
@ -22,10 +22,10 @@ public partial class LeafPatrol : BehaviourTreeNode
|
|||
{
|
||||
//Waypoints and movement
|
||||
private Transform transform;
|
||||
public List<Vector3> waypoints;
|
||||
private List<GameObject> waypoints;
|
||||
private RigidBody rb;
|
||||
private float patrolSpeed = 1.0f;
|
||||
private float turningSpeed = 5.0f;
|
||||
private float patrolSpeed;
|
||||
private float turningSpeed;
|
||||
private float retreatTimer = 0.0f;
|
||||
private int currentWaypointIndex = 0;
|
||||
private bool retreatState = false;
|
||||
|
@ -42,10 +42,9 @@ 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, Transform t, List<Vector3> wps, float patrolSpeed, float turnSpeed, RigidBody rb) : base(name)
|
||||
public LeafPatrol(string name, Transform t, float patrolSpeed, float turnSpeed, RigidBody rb) : base(name)
|
||||
{
|
||||
transform = t;
|
||||
waypoints = wps;
|
||||
this.patrolSpeed = patrolSpeed;
|
||||
turningSpeed = turnSpeed;
|
||||
this.rb = rb;
|
||||
|
@ -59,6 +58,10 @@ public partial class LeafPatrol : BehaviourTreeNode
|
|||
{
|
||||
//Debug.LogWarning("LeafPatrol");
|
||||
onEnter(BehaviourTreeNodeStatus.RUNNING);
|
||||
if(GetNodeData("currentWaypointIndex") == null)
|
||||
{
|
||||
SetNodeData("currentWaypointIndex", 0);
|
||||
}
|
||||
if (isWaiting) DelayAtWaypoint();
|
||||
else MoveToWaypoint();
|
||||
|
||||
|
@ -80,27 +83,25 @@ public partial class LeafPatrol : BehaviourTreeNode
|
|||
return;
|
||||
}
|
||||
|
||||
Vector3 remainingDistance = Vector3.Zero;
|
||||
Debug.Log($"{waypoints.Count}");
|
||||
if (currentWaypointIndex > 0)
|
||||
{
|
||||
waypoints = (List<GameObject>)GetNodeData("waypoints");
|
||||
Vector3 targetPosition = waypoints[currentWaypointIndex].GetComponent<Transform>().GlobalPosition;
|
||||
|
||||
Vector3 targetPosition = waypoints[currentWaypointIndex];
|
||||
|
||||
//Reach waypoint by X and Z being near enough
|
||||
//Do not consider Y of waypoints yet
|
||||
remainingDistance = targetPosition - transform.GlobalPosition;
|
||||
remainingDistance.y = 0.0f;
|
||||
}
|
||||
//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)
|
||||
{
|
||||
//Cycle waypoints
|
||||
++currentWaypointIndex;
|
||||
if (currentWaypointIndex >= waypoints.Count)
|
||||
if (currentWaypointIndex >= waypoints.Count())
|
||||
currentWaypointIndex = 0;
|
||||
|
||||
//Write to blackboard
|
||||
SetNodeData("currentWaypointIndex", currentWaypointIndex);
|
||||
|
||||
waitCounter = 0.0f;
|
||||
isWaiting = true;
|
||||
}
|
||||
|
@ -115,7 +116,7 @@ public partial class LeafPatrol : BehaviourTreeNode
|
|||
//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 = waypoints[currentWaypointIndex] - transform.GlobalPosition;
|
||||
Vector3 normalisedDifference = targetPosition - transform.GlobalPosition;
|
||||
normalisedDifference.y = 0.0f; //Do not move vertically
|
||||
normalisedDifference /= normalisedDifference.GetMagnitude();
|
||||
//Debug.Log("Normalised Difference x " + normalisedDifference.x.ToString() + " z " + normalisedDifference.z.ToString());
|
||||
|
|
|
@ -21,11 +21,10 @@ using System.Threading.Tasks;
|
|||
//VARIABLES HERE
|
||||
public partial class LeafSearch : BehaviourTreeNode
|
||||
{
|
||||
private GameObject player;
|
||||
private Transform transform;
|
||||
private Vector3 eyeOffset;
|
||||
private float sightDistance;
|
||||
|
||||
private GameObject? player; //To be searched for and marked
|
||||
}
|
||||
|
||||
//FUNCTIONS HERE
|
||||
|
@ -33,111 +32,143 @@ public partial class LeafSearch : BehaviourTreeNode
|
|||
{
|
||||
public LeafSearch(string name, Transform t, Vector3 eo, float sDist) : base(name)
|
||||
{
|
||||
Debug.Log($"===============================PLAYER: {t}");
|
||||
// player = p;
|
||||
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<GameObject> waypoints = (List<GameObject>)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<Transform>().GlobalPosition).GetSqrMagnitude() <
|
||||
(transform.GlobalPosition - waypoints[nearestWaypointIndex].GetComponent<Transform>().GlobalPosition).GetSqrMagnitude())
|
||||
{
|
||||
nearestWaypointIndex = i;
|
||||
}
|
||||
}
|
||||
SetNodeData("currentWaypointIndex", nearestWaypointIndex);
|
||||
}
|
||||
|
||||
public override BehaviourTreeNodeStatus Evaluate()
|
||||
{
|
||||
{
|
||||
if (!player)
|
||||
{
|
||||
player = GameObject.Find("Player").GetValueOrDefault();
|
||||
if (!player) { Debug.Log("HERE1"); return BehaviourTreeNodeStatus.FAILURE; }
|
||||
}
|
||||
}
|
||||
|
||||
//Debug.LogWarning("LeafSearch");
|
||||
onEnter(BehaviourTreeNodeStatus.RUNNING);
|
||||
|
||||
//Fail if unable to find a player
|
||||
//Get player's transform
|
||||
Transform plrT = player.GetComponent<Transform>();
|
||||
//Search for player
|
||||
player = GameObject.Find("Player");
|
||||
|
||||
//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)
|
||||
//Automatically fail if no player is found
|
||||
if (player == null)
|
||||
{
|
||||
//Debug.Log("Failure: Too far");
|
||||
if (GetNodeData("isAlert") != null && (bool)GetNodeData("isAlert") == true)
|
||||
{
|
||||
Debug.Log("AI play unalert hmm");
|
||||
}
|
||||
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)
|
||||
else
|
||||
{
|
||||
//Debug.Log("Failure: Out of FOV");
|
||||
if (GetNodeData("isAlert") != null && (bool)GetNodeData("isAlert") == true)
|
||||
//Fail if unable to find a player
|
||||
//Get player's transform
|
||||
Transform plrT = player.GetValueOrDefault().GetComponent<Transform>();
|
||||
|
||||
//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("AI play unalert hmm");
|
||||
//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;
|
||||
}
|
||||
SetNodeData("isAlert", false);
|
||||
status = BehaviourTreeNodeStatus.FAILURE;
|
||||
onExit(BehaviourTreeNodeStatus.FAILURE);
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
//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");
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue