diff --git a/Assets/Scripts/Behaviours/PedAI/BaseState.cs b/Assets/Scripts/Behaviours/PedAI/BaseState.cs new file mode 100644 index 00000000..b7c9a8bb --- /dev/null +++ b/Assets/Scripts/Behaviours/PedAI/BaseState.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using SanAndreasUnity.Behaviours.Vehicles; +using SanAndreasUnity.Utilities; + +namespace SanAndreasUnity.Behaviours.Peds.AI +{ + public abstract class BaseState : IState + { + protected PedAI _pedAI { get; private set; } + + protected Ped _ped => _pedAI.MyPed; + protected Ped MyPed => _pedAI.MyPed; + protected List _enemyPeds => _pedAI.EnemyPeds; + + + protected internal virtual void OnAwake(PedAI pedAI) + { + _pedAI = pedAI; + } + + public virtual void OnBecameActive() + { + } + + public virtual void OnBecameInactive() + { + } + + public bool RepresentsState(Type type) + { + throw new NotSupportedException(); + } + + public bool RepresentsState() where T : IState + { + throw new NotSupportedException(); + } + + public virtual void UpdateState() + { + } + + public virtual void LateUpdateState() + { + } + + public virtual void FixedUpdateState() + { + } + + protected internal virtual void OnMyPedDamaged(DamageInfo dmgInfo, Ped.DamageResult dmgResult) + { + } + + protected internal virtual void OnOtherPedDamaged(Ped damagedPed, DamageInfo dmgInfo, Ped.DamageResult dmgResult) + { + } + + protected internal virtual void OnVehicleDamaged(Vehicle vehicle, DamageInfo damageInfo) + { + } + + protected internal virtual void OnDrawGizmosSelected() + { + } + } +} diff --git a/Assets/Scripts/Behaviours/PedAI/BaseState.cs.meta b/Assets/Scripts/Behaviours/PedAI/BaseState.cs.meta new file mode 100644 index 00000000..10dc0301 --- /dev/null +++ b/Assets/Scripts/Behaviours/PedAI/BaseState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 220a8feb8843f004bb6cfc4d791cdd4f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Behaviours/PedAI/IdleState.cs b/Assets/Scripts/Behaviours/PedAI/IdleState.cs new file mode 100644 index 00000000..1a98d7ec --- /dev/null +++ b/Assets/Scripts/Behaviours/PedAI/IdleState.cs @@ -0,0 +1,10 @@ +namespace SanAndreasUnity.Behaviours.Peds.AI +{ + public class IdleState : BaseState + { + public override void UpdateState() + { + _pedAI.StartWalkingAround(); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Behaviours/PedAI/IdleState.cs.meta b/Assets/Scripts/Behaviours/PedAI/IdleState.cs.meta new file mode 100644 index 00000000..3e817776 --- /dev/null +++ b/Assets/Scripts/Behaviours/PedAI/IdleState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 73cba38be80b3254b873f8c96878a824 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Behaviours/PedAI/PedAI.cs b/Assets/Scripts/Behaviours/PedAI/PedAI.cs index 2e106940..73ad0853 100644 --- a/Assets/Scripts/Behaviours/PedAI/PedAI.cs +++ b/Assets/Scripts/Behaviours/PedAI/PedAI.cs @@ -51,9 +51,9 @@ namespace SanAndreasUnity.Behaviours.Peds.AI public PedestrianType PedestrianType => this.MyPed.PedDef.DefaultType; private List _enemyPeds = new List(); - private Ped _currentlyEngagedPed; + public List EnemyPeds => _enemyPeds; - private bool _isFindingPathNodeDelayed = false; + private Ped _currentlyEngagedPed; private void Awake() @@ -250,65 +250,44 @@ namespace SanAndreasUnity.Behaviours.Peds.AI bool ArrivedAtDestinationNode() { - if (!this.HasTargetNode) + return ArrivedAtDestinationNode(this.HasTargetNode ? this.TargetNode : (PathNode?)null, this.transform); + } + + public static bool ArrivedAtDestinationNode(PathMovementData pathMovementData, Transform tr) + { + if (!pathMovementData.destinationNode.HasValue) return false; - if (Vector2.Distance(this.transform.position.ToVec2WithXAndZ(), this.TargetNode.Position.ToVec2WithXAndZ()) - < this.TargetNode.PathWidth / 2f) + if (Vector2.Distance(tr.position.ToVec2WithXAndZ(), pathMovementData.destinationNode.Value.Position.ToVec2WithXAndZ()) + < pathMovementData.destinationNode.Value.PathWidth / 2f) return true; return false; } - void OnArrivedToDestinationNode() + private void OnArrivedToDestinationNode() { - PathNode previousNode = CurrentNode; - CurrentNode = TargetNode; - TargetNode = GetNextPathNode(previousNode, CurrentNode); - this.AssignMoveDestinationBasedOnTargetNode(); + var c = CurrentNode; + var d = TargetNode; + OnArrivedToDestinationNode(ref c, ref d, out Vector3 m); + CurrentNode = c; + TargetNode = d; + _moveDestination = m; } - void AssignMoveDestinationBasedOnTargetNode() + public static void OnArrivedToDestinationNode(PathMovementData pathMovementData) { - Vector2 offset = Random.insideUnitCircle * TargetNode.PathWidth / 2f * 0.9f; - _moveDestination = TargetNode.Position + offset.ToVector3XZ(); + if (!pathMovementData.destinationNode.HasValue) + return; + + PathNode previousNode = pathMovementData.currentNode.GetValueOrDefault(); + pathMovementData.currentNode = pathMovementData.destinationNode; + pathMovementData.destinationNode = GetNextPathNode(previousNode, pathMovementData.currentNode.Value); + pathMovementData.moveDestination = GetMoveDestinationBasedOnTargetNode(pathMovementData.destinationNode.Value); } - void UpdateIdle() + public static Vector3 GetMoveDestinationBasedOnTargetNode(PathNode targetNode) { - this.StartWalkingAround(); - } - - void UpdateWalkingAround() - { - if (this.MyPed.IsInVehicleSeat) - { - // exit vehicle - this.MyPed.OnSubmitPressed(); - return; - } - - if (this.MyPed.IsInVehicle) // wait until we exit vehicle - return; - - // check if we gained some enemies - _enemyPeds.RemoveDeadObjectsIfNotEmpty(); - if (_enemyPeds.Count > 0) - { - this.Action = PedAIAction.Chasing; - return; - } - - if (this.ArrivedAtDestinationNode()) - this.OnArrivedToDestinationNode(); - - if (!this.HasTargetNode) - { - this.FindNextNodeDelayed(); - return; - } - - this.MyPed.IsWalkOn = true; - this.MyPed.Movement = (_moveDestination - this.MyPed.transform.position).normalized; - this.MyPed.Heading = this.MyPed.Movement; + Vector2 offset = Random.insideUnitCircle * targetNode.PathWidth / 2f * 0.9f; + return targetNode.Position + offset.ToVector3XZ(); } void UpdateChasing() @@ -532,6 +511,11 @@ namespace SanAndreasUnity.Behaviours.Peds.AI _enemyPeds.AddIfNotPresent(ped); } + public void StartChasing() + { + this.Action = PedAIAction.Chasing; + } + public void Recruit(Ped recruiterPed) { if (this.Action == PedAIAction.Following) @@ -574,9 +558,8 @@ namespace SanAndreasUnity.Behaviours.Peds.AI } } - PathNode? GetClosestPathNodeToWalk() + public static PathNode? GetClosestPathNodeToWalk(Vector3 pos) { - Vector3 pos = this.MyPed.transform.position; float radius = 200f; var pathNodeInfo = NodeReader.GetAreasInRadius(pos, radius) @@ -592,33 +575,6 @@ namespace SanAndreasUnity.Behaviours.Peds.AI return pathNodeInfo.node; } - private void FindNextNodeDelayed() - { - if (_isFindingPathNodeDelayed) - return; - - _isFindingPathNodeDelayed = true; - - this.CancelInvoke(nameof(this.FindNextNodeDelayedCallback)); - this.Invoke(nameof(this.FindNextNodeDelayedCallback), 2f); - } - - private void FindNextNodeDelayedCallback() - { - _isFindingPathNodeDelayed = false; - - if (this.HasTargetNode) // already assigned ? - return; - - var closestPathNodeToWalk = this.GetClosestPathNodeToWalk(); - if (null == closestPathNodeToWalk) - return; - - this.TargetNode = closestPathNodeToWalk.Value; - this.HasTargetNode = true; - this.AssignMoveDestinationBasedOnTargetNode(); - } - private Ped GetNextPedToAttack() { _enemyPeds.RemoveDeadObjectsIfNotEmpty(); diff --git a/Assets/Scripts/Behaviours/PedAI/WalkAroundState.cs b/Assets/Scripts/Behaviours/PedAI/WalkAroundState.cs new file mode 100644 index 00000000..2989d1d7 --- /dev/null +++ b/Assets/Scripts/Behaviours/PedAI/WalkAroundState.cs @@ -0,0 +1,65 @@ +using SanAndreasUnity.Importing.Paths; +using SanAndreasUnity.Utilities; +using UnityEngine; + +namespace SanAndreasUnity.Behaviours.Peds.AI +{ + public class PathMovementData // don't change to struct, it would be large + { + public PathNode? currentNode; + public PathNode? destinationNode; + public Vector3 moveDestination; + } + + public class WalkAroundState : BaseState + { + private readonly PathMovementData _pathMovementData = new PathMovementData(); + private float _timeWhenAttemptedToFindClosestNode = 0f; + + + public override void UpdateState() + { + if (this.MyPed.IsInVehicleSeat) + { + // exit vehicle + this.MyPed.OnSubmitPressed(); + return; + } + + if (this.MyPed.IsInVehicle) // wait until we exit vehicle + return; + + // check if we gained some enemies + _enemyPeds.RemoveDeadObjectsIfNotEmpty(); + if (_enemyPeds.Count > 0) + { + _pedAI.StartChasing(); + return; + } + + if (PedAI.ArrivedAtDestinationNode(_pathMovementData, _ped.transform)) + PedAI.OnArrivedToDestinationNode(_pathMovementData); + + if (!_pathMovementData.destinationNode.HasValue) + { + if (Time.time - _timeWhenAttemptedToFindClosestNode > 2f) // don't attempt to find it every frame + { + _timeWhenAttemptedToFindClosestNode = Time.time; + + var closestPathNodeToWalk = PedAI.GetClosestPathNodeToWalk(_ped.transform.position); + if (null == closestPathNodeToWalk) + return; + + _pathMovementData.destinationNode = closestPathNodeToWalk; + _pathMovementData.moveDestination = PedAI.GetMoveDestinationBasedOnTargetNode(closestPathNodeToWalk.Value); + } + + return; + } + + this.MyPed.IsWalkOn = true; + this.MyPed.Movement = (_pathMovementData.moveDestination - this.MyPed.transform.position).normalized; + this.MyPed.Heading = this.MyPed.Movement; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Behaviours/PedAI/WalkAroundState.cs.meta b/Assets/Scripts/Behaviours/PedAI/WalkAroundState.cs.meta new file mode 100644 index 00000000..3b36aa70 --- /dev/null +++ b/Assets/Scripts/Behaviours/PedAI/WalkAroundState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 222511457f310964cb16bb4f68d52ac5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Utilities/IState.cs b/Assets/Scripts/Utilities/IState.cs index 5e775b26..0c337e29 100644 --- a/Assets/Scripts/Utilities/IState.cs +++ b/Assets/Scripts/Utilities/IState.cs @@ -7,8 +7,8 @@ namespace SanAndreasUnity.Utilities void OnBecameActive(); void OnBecameInactive(); - bool RepresentsState(System.Type type); - bool RepresentsState() where T : IState; + bool RepresentsState(System.Type type); // TODO: should be removed + bool RepresentsState() where T : IState; // TODO: should be removed void UpdateState(); void LateUpdateState(); void FixedUpdateState();