[SOLVED, Check the last comment below]
OK so… Now that I’m done with my personal Dodging State, I ran into a brand new (OK kinda old, because I asked about this when I was still using a Point-and-click system, but the problem got nullified with the new Third-Person System) problem.
My problem, this time, is that my enemies can’t move around Obstacles. Before I came to ask about it, I went to ‘Mover.cs’ (and ‘Fighter.cs’), copied a little bit of the work in there, and tried tweaking and working on something new to get it to work, BUT… It failed. Here’s what I tried doing in ‘EnemyChasingState.cs’ to try and combat this problem (everything new is labelled with ‘TEST - 27/5/2024’ for convenience):
```using UnityEngine;
using UnityEngine.AI;
namespace RPG.States.Enemies
{
public class EnemyChasingState : EnemyBaseState
{
public EnemyChasingState(EnemyStateMachine stateMachine) : base(stateMachine) {}
private float attackingRange;
public readonly int TargetingForwardSpeedHash = Animator.StringToHash("TargetingForwardSpeed");
public override void Enter()
{
Debug.Log($"{stateMachine.gameObject.name} has entered chasing state");
stateMachine.Animator.CrossFadeInFixedTime(FreeLookBlendTreeHash, stateMachine.CrossFadeDuration);
attackingRange = stateMachine.Fighter.GetAttackingRange();
attackingRange *= attackingRange;
}
public override void Tick(float deltaTime)
{
if (!IsInChaseRange() && !IsAggrevated())
{
stateMachine.TriggerOnPlayerOutOfChaseRange();
stateMachine.SwitchState(new EnemyIdleState(stateMachine));
return;
}
Vector3 lastPosition = stateMachine.transform.position;
if (IsInAttackingRange())
{
if (!stateMachine.CooldownTokenManager.HasCooldown("Attack"))
{
// you're not on Attack Cooldown, so you're quite aggressive, so switch to attacking state
Debug.Log($"{stateMachine.gameObject.name} is exiting chase state to attack state");
stateMachine.SwitchState(new EnemyAttackingState(stateMachine));
return;
}
else
{
Move(deltaTime);
stateMachine.Animator.SetFloat(FreeLookSpeedHash, 0);
// TEST (SUCCESS): get the enemies to point at each other, instead of the player, if they're fighting one another:
if (stateMachine.LastAttacker == null) stateMachine.SwitchState(new EnemyIdleState(stateMachine)); // ensures the enemy knows what to do if the enemy is dead (if this isn't here, NREs will be unleashed like bombs...!)
else FaceTarget(stateMachine.LastAttacker.transform.position, deltaTime);
// ORIGINAL:
// FaceTarget(stateMachine.Player.transform.position, deltaTime);
return;
}
}
// else MoveToTarget(deltaTime); (Replaced with Temporary 'TEST - 27/5/2024' Below):
// TEST - 27/5/2024 (THE ENTIRE 'else' STATEMENT BELOW IS A TEST):
else
{
Vector3 targetPosition = stateMachine.LastAttacker != null ? stateMachine.LastAttacker.transform.position : stateMachine.Player.transform.position;
if (!HasLineOfSight(targetPosition))
{
MoveAroundObstacles(targetPosition, deltaTime);
}
else MoveToTarget(deltaTime);
}
Vector3 deltaMovement = lastPosition - stateMachine.transform.position;
float deltaMagnitude = deltaMovement.magnitude;
if (deltaMagnitude > 0)
{
// if the game is not paused:
FaceTarget(stateMachine.transform.position - deltaMovement, deltaTime);
float grossSpeed = deltaMagnitude / deltaTime;
stateMachine.Animator.SetFloat(FreeLookSpeedHash, grossSpeed / stateMachine.MovementSpeed, stateMachine.AnimatorDampTime, deltaTime);
}
else
{
// if the game is paused:
FaceTarget(stateMachine.Player.transform.position, deltaTime);
stateMachine.Animator.SetFloat(FreeLookSpeedHash, 0f);
}
}
public override void Exit()
{
stateMachine.Agent.ResetPath();
stateMachine.Agent.velocity = Vector3.zero;
}
private bool IsInAttackingRange()
{
if (stateMachine.LastAttacker == null)
{
return Vector3.SqrMagnitude(stateMachine.Player.transform.position - stateMachine.transform.position) <= attackingRange;
}
else return Vector3.SqrMagnitude(stateMachine.LastAttacker.transform.position - stateMachine.transform.position) <= attackingRange;
}
public void MoveToTarget(float deltaTime)
{
if (!stateMachine.Agent.enabled) stateMachine.Agent.enabled = true; // turn on the NavMeshAgent, otherwise the enemy won't be able to chase you down...
// stateMachine.Agent.destination = stateMachine.Player.transform.position;
// TEST: Instead of just hunting the player down everytime this enemy is attacked, hunt down the LastAttacker (it can be the player or another NPC...).
// If you found none, and this function is called, hunt the player down (FIX THIS, BECAUSE IT SOMETIMES GETS NPCs TO ATTACK THE PLAYER FOR NO REASON!):
stateMachine.Agent.destination = stateMachine.LastAttacker != null ? stateMachine.LastAttacker.transform.position : stateMachine.Player.transform.position;
Vector3 desiredVelocity = stateMachine.Agent.desiredVelocity.normalized; // Normalized Desired Speed of the NPC
Move(desiredVelocity * stateMachine.MovementSpeed, deltaTime); // Go to the player, at that desired speed
stateMachine.Agent.velocity = stateMachine.CharacterController.velocity; // The velocity of the player
stateMachine.Agent.nextPosition = stateMachine.transform.position; // The next position to aim for
}
// TEST - 27/5/2024 (ALL FUNCTIONS BELOW ARE TEMPORARY TESTS, AND MAY OR MAY NOT STAY IN THE END):
private bool HasLineOfSight(Vector3 targetPosition)
{
Vector3 directionToTarget = targetPosition - stateMachine.transform.position;
return !Physics.Raycast(stateMachine.transform.position, directionToTarget.normalized, LayerMask.GetMask("Obstacle"));
}
private void MoveAroundObstacles(Vector3 targetPosition, float deltaTime)
{
while (!HasLineOfSight(targetPosition) && CanMoveTo(targetPosition))
{
MoveToTarget(deltaTime);
return;
}
stateMachine.SwitchState(new EnemyIdleState(stateMachine));
}
private bool CanMoveTo(Vector3 destination)
{
NavMeshPath path = new NavMeshPath();
bool hasPath = NavMesh.CalculatePath(stateMachine.transform.position, destination, NavMesh.AllAreas, path);
if (!hasPath) return false;
if (path.status != NavMeshPathStatus.PathComplete) return false;
return true;
}
}
}
SO… Like I said, the goal is that if there’s an obstacle between the player and the enemy, and there’s a path (preferably under 100 meters) that can be walked by the enemy to get to the player, then he’ll go ahead and take that path. If not, and you’re a melee warrior, just give up already… (if the enemy is a ranger or a wizard, based on the skill associated with the weapon, you can fight from a distance if the player is within your chase range, and you’re hostile (either by default nature, or because the player attacked you), but out of your physical range… I recall we covered that at the end of the first Core Combat Creator course back in the day, but I want to clean it up for the Third-Person version as well)