Projext/Assets/Scripts/Enemy/AI/Range Monster.cs

75 lines
5.0 KiB
C#

using UnityEngine; // 유니티 기능을 불러올거에요 -> UnityEngine을
public class UniversalRangedMonster : MonsterClass // 클래스를 선언할거에요 -> 원거리 몬스터를
{
public enum AttackStyle { Straight, Lob } // 열거형을 정의할거에요 -> 공격 타입을
[Header("=== 원거리 설정 ===")] // 인스펙터 제목을 달거에요 -> 원거리 설정을
[SerializeField] private AttackStyle attackStyle = AttackStyle.Straight; // 변수를 선언할거에요 -> 스타일을
[SerializeField] private GameObject projectilePrefab; // 변수를 선언할거에요 -> 투사체를
[SerializeField] private Transform firePoint; // 변수를 선언할거에요 -> 발사 위치를
[SerializeField] private float attackRange = 10f; // 변수를 선언할거에요 -> 사거리를
[SerializeField] private float attackDelay = 2f; // 변수를 선언할거에요 -> 딜레이를
[SerializeField] private float projectileSpeed = 20f; // 변수를 선언할거에요 -> 속도를
[Header("곡사 설정")] // 인스펙터 제목을 달거에요 -> 곡사 설정을
[SerializeField] private float launchAngle = 45f; // 변수를 선언할거에요 -> 각도를
[SerializeField] private float aimHeightOffset = 1.2f; // 변수를 선언할거에요 -> 높이 보정을
[Header("애니메이션")] // 인스펙터 제목을 달거에요 -> 애니메이션을
[SerializeField] private string attackAnim = "Monster_Attack"; // 변수를 선언할거에요 -> 공격 애니를
[SerializeField] private string walkAnim = "Monster_Walk"; // 변수를 선언할거에요 -> 걷기 애니를
private float lastAttackTime; // 변수를 선언할거에요 -> 마지막 공격 시간을
protected override void Init() { if (agent != null) agent.stoppingDistance = attackRange * 0.8f; } // 초기화할거에요 -> 정지 거리를
protected override void ExecuteAILogic() // 함수를 실행할거에요 -> AI 로직을
{
if (isHit || isAttacking || isResting) return; // 중단할거에요 -> 행동 불가면
float dist = Vector3.Distance(transform.position, playerTransform.position); // 계산할거에요 -> 거리를
if (dist <= attackRange) TryAttack(); else ChasePlayer(); // 분기할거에요 -> 공격 또는 추격으로
}
private void ChasePlayer() // 함수를 선언할거에요 -> 추격 로직을
{
if (agent.isOnNavMesh) { agent.isStopped = false; agent.SetDestination(playerTransform.position); } // 이동할거에요 -> 플레이어에게
animator.Play(walkAnim); // 재생할거에요 -> 걷기 애니를
}
private void TryAttack() // 함수를 선언할거에요 -> 공격 시도를
{
if (Time.time < lastAttackTime + attackDelay) return; // 중단할거에요 -> 쿨타임 중이면
lastAttackTime = Time.time; isAttacking = true; // 갱신할거에요 -> 시간과 상태를
if (agent.isOnNavMesh) agent.isStopped = true; // 멈출거에요 -> 이동을
Vector3 dir = (playerTransform.position - transform.position).normalized; dir.y = 0; // 계산할거에요 -> 방향을
transform.rotation = Quaternion.LookRotation(dir); // 회전할거에요 -> 방향대로
animator.Play(attackAnim); // 재생할거에요 -> 공격 애니를
}
public override void OnAttackStart() // 함수를 실행할거에요 -> 공격 이벤트 발생 시
{
if (projectilePrefab == null || firePoint == null) return; // 중단할거에요 -> 설정 없으면
GameObject obj = Instantiate(projectilePrefab, firePoint.position, transform.rotation); // 생성할거에요 -> 투사체를
Projectile proj = obj.GetComponent<Projectile>(); // 가져올거에요 -> 스크립트를
if (attackStyle == AttackStyle.Straight) // 조건이 맞으면 실행할거에요 -> 직선형이면
{
if (proj != null) proj.Initialize(transform.forward, projectileSpeed, attackDamage); // 초기화할거에요 -> 직선 발사로
}
else // 조건이 틀리면 실행할거에요 -> 곡사형이면
{
Rigidbody rb = obj.GetComponent<Rigidbody>(); // 가져올거에요 -> 리지드바디를
if (rb != null)
{
if (proj != null) proj.Initialize(Vector3.zero, 0, attackDamage); // 초기화할거에요 -> 물리 이동으로
rb.useGravity = true; // 켤거에요 -> 중력을
Vector3 targetPos = playerTransform.position + Vector3.up * aimHeightOffset; // 계산할거에요 -> 타겟 위치를
Vector3 velocity = ProjectileMath.CalculateLobVelocity(firePoint.position, targetPos, launchAngle); // 계산할거에요 -> 헬퍼로 속도를
if (velocity != Vector3.zero) rb.velocity = velocity; // 적용할거에요 -> 속도를
else rb.AddForce(transform.forward * 15f + Vector3.up * 5f, ForceMode.Impulse); // 실패 시 기본 힘을
}
}
}
}