Initial commit
This commit is contained in:
154
Assets/Scripts/EnemyAI.cs
Normal file
154
Assets/Scripts/EnemyAI.cs
Normal file
@@ -0,0 +1,154 @@
|
||||
using Synty.AnimationBaseLocomotion.Samples;
|
||||
using UnityEngine;
|
||||
|
||||
[RequireComponent(typeof(Rigidbody))]
|
||||
[RequireComponent(typeof(EnemyRagdoll))]
|
||||
public class EnemyAI : MonoBehaviour
|
||||
{
|
||||
[Header("Stats")]
|
||||
public int health = 30;
|
||||
private bool isStunned = false;
|
||||
|
||||
[Header("Attacking")]
|
||||
public float attackRange = 2.5f;
|
||||
public float attackCooldown = 2.0f;
|
||||
public int attackDamage = 5;
|
||||
private float lastAttackTime;
|
||||
|
||||
|
||||
[Header("Blocking")]
|
||||
[Range(0f, 1f)]
|
||||
public float blockChance = 0.15f; // 15% chance to block by default
|
||||
|
||||
private Animator anim;
|
||||
private Rigidbody rb;
|
||||
private EnemyRagdoll ragdoll;
|
||||
private bool isDead = false;
|
||||
|
||||
void Start()
|
||||
{
|
||||
anim = GetComponent<Animator>();
|
||||
rb = GetComponent<Rigidbody>();
|
||||
ragdoll = GetComponent<EnemyRagdoll>();
|
||||
|
||||
rb.isKinematic = true;
|
||||
rb.useGravity = false;
|
||||
|
||||
EnemySwarmManager.Instance.RegisterEnemy(transform);
|
||||
anim.SetFloat("MoveSpeed", 3.5f);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (isDead || isStunned) return;
|
||||
// PlayerController.Instance comes from the core script setup
|
||||
if (SamplePlayerAnimationController.Instance != null)
|
||||
{
|
||||
Vector3 offset = SamplePlayerAnimationController.Instance.transform.position - transform.position;
|
||||
|
||||
// sqrMagnitude is significantly faster for the CPU than Vector3.Distance
|
||||
if (offset.sqrMagnitude <= attackRange * attackRange)
|
||||
{
|
||||
if (Time.time - lastAttackTime > attackCooldown)
|
||||
{
|
||||
PerformAttack();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void PerformAttack()
|
||||
{
|
||||
lastAttackTime = Time.time;
|
||||
|
||||
// Tells the animator to play the attack animation
|
||||
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;
|
||||
|
||||
// --- BLOCKING LOGIC ---
|
||||
// Check if the attack is coming from in front of the enemy
|
||||
Vector3 directionToHitSource = (hitSource - transform.position).normalized;
|
||||
float facingDotProduct = Vector3.Dot(transform.forward, directionToHitSource);
|
||||
|
||||
// A dot product > 0.3 means the hit is roughly within a 140-degree cone in front of the enemy
|
||||
if (facingDotProduct > 0.3f)
|
||||
{
|
||||
if (Random.value < blockChance)
|
||||
{
|
||||
anim.SetTrigger("Block");
|
||||
|
||||
// Optional visual flair for blocking
|
||||
// Instantiate(sparksPrefab, transform.position + Vector3.up, Quaternion.identity);
|
||||
|
||||
return; // Exit early so no damage is taken and no ragdoll occurs
|
||||
}
|
||||
}
|
||||
|
||||
// --- DAMAGE LOGIC ---
|
||||
health -= damage;
|
||||
|
||||
if (health <= 0)
|
||||
{
|
||||
Die(hitSource, knockbackForce);
|
||||
}
|
||||
else
|
||||
{
|
||||
anim.SetTrigger("Hit");
|
||||
}
|
||||
}
|
||||
|
||||
void Die(Vector3 hitSource, float knockbackForce)
|
||||
{
|
||||
isDead = true;
|
||||
|
||||
EnemySwarmManager.Instance.RemoveEnemy(transform);
|
||||
ragdoll.ActivateRagdoll(hitSource, knockbackForce);
|
||||
Destroy(gameObject, 3f);
|
||||
this.enabled = false;
|
||||
}
|
||||
public void ApplyStun(float duration)
|
||||
{
|
||||
if (isDead || isStunned) return;
|
||||
StartCoroutine(StunRoutine(duration));
|
||||
}
|
||||
|
||||
private System.Collections.IEnumerator StunRoutine(float duration)
|
||||
{
|
||||
isStunned = true;
|
||||
anim.SetTrigger("Stun"); // Requires a "Stun" trigger in your Animator
|
||||
|
||||
// Detach from the Burst Compiler so they stop sliding toward you
|
||||
EnemySwarmManager.Instance.PauseEnemyMovement(transform);
|
||||
|
||||
yield return new WaitForSeconds(duration);
|
||||
|
||||
if (!isDead)
|
||||
{
|
||||
isStunned = false;
|
||||
anim.SetTrigger("Recover"); // Requires a "Recover" trigger in your Animator
|
||||
|
||||
// Reattach to the Burst Compiler so they resume the chase
|
||||
EnemySwarmManager.Instance.ResumeEnemyMovement(transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user