start refactoring into object-oriented state machine - walk around done

This commit is contained in:
in0finite 2021-09-19 22:04:06 +02:00
parent 5e08419ac8
commit 9c68ea7a54
8 changed files with 212 additions and 80 deletions

View file

@ -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<Ped> _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<T>() 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()
{
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 220a8feb8843f004bb6cfc4d791cdd4f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,10 @@
namespace SanAndreasUnity.Behaviours.Peds.AI
{
public class IdleState : BaseState
{
public override void UpdateState()
{
_pedAI.StartWalkingAround();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 73cba38be80b3254b873f8c96878a824
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -51,9 +51,9 @@ namespace SanAndreasUnity.Behaviours.Peds.AI
public PedestrianType PedestrianType => this.MyPed.PedDef.DefaultType;
private List<Ped> _enemyPeds = new List<Ped>();
private Ped _currentlyEngagedPed;
public List<Ped> 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();

View file

@ -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;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 222511457f310964cb16bb4f68d52ac5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -7,8 +7,8 @@ namespace SanAndreasUnity.Utilities
void OnBecameActive();
void OnBecameInactive();
bool RepresentsState(System.Type type);
bool RepresentsState<T>() where T : IState;
bool RepresentsState(System.Type type); // TODO: should be removed
bool RepresentsState<T>() where T : IState; // TODO: should be removed
void UpdateState();
void LateUpdateState();
void FixedUpdateState();