Attack Animation Bug transferring scripts from the RPG Core combat to Unity 6

I had completed Unity Core Combat, Unity RPG Inventory Systems, Unity RPG Dialogue & Quests, and Unity RPG Shops & Abilities course a few years ago. I was using Unity 2021.3, there’s no bugs on. I was using a MacBook 2018 and Windows 10, so I purchased a new MacBook and I’m using Mac OS 26.3.1.

I also updated to Unity’s NavMesh 2.0, and I got a bug that the player character attack animation triggers right away, but it could move. When it moves the attack animation stops. So far I checked to see if any of my code triggers the attack animation, checked the NavMesh object collision on the NavMesh Surface, and made sure that the NavMesh Surface is on a separate object than the moving game objects. I did noticed that when I move my mouse over the player, the cursor icon changes to an attack icon. That’s the furtherest I’ve gotten and I’m lost.

I’ve uploaded the screenshots and my code.

Thank you for your time and help,

Eric

using UnityEngine;

using UnityEngine.EventSystems;

using UnityEngine.AI;

using RPG.Movement;

using RPG.Combat;

using RPG.Attributes;

using System;

using RPG.Inventories;

namespace RPG.Control

{

public class PlayerController : MonoBehaviour

{

Health health;

ActionStore actionStore;

    \[System.Serializable\]

struct CursorMapping

    {

public CursorType type;

public Texture2D texture;

public Vector2 hospot;

    }



    \[SerializeField\] CursorMapping\[\] cursorMappings = null;

    \[SerializeField\] float maxNavMeshProjectionDistance = 1f;

    \[SerializeField\] float raycastRadius = 1f;

    \[SerializeField\] int numberOfAbilities = 6;

bool isDraggingUI = false;

private void Awake()

    {

health = GetComponent();

actionStore = GetComponent();

    }

void Update()

    {

if (InteractWithUI()) return;

if (health.IsDead())

        {

SetCursor(CursorType.None);

return;

        }

UseAbilities();

if (InteractWithComponent()) return;

if (InteractWithMovement()) return;

SetCursor(CursorType.None);

    }

private bool InteractWithUI()

    {

if (Input.GetMouseButtonUp(0))

        {

isDraggingUI = false;

        }

if (EventSystem.current.IsPointerOverGameObject())

        {

if (Input.GetMouseButtonDown(0))

            {

isDraggingUI = true;

            }

SetCursor(CursorType.UI);

return true;

        }

if (isDraggingUI)

        {

return true;

        }

return false;

    }

//UseAbilities method is used to press a number key on keyboard to use an ability

private void UseAbilities()

    {

for(int i = 0; i < numberOfAbilities; i++)

        {

if (Input.GetKeyDown(KeyCode.Alpha1 + i))

            {                   

actionStore.Use(i, gameObject);

            }

        }

    }

private bool InteractWithComponent()

    {

RaycastHit[] hits = RaycastAllSorted();

foreach (RaycastHit hit in hits)

        {

IRaycastable[] raycastables = hit.transform.GetComponents();

foreach(IRaycastable raycastable in raycastables)

            {

if (raycastable.HandleRaycast(this))

                {

SetCursor(raycastable.GetCursorType());

return true;

                }

            }

        }

return false;

    }

RaycastHit[] RaycastAllSorted()

    {

RaycastHit[] hits = Physics.SphereCastAll(GetMouseRay(), raycastRadius);

float[] distances = new float[hits.Length];

for(int i = 0; i < hits.Length; i++)

        {

distances[i] = hits[i].distance;

        }

Array.Sort(distances, hits);

return hits;

    }

private bool InteractWithMovement()

    {           

Vector3 target;

bool hasHit = RaycastNavMesh(out target);

if (hasHit)

        {

if (!GetComponent().CanMoveTo(target)) return false;

if (Input.GetMouseButton(0))

            {

GetComponent().StartMoveAction(target, 1f);

            }

SetCursor(CursorType.Movement);

return true;

        }

return false;

    }

private bool RaycastNavMesh(out Vector3 target)

    {

target = new Vector3();

RaycastHit hit;

bool hasHit = Physics.Raycast(GetMouseRay(), out hit);

if (!hasHit) return false;

NavMeshHit navMeshHit;

bool hasCastToNavMesh = NavMesh.SamplePosition(hit.point, out navMeshHit, maxNavMeshProjectionDistance, NavMesh.AllAreas);

if (!hasCastToNavMesh) return false;

target = navMeshHit.position;

return true;

    }

private void SetCursor(CursorType type)

    {

CursorMapping mapping = GetCursorMapping(type);

Cursor.SetCursor(mapping.texture, mapping.hospot, CursorMode.Auto);

    }

private CursorMapping GetCursorMapping(CursorType type)

    {

foreach(CursorMapping mapping in cursorMappings)

        {

if(mapping.type == type)

            {

return mapping;

            }

        }

return cursorMappings[0];

    }

public static Ray GetMouseRay()

    {

return Camera.main.ScreenPointToRay(Input.mousePosition);

    }

}

}

using RPG.Core;

using RPG.Saving;

using UnityEngine;

using UnityEngine.AI;

using RPG.Attributes;

namespace RPG.Movement

{

public class CharacterMove : MonoBehaviour, IAction, ISaveable

{

    \[SerializeField\] Transform target;

    \[SerializeField\] float maxSpeed = 6f;

    \[Header("Adjust for Player Path Distance to see if it feels right in the game")\]

    \[SerializeField\] float maxNavPathLength = 40f; //Test to see if the player path feels right in game

NavMeshAgent navMeshAgent;

Health health;

void Awake()

    {

navMeshAgent = GetComponent();

health = GetComponent();

    }

// Update is called once per frame

void Update()

    {

navMeshAgent.enabled = !health.IsDead();

UpdateAnimator();

    }

public void StartMoveAction(Vector3 destination, float speedFraction)

    {

GetComponent().StartAction(this);

MoveTo(destination, speedFraction);

    }

public bool CanMoveTo(Vector3 destination)

    {

NavMeshPath path = new NavMeshPath();

bool hasPath = NavMesh.CalculatePath(transform.position, destination, NavMesh.AllAreas, path);

if (!hasPath) return false;

if (path.status != NavMeshPathStatus.PathComplete) return false;

if (GetPathLength(path) > maxNavPathLength) return false;

return true;

    }

public void MoveTo(Vector3 destination, float speedFraction)

    {

navMeshAgent.destination = destination;

navMeshAgent.speed = maxSpeed * Mathf.Clamp01(speedFraction);

navMeshAgent.isStopped = false;

    }

public void CancelAttack()

    {

navMeshAgent.isStopped = true;

    }

private void UpdateAnimator()

    {

Vector3 velocity = navMeshAgent.velocity;

Vector3 localVelocty = transform.InverseTransformDirection(velocity);

float speed = localVelocty.z;

GetComponent().SetFloat(“ForwardSpeed”, speed);

    }

private float GetPathLength(NavMeshPath path)

    {

float total = 0;

if (path.corners.Length < 2) return total;

for (int i = 0; i < path.corners.Length - 1; i++)

        {

total += Vector3.Distance(path.corners[i], path.corners[i + 1]);

        }

return total;

    }



    \[System.Serializable\]

struct MoverSaveData

    {

public SerializableVector3 position;

public SerializableVector3 rotation;

    }

public object CaptureState()

    {

MoverSaveData data = new MoverSaveData();

data.position = new SerializableVector3(transform.position);

data.rotation = new SerializableVector3(transform.eulerAngles);

return data;

    }

public void RestoreState(object state)

    {

MoverSaveData data = (MoverSaveData)state;

GetComponent().enabled = false;

transform.position = data.position.ToVector();

transform.eulerAngles = data.rotation.ToVector();

GetComponent().enabled = true;

    }

}

}

using UnityEngine;

using Random = UnityEngine.Random;

using RPG.Movement;

using RPG.Core;

using RPG.Attributes;

using RPG.Stats;

using System.Collections.Generic;

using RPG.Utils;

using System;

using RPG.Inventories;

namespace RPG.Combat

{

public class Fighter : MonoBehaviour, IAction, IModifierProvider

{

    \[SerializeField\] float timeBetweenAttacks = 1f;

    \[SerializeField\] Transform rightHandTransform = null;

    \[SerializeField\] Transform leftHandTransform = null;

    \[SerializeField\] WeaponConfig defaultWeapon = null;

    \[SerializeField\] bool isRightHanded = true;

    \[SerializeField\] float autoAttackRange = 4f;

Health target;

Equipment equipment;

float timeSinceLastAttack = Mathf.Infinity;

WeaponConfig currentWeaponConfig;

LazyValue currentWeapon;

void Awake()

    {

currentWeaponConfig = defaultWeapon;

currentWeapon = new LazyValue(SetupDefaultWeapon);

equipment = GetComponent();

if (equipment)

        {

equipment.equipmentUpdated += UpdateWeapon;

        }

    }

private Weapon SetupDefaultWeapon()

    {

return AttachWeapon(defaultWeapon);

    }

void Start()

    {

currentWeapon.ForceInit();

    }      

private void Update()

    {

timeSinceLastAttack += Time.deltaTime;

if (target == null) return;

if (target.IsDead()) return;

if (!GetIsInRange(target.transform))

        {

GetComponent().MoveTo(target.transform.position, 1f);

        }

else

        {

GetComponent().CancelAttack();

AttackBehavior();

        }

    }

public void EquipWeapon(WeaponConfig weapon)

    {

currentWeaponConfig = weapon;

currentWeapon.value = AttachWeapon(weapon);

    }

void UpdateWeapon()

    {

var weapon = equipment.GetItemInSlot(EquipLocation.Weapon) as WeaponConfig;

if(weapon == null)

        {

EquipWeapon(defaultWeapon);

        }

else

        {

EquipWeapon(weapon);

        }

    }

private Weapon AttachWeapon(WeaponConfig weapon)

    {

Animator animator = GetComponent();

return weapon.Spawn(rightHandTransform, leftHandTransform, animator);

    }

public Health GetTarget()

    {

return target;

    }

public Transform GetHandTransform(bool isRightHand)

    {

if (isRightHand)

        {

return rightHandTransform;

        }

else

        {

return leftHandTransform;

        }

    }

private void AttackBehavior()

    {

transform.LookAt(target.transform);

if(timeSinceLastAttack > timeBetweenAttacks)

        {

TriggerAttack();

timeSinceLastAttack = 0;

        }

    }

private Health FindNewTargetInRange()

    {

Health best = null;

float bestDistance = Mathf.Infinity;

foreach (var candidate in FindAllTargetsInRange())

        {

float candidateDistance = Vector3.Distance(

transform.position, candidate.transform.position);

if (candidateDistance < bestDistance)

            {

best = candidate;

bestDistance = candidateDistance;

            }

        }

return best;

    }

private IEnumerable FindAllTargetsInRange()

    {

RaycastHit[] raycastHits = Physics.SphereCastAll(transform.position,

autoAttackRange, Vector3.up);

foreach (var hit in raycastHits)

        {

Health health = hit.transform.GetComponent();

if (health == null) continue;

if (health.IsDead()) continue;

if (health.gameObject == gameObject) continue;

yield return health;

        }

    }

private void TriggerAttack()

    {

GetComponent().ResetTrigger(“StopAttack”);

GetComponent().SetTrigger(“Attack”);

Debug.Log(“Attack trigger triggered”);

    }

//Animation Event

void Hit()

    {  

if(target == null) { return; }

float damage = GetComponent().GetStat(Stat.Attack);

float critHit = GetComponent().GetStat(Stat.CritChance);

float critDamage = GetComponent().GetStat(Stat.CritDamage);

BaseStats targetBaseStats = target.GetComponent();

if(targetBaseStats != null)

        {

float defense = targetBaseStats.GetStat(Stat.Defense);

//How damage is calulated in tutorial is commented out.

damage /= 1 + defense / damage;

//damage = damage - defense;

float critHitRoll = Random.Range(0f , 1f);

if(critHitRoll < critHit)

            {

damage *= (1 + critDamage);

            }

        }

if(currentWeapon.value != null)

        {

currentWeapon.value.OnHit();

        }

if (currentWeaponConfig.HasProjectile())

        {

currentWeaponConfig.LaunchProjectile(rightHandTransform, leftHandTransform, target, gameObject, damage);

        }

else

        {

target.TakeDamage(gameObject, damage);

        }

    }

//Wrap Shoot with Hit for animation event, not to repeat code from Hit method

void Shoot()

    {

Hit();

    }

private bool GetIsInRange(Transform targetTransform)

    {

return Vector3.Distance(transform.position, targetTransform.position) < currentWeaponConfig.GetRange();

    }

public bool CanAttack(GameObject combatTarget)

    {

if(combatTarget == null)

        {

return false;

        }

if (!GetComponent().CanMoveTo(combatTarget.transform.position) && !GetIsInRange(combatTarget.transform))

        {

return false;

        }

Health targetToTest = combatTarget.GetComponent();

return targetToTest != null && !targetToTest.IsDead();

    }

public void Attack(GameObject CombatTarget)

    {

GetComponent().StartAction(this);

target = CombatTarget.GetComponent();

    }

public void CancelAttack()

    {

StopAttack();

target = null;

    }

private void StopAttack()

    {

GetComponent().ResetTrigger(“Attack”);

GetComponent().SetTrigger(“StopAttack”);

    }

public IEnumerable GetAdditiveModifiers(Stat stat)

    {

if(stat == Stat.Attack)

        {

yield return currentWeaponConfig.GetDamage();

        }

    }

public IEnumerable GetPercentageModifiers(Stat stat)

    {

if(stat == Stat.Attack)

        {

yield return currentWeaponConfig.GetPercentageBonus();

        }

    }

}

}