96 lines
5.8 KiB
C#
96 lines
5.8 KiB
C#
using UnityEngine; // 유니티 기능을 불러올거에요 -> UnityEngine을
|
|
using System.Collections.Generic; // 리스트를 사용할거에요 -> 제네릭을
|
|
|
|
public class MeleeMonster : MonsterClass // 클래스를 선언할거에요 -> 근접 몬스터를
|
|
{
|
|
[Header("=== 공격 설정 ===")] // 인스펙터 제목을 달거에요 -> 공격 설정을
|
|
[SerializeField] private float attackRange = 2f; // 변수를 선언할거에요 -> 사거리를
|
|
[SerializeField] private float attackDelay = 1.5f; // 변수를 선언할거에요 -> 딜레이를
|
|
|
|
[Header("=== 드랍 ===")] // 인스펙터 제목을 달거에요 -> 드랍 설정을
|
|
[SerializeField] private List<GameObject> dropItemPrefabs; // 변수를 선언할거에요 -> 아이템 목록을
|
|
[Range(0, 100)][SerializeField] private float dropChance = 30f; // 변수를 선언할거에요 -> 확률을
|
|
|
|
[Header("애니메이션")] // 인스펙터 제목을 달거에요 -> 애니메이션을
|
|
[SerializeField] private string[] attackAnimations = { "Monster_Attack_1" }; // 변수를 선언할거에요 -> 공격 애니들을
|
|
[SerializeField] private string Monster_Walk = "Monster_Walk"; // 변수를 선언할거에요 -> 걷기 애니를
|
|
|
|
private float lastAttackTime; // 변수를 선언할거에요 -> 마지막 공격 시간을
|
|
private int attackIndex; // 변수를 선언할거에요 -> 공격 인덱스를
|
|
|
|
protected override void Init() // 함수를 실행할거에요 -> 초기화를
|
|
{
|
|
if (agent != null) agent.stoppingDistance = attackRange - 0.4f; // 설정할거에요 -> 정지 거리를
|
|
if (animator != null) animator.applyRootMotion = false; // 끌거에요 -> 루트 모션을
|
|
}
|
|
|
|
protected override void OnResetStats() => myWeapon?.SetDamage(attackDamage); // 함수를 실행할거에요 -> 무기 데미지 설정을
|
|
|
|
protected override void ExecuteAILogic() // 함수를 실행할거에요 -> AI 로직을
|
|
{
|
|
if (isHit || isAttacking || isResting) return; // 중단할거에요 -> 행동 불가면
|
|
|
|
float dist = Vector3.Distance(transform.position, playerTransform.position); // 계산할거에요 -> 거리를
|
|
if (dist <= attackRange) TryAttack(); // 분기할거에요 -> 가까우면 공격을
|
|
else MoveToPlayer(); // 분기할거에요 -> 멀면 이동을
|
|
}
|
|
|
|
private void MoveToPlayer() // 함수를 선언할거에요 -> 이동 로직을
|
|
{
|
|
if (agent.isOnNavMesh) { agent.isStopped = false; agent.SetDestination(playerTransform.position); } // 이동할거에요 -> 플레이어에게
|
|
|
|
// ── [Bug 1 수정] 속도 체크 + 현재 state 확인으로 불필요한 재호출 방지 + 레이어 0 명시 ──
|
|
bool isMoving = agent.velocity.magnitude > 0.1f; // 판단할거에요 -> 실제로 이동 중인지를
|
|
|
|
if (isMoving) // 조건이 맞으면 실행할거에요 -> 이동 중이면
|
|
{
|
|
if (!animator.GetCurrentAnimatorStateInfo(0).IsName(Monster_Walk)) // 조건이 맞으면 실행할거에요 -> 이미 Walk가 아니면
|
|
{
|
|
animator.Play(Monster_Walk, 0, 0f); // 재생할거에요 -> 걷기 애니를 (레이어 0 명시)
|
|
}
|
|
}
|
|
else // 조건이 틀리면 실행할거에요 -> 멈췄으면 (장애물 등)
|
|
{
|
|
if (!animator.GetCurrentAnimatorStateInfo(0).IsName(Monster_Idle)) // 조건이 맞으면 실행할거에요 -> 이미 Idle이 아니면
|
|
{
|
|
animator.Play(Monster_Idle, 0, 0f); // 재생할거에요 -> 대기 애니를 (레이어 0 명시)
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
|
|
string animName = attackAnimations[attackIndex]; // 가져올거에요 -> 애니 이름을
|
|
attackIndex = (attackIndex + 1) % attackAnimations.Length; // 갱신할거에요 -> 인덱스를
|
|
animator.Play(animName, 0, 0f); // 재생할거에요 -> 공격 애니를 (레이어 0 명시)
|
|
}
|
|
|
|
protected override void OnDie() // 함수를 실행할거에요 -> 사망 시
|
|
{
|
|
base.OnDie(); // 실행할거에요 -> 부모 사망 로직을
|
|
if (dropItemPrefabs != null && dropItemPrefabs.Count > 0) // 조건이 맞으면 실행할거에요 -> 아이템이 있으면
|
|
{
|
|
if (Random.Range(0f, 100f) <= dropChance) // 조건이 맞으면 실행할거에요 -> 확률 당첨되면
|
|
{
|
|
int idx = Random.Range(0, dropItemPrefabs.Count); // 뽑을거에요 -> 랜덤 인덱스를
|
|
if (dropItemPrefabs[idx] != null) Instantiate(dropItemPrefabs[idx], transform.position + Vector3.up * 0.5f, Quaternion.identity); // 생성할거에요 -> 아이템을
|
|
}
|
|
}
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────
|
|
// 디버그 기즈모 오버라이드 — 근접 공격 범위 추가 표시
|
|
// ─────────────────────────────────────────────────────────
|
|
protected override void OnDrawGizmosSelected() // 함수를 선언할거에요 -> 기즈모 오버라이드를
|
|
{
|
|
base.OnDrawGizmosSelected(); // 실행할거에요 -> 부모(감지 범위)를 먼저 그리기
|
|
|
|
if (!showDebugGizmos) return; // 중단할거에요 -> 기즈모가 꺼져 있으면
|
|
|
|
// 근접 공격 범위 — 빨강 반투명
|
|
Gizmos.color = new Color(1f, 0f, 0f, 0.15f); // 설정할거에요 -> 빨강 반투명 채우기를
|
|
Gizmos.DrawSphere(transform.position, attackRange); // 그릴거에요 -> 공격 범위를
|
|
Gizmos.color = Color.red; // 설정할거에요 -> 빨강 외곽선을
|
|
Gizmos.DrawWireSphere(transform.position, attackRange); // 그릴거에요 -> 공격 범위 외곽선을
|
|
}
|
|
} |