2026-02-13 09:11:54 +00:00
|
|
|
|
using UnityEngine; // 유니티 기능을 불러올거에요 -> UnityEngine을
|
|
|
|
|
|
using System.Collections; // 코루틴을 사용할거에요 -> System.Collections를
|
|
|
|
|
|
using System.Collections.Generic; // 리스트를 사용할거에요 -> System.Collections.Generic을
|
2026-02-04 14:06:25 +00:00
|
|
|
|
|
2026-02-13 09:11:54 +00:00
|
|
|
|
public class ChargeMonster : MonsterClass // 클래스를 선언할거에요 -> 돌진 몬스터를
|
2026-02-04 14:06:25 +00:00
|
|
|
|
{
|
2026-02-13 09:11:54 +00:00
|
|
|
|
[Header("=== 돌진 설정 ===")] // 인스펙터 제목을 달거에요 -> 돌진 설정을
|
|
|
|
|
|
[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("애니메이션")] // 인스펙터 제목을 달거에요 -> 애니메이션을
|
|
|
|
|
|
[SerializeField] private string chargeAnim = "Monster_Charge"; // 변수를 선언할거에요 -> 돌진 애니를
|
|
|
|
|
|
[SerializeField] private string prepareAnim = "Monster_ChargePrepare"; // 변수를 선언할거에요 -> 준비 애니를
|
|
|
|
|
|
[SerializeField] private string walkAnim = "Monster_Walk"; // 변수를 선언할거에요 -> 걷기 애니를
|
|
|
|
|
|
|
|
|
|
|
|
private float lastChargeTime; // 변수를 선언할거에요 -> 마지막 돌진 시간을
|
|
|
|
|
|
private bool isCharging; // 변수를 선언할거에요 -> 돌진 중 여부를
|
|
|
|
|
|
private Rigidbody _rb; // 변수를 선언할거에요 -> 리지드바디를
|
|
|
|
|
|
|
|
|
|
|
|
protected override void Awake() // 함수를 실행할거에요 -> 초기화 Awake를
|
2026-02-04 14:06:25 +00:00
|
|
|
|
{
|
2026-02-13 09:11:54 +00:00
|
|
|
|
base.Awake(); // 부모 함수를 실행할거에요
|
|
|
|
|
|
_rb = GetComponent<Rigidbody>(); // 가져올거에요 -> 리지드바디를
|
|
|
|
|
|
if (_rb == null) _rb = gameObject.AddComponent<Rigidbody>(); // 추가할거에요 -> 없으면 새로
|
|
|
|
|
|
_rb.isKinematic = true; // 끌거에요 -> 물리 연산을
|
2026-02-04 14:06:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-13 09:11:54 +00:00
|
|
|
|
protected override void Init() { if (agent != null) agent.stoppingDistance = 1f; } // 초기화할거에요 -> 정지 거리를
|
2026-02-04 14:06:25 +00:00
|
|
|
|
|
2026-02-13 09:11:54 +00:00
|
|
|
|
protected override void ExecuteAILogic() // 함수를 실행할거에요 -> AI 로직을
|
2026-02-04 14:06:25 +00:00
|
|
|
|
{
|
2026-02-13 09:11:54 +00:00
|
|
|
|
if (isHit || isCharging || isAttacking) return; // 중단할거에요 -> 행동 불가면
|
|
|
|
|
|
float dist = Vector3.Distance(transform.position, playerTransform.position); // 계산할거에요 -> 거리를
|
|
|
|
|
|
if (dist <= chargeRange && Time.time >= lastChargeTime + chargeDelay) StartCoroutine(ChargeRoutine()); // 실행할거에요 -> 돌진을
|
|
|
|
|
|
else ChasePlayer(); // 실행할거에요 -> 추격을
|
2026-02-04 14:06:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-13 09:11:54 +00:00
|
|
|
|
private void ChasePlayer() // 함수를 선언할거에요 -> 추격 로직을
|
2026-02-04 14:06:25 +00:00
|
|
|
|
{
|
2026-02-13 09:11:54 +00:00
|
|
|
|
if (agent.isOnNavMesh) { agent.isStopped = false; agent.SetDestination(playerTransform.position); } // 이동할거에요 -> 플레이어에게
|
|
|
|
|
|
animator.Play(walkAnim); // 재생할거에요 -> 걷기 애니를
|
2026-02-04 14:06:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-13 09:11:54 +00:00
|
|
|
|
private IEnumerator ChargeRoutine() // 코루틴 함수를 정의할거에요 -> 돌진 과정을
|
2026-02-04 14:06:25 +00:00
|
|
|
|
{
|
2026-02-13 09:11:54 +00:00
|
|
|
|
isAttacking = true; agent.isStopped = true; // 설정할거에요 -> 공격 상태와 정지를
|
|
|
|
|
|
animator.Play(prepareAnim); // 재생할거에요 -> 준비 애니를
|
2026-02-04 14:06:25 +00:00
|
|
|
|
|
2026-02-13 09:11:54 +00:00
|
|
|
|
Vector3 dir = (playerTransform.position - transform.position).normalized; dir.y = 0; // 계산할거에요 -> 방향을
|
|
|
|
|
|
transform.rotation = Quaternion.LookRotation(dir); // 회전할거에요 -> 방향대로
|
2026-02-12 15:23:25 +00:00
|
|
|
|
yield return new WaitForSeconds(prepareTime); // 기다릴거에요 -> 준비 시간만큼
|
2026-02-04 14:06:25 +00:00
|
|
|
|
|
2026-02-13 09:11:54 +00:00
|
|
|
|
isCharging = true; lastChargeTime = Time.time; // 설정할거에요 -> 돌진 상태와 시간을
|
|
|
|
|
|
agent.enabled = false; _rb.isKinematic = false; // 전환할거에요 -> 물리 이동으로
|
|
|
|
|
|
animator.Play(chargeAnim); // 재생할거에요 -> 돌진 애니를
|
2026-02-05 15:42:48 +00:00
|
|
|
|
|
2026-02-13 09:11:54 +00:00
|
|
|
|
float elapsed = 0f; // 초기화할거에요 -> 경과 시간을
|
|
|
|
|
|
while (elapsed < chargeDuration) // 반복할거에요 -> 지속 시간 동안
|
2026-02-04 14:06:25 +00:00
|
|
|
|
{
|
2026-02-13 09:11:54 +00:00
|
|
|
|
_rb.velocity = transform.forward * chargeSpeed; // 적용할거에요 -> 속도를
|
|
|
|
|
|
elapsed += Time.deltaTime; // 더할거에요 -> 시간을
|
2026-02-12 15:23:25 +00:00
|
|
|
|
yield return null; // 대기할거에요 -> 다음 프레임까지
|
2026-02-04 14:06:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-13 09:11:54 +00:00
|
|
|
|
_rb.velocity = Vector3.zero; _rb.isKinematic = true; agent.enabled = true; // 복구할거에요 -> 상태를
|
|
|
|
|
|
isCharging = false; OnAttackEnd(); // 종료할거에요 -> 돌진과 공격을
|
2026-02-04 14:06:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-13 09:11:54 +00:00
|
|
|
|
private void OnCollisionEnter(Collision col) // 함수를 실행할거에요 -> 충돌 시
|
2026-02-04 14:06:25 +00:00
|
|
|
|
{
|
2026-02-13 09:11:54 +00:00
|
|
|
|
if (isCharging && col.gameObject.CompareTag("Player")) // 조건이 맞으면 실행할거에요 -> 돌진 중 플레이어 충돌이면
|
2026-02-06 09:27:08 +00:00
|
|
|
|
{
|
2026-02-13 09:11:54 +00:00
|
|
|
|
var t = col.gameObject.GetComponent<IDamageable>(); // 가져올거에요 -> 인터페이스를
|
|
|
|
|
|
if (t != null) t.TakeDamage(attackDamage); // 입힐거에요 -> 데미지를
|
2026-02-04 14:06:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-02-05 15:42:48 +00:00
|
|
|
|
}
|