Projext/Assets/7.Other Code/Monster_Scripts/NormalMonster.cs

145 lines
4.6 KiB
C#
Raw Normal View History

2026-01-29 06:58:38 +00:00
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
using System;
public class NormalMonster : MonsterClass
{
[Header("전투")]
[SerializeField] private float damage = 10f;
[SerializeField] private float attackRange = 2f;
[SerializeField] private float attackDelay = 1.5f;
2026-01-30 08:53:18 +00:00
// [SerializeField] private float postAttackDelay = 1.5f; // ⭐ MonsterClass의 attackRestDuration을 사용하므로 삭제 권장
2026-01-29 06:58:38 +00:00
private float lastAttackTime;
[Header("공격 애니메이션")]
[SerializeField] private string[] attackAnimations = { "Monster_Attack_1" };
[Header("이동 애니메이션")]
[SerializeField] private string Monster_Idle = "Monster_Idle";
[SerializeField] private string Monster_Walk = "Monster_Walk";
[Header("AI")]
[SerializeField] private float stopBuffer = 0.3f;
[SerializeField] private float patrolRadius = 5f;
[SerializeField] private float patrolInterval = 2f;
private float nextPatrolTime;
private float repathInterval = 0.3f;
private float nextRepathTime;
private Transform player;
private int attackIndex;
2026-01-30 08:53:18 +00:00
// ❌ [삭제] bool isAttacking;
// ⭐ 부모(MonsterClass)에 이미 있으므로 자식에서 또 선언하면 에러가 납니다!
2026-01-29 06:58:38 +00:00
private bool isPlayerInZone;
protected override void Start()
{
2026-01-30 08:53:18 +00:00
base.Start(); // 부모의 Start 실행
2026-01-29 06:58:38 +00:00
player = GameObject.FindWithTag("Player")?.transform;
agent.stoppingDistance = attackRange - 0.4f;
animator.applyRootMotion = false;
}
protected override void Update()
2026-01-29 06:58:38 +00:00
{
2026-01-30 08:53:18 +00:00
// ⭐ 부모의 Update(이동 제한 로직)를 먼저 실행합니다.
base.Update();
2026-01-29 06:58:38 +00:00
if (isDead || player == null) return;
2026-01-30 08:53:18 +00:00
// 피격 중이거나 공격 중, 혹은 쉬는 중(isResting)에는 AI 로직을 타지 않습니다.
if (isHit || isAttacking || isResting) return;
2026-01-29 06:58:38 +00:00
if (isPlayerInZone) HandlePlayerTarget();
else Patrol();
UpdateMovementAnimation();
}
void HandlePlayerTarget()
{
float distance = Vector3.Distance(transform.position, player.position);
2026-01-30 08:53:18 +00:00
// 공격 범위 안에 들어왔을 때
2026-01-29 06:58:38 +00:00
if (distance <= attackRange - stopBuffer)
{
2026-01-30 08:53:18 +00:00
TryAttack();
2026-01-29 06:58:38 +00:00
}
2026-01-30 08:53:18 +00:00
else if (Time.time >= nextRepathTime)
2026-01-29 06:58:38 +00:00
{
agent.SetDestination(player.position);
nextRepathTime = Time.time + repathInterval;
}
}
void TryAttack()
{
if (Time.time < lastAttackTime + attackDelay) return;
lastAttackTime = Time.time;
string attackName = attackAnimations[attackIndex];
attackIndex = (attackIndex + 1) % attackAnimations.Length;
2026-01-30 08:53:18 +00:00
// ⭐ 부모의 OnAttackStart를 호출하여 isAttacking을 true로 만들고 이동을 멈춥니다.
OnAttackStart();
2026-01-29 06:58:38 +00:00
animator.Play(attackName, 0, 0f);
}
2026-01-30 08:53:18 +00:00
// ⭐ [수정] 부모의 가상 메서드를 오버라이드합니다.
public override void OnAttackStart()
2026-01-29 06:58:38 +00:00
{
2026-01-30 08:53:18 +00:00
base.OnAttackStart(); // 부모의 isAttacking = true 로직 실행
2026-01-29 06:58:38 +00:00
}
2026-01-30 08:53:18 +00:00
// ⭐ [수정] 부모의 가상 메서드를 오버라이드하여 '휴식' 기능을 활성화합니다.
public override void OnAttackEnd()
2026-01-29 06:58:38 +00:00
{
2026-01-30 08:53:18 +00:00
base.OnAttackEnd(); // 부모의 'n초간 휴식' 코루틴을 실행합니다!
2026-01-29 06:58:38 +00:00
}
2026-01-30 08:53:18 +00:00
public void OnAttackHit() // 애니메이션 이벤트 (공격 판정 시점)
2026-01-29 06:58:38 +00:00
{
2026-01-30 08:53:18 +00:00
if (player == null || isHit || isDead) return;
2026-01-29 06:58:38 +00:00
PlayerHealth playerHealth = player.GetComponent<PlayerHealth>();
if (playerHealth != null) playerHealth.TakeDamage(damage);
}
void UpdateMovementAnimation()
{
2026-01-30 08:53:18 +00:00
if (isAttacking || isHit || isResting) return;
2026-01-29 06:58:38 +00:00
if (agent.velocity.magnitude < 0.1f)
animator.Play(Monster_Idle);
else
animator.Play(Monster_Walk);
}
void Patrol()
{
if (Time.time < nextPatrolTime) return;
Vector3 randomPoint = transform.position +
new Vector3(UnityEngine.Random.Range(-patrolRadius, patrolRadius), 0,
UnityEngine.Random.Range(-patrolRadius, patrolRadius));
if (NavMesh.SamplePosition(randomPoint, out NavMeshHit hit, 3f, NavMesh.AllAreas))
agent.SetDestination(hit.position);
nextPatrolTime = Time.time + patrolInterval;
}
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Player")) isPlayerInZone = true;
}
private void OnTriggerExit(Collider other)
{
if (other.CompareTag("Player")) isPlayerInZone = false;
}
2026-01-30 08:53:18 +00:00
}