Projext/Assets/Scripts/Enemy/AI/ChargeMonster.cs

211 lines
6.6 KiB
C#
Raw Normal View History

2026-02-04 14:06:25 +00:00
using UnityEngine;
using UnityEngine.AI;
using System.Collections;
2026-02-06 09:27:08 +00:00
using System.Collections.Generic; // ✅ 리스트 사용 필수
2026-02-04 14:06:25 +00:00
/// <summary>
2026-02-06 09:27:08 +00:00
/// 돌진 공격 몬스터
2026-02-04 14:06:25 +00:00
/// </summary>
public class ChargeMonster : MonsterClass
{
[Header("=== 돌진 공격 설정 ===")]
2026-02-06 09:27:08 +00:00
[SerializeField] private float chargeSpeed = 15f;
[SerializeField] private float chargeRange = 10f;
[SerializeField] private float chargeDuration = 2f;
[SerializeField] private float chargeDelay = 3f;
[SerializeField] private float prepareTime = 0.5f;
// 🎁 [추가] 드랍 아이템 리스트
[Header("=== 드랍 아이템 설정 ===")]
[Tooltip("드랍 가능한 아이템 리스트 (랜덤 1개)")]
[SerializeField] private List<GameObject> dropItemPrefabs;
[Tooltip("아이템이 나올 확률 (0 ~ 100%)")]
[Range(0, 100)][SerializeField] private float dropChance = 30f;
2026-02-04 14:06:25 +00:00
private float lastChargeTime;
private bool isCharging = false;
private bool isPreparing = false;
private Vector3 chargeDirection;
[Header("공격 / 이동 애니메이션")]
[SerializeField] private string chargeAnimation = "Monster_Charge";
[SerializeField] private string prepareAnimation = "Monster_ChargePrepare";
[SerializeField] private string Monster_Walk = "Monster_Walk";
[Header("AI 상세 설정")]
[SerializeField] private float patrolRadius = 5f;
[SerializeField] private float patrolInterval = 2f;
private float nextPatrolTime;
private bool isPlayerInZone;
private Rigidbody _rigidbody;
protected override void Awake()
{
base.Awake();
_rigidbody = GetComponent<Rigidbody>();
2026-02-05 15:42:48 +00:00
if (_rigidbody == null) _rigidbody = gameObject.AddComponent<Rigidbody>();
_rigidbody.isKinematic = true;
2026-02-04 14:06:25 +00:00
}
protected override void Init()
{
2026-02-05 15:42:48 +00:00
if (agent != null) agent.stoppingDistance = 1f;
2026-02-04 14:06:25 +00:00
if (animator != null) animator.applyRootMotion = false;
}
protected override void ExecuteAILogic()
{
if (isHit || isCharging || isPreparing) return;
float distance = Vector3.Distance(transform.position, playerTransform.position);
if (isPlayerInZone || distance <= chargeRange * 1.5f)
{
HandleChargeCombat(distance);
}
else
{
Patrol();
}
UpdateMovementAnimation();
}
void HandleChargeCombat(float distance)
{
if (distance <= chargeRange && Time.time >= lastChargeTime + chargeDelay)
{
StartCoroutine(PrepareCharge());
}
2026-02-05 15:42:48 +00:00
else if (!isCharging && !isPreparing)
2026-02-04 14:06:25 +00:00
{
2026-02-05 15:42:48 +00:00
if (agent.isOnNavMesh)
{
if (agent.isStopped) agent.isStopped = false;
2026-02-04 14:06:25 +00:00
agent.SetDestination(playerTransform.position);
2026-02-05 15:42:48 +00:00
}
2026-02-04 14:06:25 +00:00
}
}
IEnumerator PrepareCharge()
{
isPreparing = true;
if (agent.isOnNavMesh)
{
agent.isStopped = true;
2026-02-05 15:42:48 +00:00
agent.ResetPath();
2026-02-04 14:06:25 +00:00
agent.velocity = Vector3.zero;
}
chargeDirection = (playerTransform.position - transform.position).normalized;
chargeDirection.y = 0;
2026-02-05 15:42:48 +00:00
if (chargeDirection != Vector3.zero) transform.rotation = Quaternion.LookRotation(chargeDirection);
2026-02-04 14:06:25 +00:00
2026-02-05 15:42:48 +00:00
if (!string.IsNullOrEmpty(prepareAnimation)) animator.Play(prepareAnimation, 0, 0f);
2026-02-04 14:06:25 +00:00
2026-02-05 15:42:48 +00:00
Debug.Log("[ChargeMonster] 돌진 준비...");
2026-02-04 14:06:25 +00:00
yield return new WaitForSeconds(prepareTime);
StartCoroutine(Charge());
}
IEnumerator Charge()
{
isPreparing = false;
isCharging = true;
lastChargeTime = Time.time;
2026-02-05 15:42:48 +00:00
if (agent != null) agent.enabled = false;
2026-02-04 14:06:25 +00:00
if (_rigidbody != null) _rigidbody.isKinematic = false;
animator.Play(chargeAnimation, 0, 0f);
Debug.Log("[ChargeMonster] 돌진 시작!");
float chargeStartTime = Time.time;
2026-02-05 15:42:48 +00:00
2026-02-04 14:06:25 +00:00
while (Time.time < chargeStartTime + chargeDuration)
{
if (_rigidbody != null)
_rigidbody.velocity = chargeDirection * chargeSpeed;
else
transform.position += chargeDirection * chargeSpeed * Time.deltaTime;
yield return null;
}
2026-02-05 15:42:48 +00:00
if (_rigidbody != null)
2026-02-04 14:06:25 +00:00
{
_rigidbody.velocity = Vector3.zero;
_rigidbody.isKinematic = true;
}
2026-02-05 15:42:48 +00:00
if (agent != null)
{
agent.enabled = true;
agent.ResetPath();
agent.velocity = Vector3.zero;
}
2026-02-04 14:06:25 +00:00
isCharging = false;
2026-02-05 15:42:48 +00:00
Debug.Log("[ChargeMonster] 돌진 종료, 쿨타임 시작");
2026-02-04 14:06:25 +00:00
}
void UpdateMovementAnimation()
{
if (isCharging || isPreparing || isHit || isResting) return;
2026-02-05 15:42:48 +00:00
if (agent.enabled && agent.velocity.magnitude > 0.1f)
2026-02-04 14:06:25 +00:00
animator.Play(Monster_Walk);
2026-02-05 15:42:48 +00:00
else
animator.Play(Monster_Idle);
2026-02-04 14:06:25 +00:00
}
void Patrol()
{
if (Time.time < nextPatrolTime) return;
Vector3 randomPoint = transform.position + new Vector3(
2026-02-05 15:42:48 +00:00
Random.Range(-patrolRadius, patrolRadius),
0,
2026-02-04 14:06:25 +00:00
Random.Range(-patrolRadius, patrolRadius)
);
2026-02-05 15:42:48 +00:00
2026-02-04 14:06:25 +00:00
if (NavMesh.SamplePosition(randomPoint, out NavMeshHit hit, 3f, NavMesh.AllAreas))
if (agent.isOnNavMesh) agent.SetDestination(hit.position);
2026-02-05 15:42:48 +00:00
2026-02-04 14:06:25 +00:00
nextPatrolTime = Time.time + patrolInterval;
}
private void OnCollisionEnter(Collision collision)
{
if (!isCharging) return;
if (collision.gameObject.CompareTag("Player"))
{
if (collision.gameObject.TryGetComponent<PlayerHealth>(out var playerHealth))
{
if (!playerHealth.isInvincible)
playerHealth.TakeDamage(attackDamage);
2026-02-06 09:27:08 +00:00
}
}
}
// ☠️ [추가] 죽을 때 아이템 드랍
protected override void OnDie()
{
if (_rigidbody != null) _rigidbody.velocity = Vector3.zero;
if (dropItemPrefabs != null && dropItemPrefabs.Count > 0)
{
float randomValue = Random.Range(0f, 100f);
if (randomValue <= dropChance)
{
int randomIndex = Random.Range(0, dropItemPrefabs.Count);
if (dropItemPrefabs[randomIndex] != null)
{
Instantiate(dropItemPrefabs[randomIndex], transform.position + Vector3.up * 0.5f, Quaternion.identity);
2026-02-04 14:06:25 +00:00
}
}
}
}
2026-02-05 15:42:48 +00:00
private void OnTriggerEnter(Collider other) { if (other.CompareTag("Player")) isPlayerInZone = true; }
private void OnTriggerExit(Collider other) { if (other.CompareTag("Player")) isPlayerInZone = false; }
}