2026-02-12 15:23:25 +00:00
|
|
|
using UnityEngine; // 유니티 엔진의 기본 기능을 불러올거에요 -> UnityEngine을
|
|
|
|
|
using UnityEngine.AI; // 길찾기(내비게이션) 기능을 불러올거에요 -> UnityEngine.AI를
|
|
|
|
|
using System.Collections; // 코루틴 기능을 사용할거에요 -> System.Collections를
|
|
|
|
|
using System.Collections.Generic; // 리스트 기능을 사용할거에요 -> 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>
|
2026-02-12 15:23:25 +00:00
|
|
|
public class ChargeMonster : MonsterClass // 클래스를 선언할거에요 -> MonsterClass를 상속받는 ChargeMonster를
|
2026-02-04 14:06:25 +00:00
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
[Header("=== 돌진 공격 설정 ===")] // 인스펙터 창에 제목을 표시할거에요 -> === 돌진 공격 설정 === 을
|
|
|
|
|
[SerializeField] private float chargeSpeed = 15f; // 변수를 선언할거에요 -> 돌진 속도(15.0)를 chargeSpeed에
|
|
|
|
|
[SerializeField] private float chargeRange = 10f; // 변수를 선언할거에요 -> 돌진 시작 거리(10.0)를 chargeRange에
|
|
|
|
|
[SerializeField] private float chargeDuration = 2f; // 변수를 선언할거에요 -> 돌진 지속 시간(2.0초)을 chargeDuration에
|
|
|
|
|
[SerializeField] private float chargeDelay = 3f; // 변수를 선언할거에요 -> 돌진 쿨타임(3.0초)을 chargeDelay에
|
|
|
|
|
[SerializeField] private float prepareTime = 0.5f; // 변수를 선언할거에요 -> 돌진 준비 시간(0.5초)을 prepareTime에
|
2026-02-06 09:27:08 +00:00
|
|
|
|
|
|
|
|
// 🎁 [추가] 드랍 아이템 리스트
|
2026-02-12 15:23:25 +00:00
|
|
|
[Header("=== 드랍 아이템 설정 ===")] // 인스펙터 창에 제목을 표시할거에요 -> === 드랍 아이템 설정 === 을
|
|
|
|
|
[Tooltip("드랍 가능한 아이템 리스트 (랜덤 1개)")] // 마우스를 올리면 설명을 보여줄거에요 -> 툴팁 내용을
|
|
|
|
|
[SerializeField] private List<GameObject> dropItemPrefabs; // 리스트를 선언할거에요 -> 드랍 아이템 프리팹 목록을 dropItemPrefabs에
|
|
|
|
|
[Tooltip("아이템이 나올 확률 (0 ~ 100%)")] // 마우스를 올리면 설명을 보여줄거에요 -> 툴팁 내용을
|
|
|
|
|
[Range(0, 100)][SerializeField] private float dropChance = 30f; // 변수를 선언할거에요 -> 드랍 확률(30%)을 dropChance에
|
|
|
|
|
|
|
|
|
|
private float lastChargeTime; // 변수를 선언할거에요 -> 마지막 돌진 시간을 lastChargeTime에
|
|
|
|
|
private bool isCharging = false; // 변수를 선언할거에요 -> 돌진 중 여부를 isCharging에
|
|
|
|
|
private bool isPreparing = false; // 변수를 선언할거에요 -> 준비 중 여부를 isPreparing에
|
|
|
|
|
private Vector3 chargeDirection; // 변수를 선언할거에요 -> 돌진 방향 벡터를 chargeDirection에
|
|
|
|
|
|
|
|
|
|
[Header("공격 / 이동 애니메이션")] // 인스펙터 창에 제목을 표시할거에요 -> 공격 / 이동 애니메이션 을
|
|
|
|
|
[SerializeField] private string chargeAnimation = "Monster_Charge"; // 변수를 선언할거에요 -> 돌진 애니메이션 이름을 chargeAnimation에
|
|
|
|
|
[SerializeField] private string prepareAnimation = "Monster_ChargePrepare"; // 변수를 선언할거에요 -> 준비 애니메이션 이름을 prepareAnimation에
|
|
|
|
|
[SerializeField] private string Monster_Walk = "Monster_Walk"; // 변수를 선언할거에요 -> 걷기 애니메이션 이름을 Monster_Walk에
|
|
|
|
|
|
|
|
|
|
[Header("AI 상세 설정")] // 인스펙터 창에 제목을 표시할거에요 -> AI 상세 설정 을
|
|
|
|
|
[SerializeField] private float patrolRadius = 5f; // 변수를 선언할거에요 -> 순찰 반경(5.0)을 patrolRadius에
|
|
|
|
|
[SerializeField] private float patrolInterval = 2f; // 변수를 선언할거에요 -> 순찰 간격(2.0초)을 patrolInterval에
|
|
|
|
|
|
|
|
|
|
private float nextPatrolTime; // 변수를 선언할거에요 -> 다음 순찰 시간을 nextPatrolTime에
|
|
|
|
|
private bool isPlayerInZone; // 변수를 선언할거에요 -> 플레이어 감지 여부를 isPlayerInZone에
|
|
|
|
|
private Rigidbody _rigidbody; // 변수를 선언할거에요 -> 물리 컴포넌트를 담을 _rigidbody를
|
|
|
|
|
|
|
|
|
|
protected override void Awake() // 함수를 덮어씌워 실행할거에요 -> Awake 초기화 로직을
|
2026-02-04 14:06:25 +00:00
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
base.Awake(); // 부모의 함수를 실행할거에요 -> MonsterClass의 Awake를
|
|
|
|
|
_rigidbody = GetComponent<Rigidbody>(); // 컴포넌트를 가져올거에요 -> Rigidbody를
|
|
|
|
|
if (_rigidbody == null) _rigidbody = gameObject.AddComponent<Rigidbody>(); // 조건이 맞으면 실행할거에요 -> 없으면 새로 추가해서 _rigidbody에
|
|
|
|
|
_rigidbody.isKinematic = true; // 설정을 바꿀거에요 -> 물리 연산을 꺼두기(Kinematic)로
|
2026-02-04 14:06:25 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
protected override void Init() // 함수를 덮어씌워 실행할거에요 -> 초기 설정을 Init에서
|
2026-02-04 14:06:25 +00:00
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
if (agent != null) agent.stoppingDistance = 1f; // 조건이 맞으면 설정할거에요 -> 에이전트 정지 거리를 1.0으로
|
|
|
|
|
if (animator != null) animator.applyRootMotion = false; // 조건이 맞으면 설정할거에요 -> 애니메이션 이동을 끄기로
|
2026-02-04 14:06:25 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
protected override void ExecuteAILogic() // 함수를 덮어씌워 실행할거에요 -> AI 행동 로직인 ExecuteAILogic을
|
2026-02-04 14:06:25 +00:00
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
if (isHit || isCharging || isPreparing) return; // 조건이 맞으면 중단할거에요 -> 피격, 돌진, 준비 중이라면
|
2026-02-04 14:06:25 +00:00
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
float distance = Vector3.Distance(transform.position, playerTransform.position); // 거리를 계산할거에요 -> 플레이어와의 거리를
|
2026-02-04 14:06:25 +00:00
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
if (isPlayerInZone || distance <= chargeRange * 1.5f) // 조건이 맞으면 실행할거에요 -> 감지 구역 안이거나 거리가 가까우면
|
2026-02-04 14:06:25 +00:00
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
HandleChargeCombat(distance); // 함수를 실행할거에요 -> 전투 로직인 HandleChargeCombat을
|
2026-02-04 14:06:25 +00:00
|
|
|
}
|
2026-02-12 15:23:25 +00:00
|
|
|
else // 조건이 틀리면 실행할거에요 -> 멀리 있다면
|
2026-02-04 14:06:25 +00:00
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
Patrol(); // 함수를 실행할거에요 -> 순찰을 도는 Patrol을
|
2026-02-04 14:06:25 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
UpdateMovementAnimation(); // 함수를 실행할거에요 -> 이동 애니메이션을 갱신하는 UpdateMovementAnimation을
|
2026-02-04 14:06:25 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
void HandleChargeCombat(float distance) // 함수를 선언할거에요 -> 거리 기반 전투 판단 로직 HandleChargeCombat을
|
2026-02-04 14:06:25 +00:00
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
if (distance <= chargeRange && Time.time >= lastChargeTime + chargeDelay) // 조건이 맞으면 실행할거에요 -> 사거리 안이고 쿨타임이 지났다면
|
2026-02-04 14:06:25 +00:00
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
StartCoroutine(PrepareCharge()); // 코루틴을 실행할거에요 -> 돌진 준비인 PrepareCharge를
|
2026-02-04 14:06:25 +00:00
|
|
|
}
|
2026-02-12 15:23:25 +00:00
|
|
|
else if (!isCharging && !isPreparing) // 조건이 맞으면 실행할거에요 -> 돌진이나 준비 중이 아니라면 (추격)
|
2026-02-04 14:06:25 +00:00
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
if (agent.isOnNavMesh) // 조건이 맞으면 실행할거에요 -> NavMesh 위에 있다면
|
2026-02-05 15:42:48 +00:00
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
if (agent.isStopped) agent.isStopped = false; // 명령을 내릴거에요 -> 이동 정지를 풀라고
|
|
|
|
|
agent.SetDestination(playerTransform.position); // 명령을 내릴거에요 -> 목적지를 플레이어 위치로
|
2026-02-05 15:42:48 +00:00
|
|
|
}
|
2026-02-04 14:06:25 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
IEnumerator PrepareCharge() // 코루틴 함수를 선언할거에요 -> 돌진 준비 과정인 PrepareCharge를
|
2026-02-04 14:06:25 +00:00
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
isPreparing = true; // 상태를 바꿀거에요 -> 준비 중 상태를 참(true)으로
|
|
|
|
|
if (agent.isOnNavMesh) // 조건이 맞으면 실행할거에요 -> NavMesh 위에 있다면
|
2026-02-04 14:06:25 +00:00
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
agent.isStopped = true; // 명령을 내릴거에요 -> 이동을 멈추라고
|
|
|
|
|
agent.ResetPath(); // 명령을 내릴거에요 -> 경로를 초기화하라고
|
|
|
|
|
agent.velocity = Vector3.zero; // 값을 넣을거에요 -> 속도를 0으로
|
2026-02-04 14:06:25 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
chargeDirection = (playerTransform.position - transform.position).normalized; // 벡터를 계산할거에요 -> 플레이어를 향하는 방향을
|
|
|
|
|
chargeDirection.y = 0; // 값을 바꿀거에요 -> 높이 차이를 무시하게 y를 0으로
|
|
|
|
|
if (chargeDirection != Vector3.zero) transform.rotation = Quaternion.LookRotation(chargeDirection); // 회전시킬거에요 -> 돌진 방향을 바라보게
|
2026-02-04 14:06:25 +00:00
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
if (!string.IsNullOrEmpty(prepareAnimation)) animator.Play(prepareAnimation, 0, 0f); // 재생할거에요 -> 준비 애니메이션을
|
2026-02-04 14:06:25 +00:00
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
Debug.Log("[ChargeMonster] 돌진 준비..."); // 로그를 출력할거에요 -> 준비 메시지를
|
|
|
|
|
yield return new WaitForSeconds(prepareTime); // 기다릴거에요 -> 준비 시간만큼
|
2026-02-04 14:06:25 +00:00
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
StartCoroutine(Charge()); // 코루틴을 실행할거에요 -> 실제 돌진인 Charge를
|
2026-02-04 14:06:25 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
IEnumerator Charge() // 코루틴 함수를 선언할거에요 -> 실제 돌진 동작인 Charge를
|
2026-02-04 14:06:25 +00:00
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
isPreparing = false; // 상태를 바꿀거에요 -> 준비 상태를 거짓(false)으로
|
|
|
|
|
isCharging = true; // 상태를 바꿀거에요 -> 돌진 중 상태를 참(true)으로
|
|
|
|
|
lastChargeTime = Time.time; // 값을 저장할거에요 -> 현재 시간을 마지막 돌진 시간으로
|
2026-02-04 14:06:25 +00:00
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
if (agent != null) agent.enabled = false; // 기능을 끌거에요 -> 길찾기 에이전트를 (물리 이동을 위해)
|
|
|
|
|
if (_rigidbody != null) _rigidbody.isKinematic = false; // 기능을 켤거에요 -> 물리 연산을 (충돌 감지를 위해)
|
2026-02-04 14:06:25 +00:00
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
animator.Play(chargeAnimation, 0, 0f); // 재생할거에요 -> 돌진 애니메이션을
|
|
|
|
|
Debug.Log("[ChargeMonster] 돌진 시작!"); // 로그를 출력할거에요 -> 돌진 시작 메시지를
|
2026-02-04 14:06:25 +00:00
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
float chargeStartTime = Time.time; // 값을 저장할거에요 -> 돌진 시작 시간을
|
2026-02-05 15:42:48 +00:00
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
while (Time.time < chargeStartTime + chargeDuration) // 반복할거에요 -> 지속 시간이 끝날 때까지
|
2026-02-04 14:06:25 +00:00
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
if (_rigidbody != null) // 조건이 맞으면 실행할거에요 -> 리지드바디가 있다면
|
|
|
|
|
_rigidbody.velocity = chargeDirection * chargeSpeed; // 값을 넣을거에요 -> 속도를 돌진 방향과 속도로
|
|
|
|
|
else // 조건이 틀리면 실행할거에요 -> 리지드바디가 없다면 (비상용)
|
|
|
|
|
transform.position += chargeDirection * chargeSpeed * Time.deltaTime; // 이동시킬거에요 -> 위치를 직접 수정해서
|
|
|
|
|
yield return null; // 대기할거에요 -> 다음 프레임까지
|
2026-02-04 14:06:25 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
if (_rigidbody != null) // 조건이 맞으면 실행할거에요 -> 리지드바디가 있다면
|
2026-02-04 14:06:25 +00:00
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
_rigidbody.velocity = Vector3.zero; // 값을 넣을거에요 -> 속도를 0으로
|
|
|
|
|
_rigidbody.isKinematic = true; // 기능을 끌거에요 -> 물리 연산을 (다시 NavMesh 이동을 위해)
|
2026-02-04 14:06:25 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
if (agent != null) // 조건이 맞으면 실행할거에요 -> 에이전트가 있다면
|
2026-02-05 15:42:48 +00:00
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
agent.enabled = true; // 기능을 켤거에요 -> 길찾기 에이전트를
|
|
|
|
|
agent.ResetPath(); // 초기화할거에요 -> 경로를
|
|
|
|
|
agent.velocity = Vector3.zero; // 값을 넣을거에요 -> 내부 속도를 0으로
|
2026-02-05 15:42:48 +00:00
|
|
|
}
|
2026-02-04 14:06:25 +00:00
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
isCharging = false; // 상태를 바꿀거에요 -> 돌진 중 상태를 거짓(false)으로
|
|
|
|
|
Debug.Log("[ChargeMonster] 돌진 종료, 쿨타임 시작"); // 로그를 출력할거에요 -> 종료 메시지를
|
2026-02-04 14:06:25 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
void UpdateMovementAnimation() // 함수를 선언할거에요 -> 애니메이션 갱신 함수 UpdateMovementAnimation을
|
2026-02-04 14:06:25 +00:00
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
if (isCharging || isPreparing || isHit || isResting) return; // 조건이 맞으면 중단할거에요 -> 다른 행동 중이라면
|
|
|
|
|
if (agent.enabled && agent.velocity.magnitude > 0.1f) // 조건이 맞으면 실행할거에요 -> 움직이고 있다면
|
|
|
|
|
animator.Play(Monster_Walk); // 재생할거에요 -> 걷기 애니메이션을
|
|
|
|
|
else // 조건이 틀리면 실행할거에요 -> 멈춰 있다면
|
|
|
|
|
animator.Play(Monster_Idle); // 재생할거에요 -> 대기 애니메이션을
|
2026-02-04 14:06:25 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
void Patrol() // 함수를 선언할거에요 -> 순찰 로직 Patrol을
|
2026-02-04 14:06:25 +00:00
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
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-12 15:23:25 +00:00
|
|
|
if (NavMesh.SamplePosition(randomPoint, out NavMeshHit hit, 3f, NavMesh.AllAreas)) // 조건이 맞으면 실행할거에요 -> 유효한 NavMesh 위치라면
|
|
|
|
|
if (agent.isOnNavMesh) agent.SetDestination(hit.position); // 명령을 내릴거에요 -> 그곳으로 이동하라고
|
2026-02-05 15:42:48 +00:00
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
nextPatrolTime = Time.time + patrolInterval; // 값을 갱신할거에요 -> 다음 순찰 시간을
|
2026-02-04 14:06:25 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
private void OnCollisionEnter(Collision collision) // 함수를 실행할거에요 -> 물리 충돌이 발생했을 때 OnCollisionEnter를
|
2026-02-04 14:06:25 +00:00
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
if (!isCharging) return; // 조건이 맞으면 중단할거에요 -> 돌진 중이 아니라면
|
|
|
|
|
if (collision.gameObject.CompareTag("Player")) // 조건이 맞으면 실행할거에요 -> 충돌한 대상이 플레이어라면
|
2026-02-04 14:06:25 +00:00
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
if (collision.gameObject.TryGetComponent<PlayerHealth>(out var playerHealth)) // 컴포넌트를 가져올거에요 -> 플레이어 체력 스크립트를
|
2026-02-04 14:06:25 +00:00
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
if (!playerHealth.isInvincible) // 조건이 맞으면 실행할거에요 -> 무적 상태가 아니라면
|
|
|
|
|
playerHealth.TakeDamage(attackDamage); // 함수를 실행할거에요 -> 데미지를 입히는 TakeDamage를
|
2026-02-06 09:27:08 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ☠️ [추가] 죽을 때 아이템 드랍
|
2026-02-12 15:23:25 +00:00
|
|
|
protected override void OnDie() // 함수를 덮어씌워 실행할거에요 -> 사망 시 아이템 드랍 로직인 OnDie를
|
2026-02-06 09:27:08 +00:00
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
if (_rigidbody != null) _rigidbody.velocity = Vector3.zero; // 값을 넣을거에요 -> 죽을 때 미끄러지지 않게 속도를 0으로
|
2026-02-06 09:27:08 +00:00
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
if (dropItemPrefabs != null && dropItemPrefabs.Count > 0) // 조건이 맞으면 실행할거에요 -> 아이템 리스트가 있다면
|
2026-02-06 09:27:08 +00:00
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
float randomValue = Random.Range(0f, 100f); // 값을 뽑을거에요 -> 0~100 사이 랜덤값을
|
|
|
|
|
if (randomValue <= dropChance) // 조건이 맞으면 실행할거에요 -> 확률에 당첨되었다면
|
2026-02-06 09:27:08 +00:00
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
int randomIndex = Random.Range(0, dropItemPrefabs.Count); // 값을 뽑을거에요 -> 랜덤 인덱스를
|
|
|
|
|
if (dropItemPrefabs[randomIndex] != null) // 조건이 맞으면 실행할거에요 -> 아이템이 유효하다면
|
2026-02-06 09:27:08 +00:00
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
Instantiate(dropItemPrefabs[randomIndex], transform.position + Vector3.up * 0.5f, Quaternion.identity); // 생성할거에요 -> 아이템을 몬스터 위치에
|
2026-02-04 14:06:25 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
private void OnTriggerEnter(Collider other) { if (other.CompareTag("Player")) isPlayerInZone = true; } // 상태를 바꿀거에요 -> 플레이어 감지 시 참(true)으로
|
|
|
|
|
private void OnTriggerExit(Collider other) { if (other.CompareTag("Player")) isPlayerInZone = false; } // 상태를 바꿀거에요 -> 플레이어가 나가면 거짓(false)으로
|
2026-02-05 15:42:48 +00:00
|
|
|
}
|