Moved from swarm to NavMesh - need to fix the enemy navigation! Review the animation controller to see if anything is being triggered.
This commit is contained in:
@@ -1,10 +1,17 @@
|
||||
using Synty.AnimationBaseLocomotion.Samples;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
|
||||
[RequireComponent(typeof(Rigidbody))]
|
||||
[RequireComponent(typeof(EnemyRagdoll))]
|
||||
[RequireComponent(typeof(NavMeshAgent))]
|
||||
public class EnemyAI : MonoBehaviour
|
||||
{
|
||||
public enum EnemyType { Horde, Siege }
|
||||
[Header("Targeting Priorities")]
|
||||
public EnemyType type = EnemyType.Horde;
|
||||
[Range(0f, 1f)]
|
||||
public float hordeBarrierFocusChance = 0.2f;
|
||||
[Header("Stats")]
|
||||
public int health = 30;
|
||||
private bool isStunned = false;
|
||||
@@ -19,47 +26,166 @@ public class EnemyAI : MonoBehaviour
|
||||
[Header("Blocking")]
|
||||
[Range(0f, 1f)]
|
||||
public float blockChance = 0.15f; // 15% chance to block by default
|
||||
[HideInInspector] public Transform currentTarget;
|
||||
|
||||
private Animator anim;
|
||||
private Rigidbody rb;
|
||||
private EnemyRagdoll ragdoll;
|
||||
private NavMeshAgent agent;
|
||||
private Fortification targetFortification;
|
||||
private bool isDead = false;
|
||||
public GameObject stunVFX;
|
||||
private GameObject activeStunVFX;
|
||||
|
||||
void Start()
|
||||
{
|
||||
anim = GetComponent<Animator>();
|
||||
rb = GetComponent<Rigidbody>();
|
||||
ragdoll = GetComponent<EnemyRagdoll>();
|
||||
agent = GetComponent<NavMeshAgent>();
|
||||
|
||||
rb.isKinematic = true;
|
||||
rb.useGravity = false;
|
||||
|
||||
EnemySwarmManager.Instance.RegisterEnemy(transform);
|
||||
anim.SetFloat("MoveSpeed", 3.5f);
|
||||
EnemySwarmManager.Instance.RegisterEnemy(this);
|
||||
//anim.SetFloat("MoveSpeed", agent.speed);
|
||||
|
||||
// Add a random offset to the initial attack timer so the mob doesn't attack in perfectly unified sync
|
||||
lastAttackTime = Time.time + Random.Range(0f, attackCooldown);
|
||||
//AssignTarget();
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (isDead || isStunned) return;
|
||||
// PlayerController.Instance comes from the core script setup
|
||||
if (SamplePlayerAnimationController.Instance != null)
|
||||
|
||||
// Retargeting check if a barrier is broken
|
||||
if (targetFortification != null && targetFortification.IsDestroyed)
|
||||
{
|
||||
Vector3 offset = SamplePlayerAnimationController.Instance.transform.position - transform.position;
|
||||
|
||||
// sqrMagnitude is significantly faster for the CPU than Vector3.Distance
|
||||
if (offset.sqrMagnitude <= attackRange * attackRange)
|
||||
targetFortification = null;
|
||||
AssignTarget();
|
||||
}
|
||||
|
||||
if (currentTarget != null)
|
||||
{
|
||||
Vector3 offset = currentTarget.position - transform.position;
|
||||
float effectiveRange = (targetFortification != null) ? attackRange * 1.5f : attackRange;
|
||||
|
||||
// --- THE FIX: Pure mathematical distance check ---
|
||||
if (offset.sqrMagnitude <= effectiveRange * effectiveRange)
|
||||
{
|
||||
agent.isStopped = true;
|
||||
anim.SetFloat("MoveSpeed", 0f);
|
||||
|
||||
// Face the target manually
|
||||
Vector3 direction = offset.normalized;
|
||||
direction.y = 0;
|
||||
|
||||
// Prevent Unity error if direction is perfectly zero
|
||||
if (direction.sqrMagnitude > 0.001f)
|
||||
{
|
||||
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(direction), Time.deltaTime * 5f);
|
||||
}
|
||||
|
||||
if (Time.time - lastAttackTime > attackCooldown)
|
||||
{
|
||||
PerformAttack();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
agent.isStopped = false;
|
||||
agent.SetDestination(currentTarget.position);
|
||||
|
||||
// Fallback: If agent gets stuck briefly, keep the run animation playing smoothly
|
||||
float currentSpeed = agent.velocity.magnitude;
|
||||
if (currentSpeed < 0.1f && !agent.pathPending) currentSpeed = agent.speed;
|
||||
|
||||
anim.SetFloat("MoveSpeed", currentSpeed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- TRIGGERED VIA ANIMATION EVENT ON THE ENEMY ATTACK CLIP ---
|
||||
public void ExecuteEnemyHit()
|
||||
{
|
||||
if (isDead || currentTarget == null) return;
|
||||
|
||||
Vector3 offset = currentTarget.position - transform.position;
|
||||
float effectiveRange = (targetFortification != null) ? attackRange * 1.5f : attackRange;
|
||||
|
||||
if (offset.sqrMagnitude <= effectiveRange * effectiveRange)
|
||||
{
|
||||
if (targetFortification != null && !targetFortification.IsDestroyed)
|
||||
{
|
||||
targetFortification.TakeDamage(attackDamage);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Safely grab the player health without relying on the old PlayerController script
|
||||
PlayerHealth pHealth = currentTarget.GetComponent<PlayerHealth>();
|
||||
if (pHealth != null)
|
||||
{
|
||||
pHealth.TakeDamage(attackDamage);
|
||||
Debug.Log("<color=red>Enemy landed a hit on the Player!</color>");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private void AssignTarget()
|
||||
{
|
||||
bool prioritizeBarrier = (type == EnemyType.Siege) || (Random.value < hordeBarrierFocusChance);
|
||||
|
||||
if (prioritizeBarrier)
|
||||
{
|
||||
targetFortification = FindNearestFortification();
|
||||
if (targetFortification != null)
|
||||
{
|
||||
currentTarget = targetFortification.transform;
|
||||
}
|
||||
else
|
||||
{
|
||||
FindPlayerTarget();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FindPlayerTarget();
|
||||
}
|
||||
}
|
||||
|
||||
private void FindPlayerTarget()
|
||||
{
|
||||
GameObject player = GameObject.FindGameObjectWithTag("Player");
|
||||
if (player != null)
|
||||
{
|
||||
currentTarget = player.transform;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("<color=red>EnemyAI could not find the Player! Ensure your Player Prefab has the Tag set to 'Player'.</color>");
|
||||
}
|
||||
}
|
||||
|
||||
private Fortification FindNearestFortification()
|
||||
{
|
||||
Fortification nearest = null;
|
||||
float minDist = float.MaxValue;
|
||||
|
||||
foreach (Fortification fort in Fortification.AllFortifications)
|
||||
{
|
||||
if (fort.IsDestroyed) continue; // Ignore rubble
|
||||
|
||||
float dist = (transform.position - fort.transform.position).sqrMagnitude;
|
||||
if (dist < minDist)
|
||||
{
|
||||
minDist = dist;
|
||||
nearest = fort;
|
||||
}
|
||||
}
|
||||
return nearest;
|
||||
}
|
||||
|
||||
private void PerformAttack()
|
||||
{
|
||||
lastAttackTime = Time.time;
|
||||
@@ -68,19 +194,6 @@ public class EnemyAI : MonoBehaviour
|
||||
anim.SetTrigger("Attack");
|
||||
}
|
||||
|
||||
// --- TRIGGERED VIA ANIMATION EVENT ON THE ENEMY ATTACK CLIP ---
|
||||
public void ExecuteEnemyHit()
|
||||
{
|
||||
if (isDead || SamplePlayerAnimationController.Instance == null) return;
|
||||
|
||||
// Simple check to see if the player is still in range when the attack lands
|
||||
Vector3 offset = SamplePlayerAnimationController.Instance.transform.position - transform.position;
|
||||
if (offset.sqrMagnitude <= attackRange * attackRange)
|
||||
{
|
||||
SamplePlayerAnimationController.Instance.GetComponent<PlayerHealth>().TakeDamage(attackDamage);
|
||||
}
|
||||
}
|
||||
|
||||
public void TakeDamage(int damage, Vector3 hitSource, float knockbackForce)
|
||||
{
|
||||
if (isDead) return;
|
||||
@@ -121,8 +234,9 @@ public class EnemyAI : MonoBehaviour
|
||||
{
|
||||
isDead = true;
|
||||
|
||||
EnemySwarmManager.Instance.RemoveEnemy(transform);
|
||||
EnemySwarmManager.Instance.RemoveEnemy(this);
|
||||
ragdoll.ActivateRagdoll(hitSource, knockbackForce);
|
||||
Destroy(stunVFX);
|
||||
Destroy(gameObject, 3f);
|
||||
this.enabled = false;
|
||||
}
|
||||
@@ -136,6 +250,10 @@ public class EnemyAI : MonoBehaviour
|
||||
{
|
||||
isStunned = true;
|
||||
anim.SetTrigger("Stun"); // Requires a "Stun" trigger in your Animator
|
||||
if (stunVFX != null && activeStunVFX == null)
|
||||
{
|
||||
activeStunVFX = Instantiate(stunVFX, transform.position, Quaternion.identity, transform);
|
||||
}
|
||||
|
||||
// Detach from the Burst Compiler so they stop sliding toward you
|
||||
EnemySwarmManager.Instance.PauseEnemyMovement(transform);
|
||||
@@ -146,6 +264,7 @@ public class EnemyAI : MonoBehaviour
|
||||
{
|
||||
isStunned = false;
|
||||
anim.SetTrigger("Recover"); // Requires a "Recover" trigger in your Animator
|
||||
Destroy(activeStunVFX);
|
||||
|
||||
// Reattach to the Burst Compiler so they resume the chase
|
||||
EnemySwarmManager.Instance.ResumeEnemyMovement(transform);
|
||||
|
||||
Reference in New Issue
Block a user