using RPG.Control;
using UnityEngine;
namespace RPG.States.Enemies
{
public class EnemyPatrolState : EnemyBaseState
{
private const string NextPatrolPointIndexKey = "NextPatrolPointIndex";
public EnemyPatrolState(EnemyStateMachine stateMachine) : base(stateMachine) {}
private float movementSpeed = 0.5f;
private float acceptanceRadius = 2f;
private float dwellTime = 2f;
private Vector3 targetPatrolPoint;
public override void Enter()
{
if (stateMachine.PatrolPath == null)
{
stateMachine.SwitchState(new EnemyIdleState(stateMachine));
return;
}
int index;
// Check blackboard for key, set index if key is set
if (stateMachine.Blackboard.ContainsKey(NextPatrolPointIndexKey))
{
index = stateMachine.Blackboard.GetValueAsInt(NextPatrolPointIndexKey);
}
else
{
// The first time we enter a Patrol state, the index will not be set in the blackboard
// So we get it from the PatrolPath's GetNearestIndex
// We will also be resetting the index if the enemy goes into 'EnemyChasingState.cs'
index = stateMachine.PatrolPath.GetNearestIndex(stateMachine.transform.position);
}
// Set our goal
targetPatrolPoint = stateMachine.PatrolPath.GetWaypoint(index);
PatrolPoint patrolPoint = stateMachine.PatrolPath.GetPatrolPoint(index);
if (patrolPoint) // If the current index has a PatrolPoint attached, then we can adjust the settings from the default
{
movementSpeed = stateMachine.MovementSpeed * patrolPoint.SpeedModifier;
acceptanceRadius = patrolPoint.AcceptanceRadius;
dwellTime = patrolPoint.DwellTime;
}
else // if not, calculate the movementSpeed as a percentage of the stateMachine's movement speed
{
movementSpeed *= stateMachine.MovementSpeed;
}
// Squaring the acceptanceRadius (to save calculation time when we use 'SqrMagnitude')
acceptanceRadius *= acceptanceRadius;
// next waypoint index setup
stateMachine.Blackboard[NextPatrolPointIndexKey] = stateMachine.PatrolPath.GetNextIndex(index);
// Since the waypoint won't move, set the destination here on the Agent
stateMachine.Agent.SetDestination(targetPatrolPoint);
// Set the animation
stateMachine.Animator.CrossFadeInFixedTime(FreeLookBlendTreeHash, stateMachine.CrossFadeDuration);
}
public override void Tick(float deltaTime)
{
if (IsInChaseRange() && IsAggro()) // if you want to consider the optional aggregation, add "&& IsAggro()" to the if statement of this line
{
// if you want the vision system to work, uncomment the if statement below:
// if (CanSeePlayer()) {
// Clearing key to ensure that at the end of the battle, the enemy finds the nearest waypoint
stateMachine.Blackboard.Remove(NextPatrolPointIndexKey);
stateMachine.SwitchState(new EnemyChasingState(stateMachine));
return;
// }
}
if (IsInAcceptanceRange())
{
// Once we're close enough to the waypoint, we head to a Dwell state
stateMachine.SwitchState(new EnemyDwellState(stateMachine, dwellTime));
return;
}
// Chasing Code
Vector3 lastPosition = stateMachine.transform.position;
MoveToWayPoint(deltaTime);
Vector3 deltaMovement = lastPosition - stateMachine.transform.position;
float deltaMagnitude = deltaMovement.magnitude;
float grossSpeed = deltaMagnitude / deltaTime;
stateMachine.Animator.SetFloat(FreeLookSpeedHash, grossSpeed / stateMachine.MovementSpeed, stateMachine.AnimatorDampTime, deltaTime);
if (deltaMagnitude > 0)
{
FaceTarget(stateMachine.transform.position - deltaMovement, deltaTime);
}
else
{
FaceTarget(targetPatrolPoint, deltaTime);
}
}
private bool IsInAcceptanceRange()
{
return (stateMachine.transform.position - targetPatrolPoint).sqrMagnitude < acceptanceRadius;
}
public override void Exit()
{
stateMachine.Agent.ResetPath();
stateMachine.Agent.velocity = Vector3.zero;
}
void MoveToWayPoint(float deltaTime)
{
Vector3 direction = stateMachine.Agent.desiredVelocity.normalized;
Move(direction * movementSpeed, deltaTime);
stateMachine.Agent.velocity = stateMachine.CharacterController.velocity;
stateMachine.Agent.nextPosition = stateMachine.transform.position;
}
}
}
I need to remember to go back to this bugā¦