2026-02-12 15:23:25 +00:00
|
|
|
|
using UnityEngine; // 유니티 엔진의 기본 기능을 불러올거에요 -> UnityEngine을
|
2026-02-02 08:30:23 +00:00
|
|
|
|
|
2026-02-03 14:41:49 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 하이브리드 몬스터 스포너 (생성은 스포너 기준, 생존은 몬스터 기준)
|
|
|
|
|
|
/// - Birth: 스포너 기준 거리로 생성
|
|
|
|
|
|
/// - Life & Hibernate: 몬스터 기준 거리로 최적화
|
|
|
|
|
|
/// - Wake Up: 범위 안으로 복귀 시 재활성화
|
|
|
|
|
|
/// </summary>
|
2026-02-12 15:23:25 +00:00
|
|
|
|
public class MonsterSpawner : MonoBehaviour // 클래스를 선언할거에요 -> MonoBehaviour를 상속받는 MonsterSpawner를
|
2026-02-02 08:30:23 +00:00
|
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
|
[Header("=== 생성 설정 (Birth - 스포너 기준) ===")] // 인스펙터 창에 제목을 표시할거에요 -> === 생성 설정 (Birth - 스포너 기준) === 을
|
|
|
|
|
|
[Tooltip("스포너로부터 이 거리 안에 플레이어가 들어오면 몬스터 생성")] // 마우스를 올리면 설명을 보여줄거에요 -> 툴팁 내용을
|
|
|
|
|
|
[SerializeField] private float spawnRange = 25f; // 변수를 선언할거에요 -> 몬스터 생성 거리(25.0)를 spawnRange에
|
2026-02-03 14:41:49 +00:00
|
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
|
[Tooltip("몬스터가 죽은 후 재생성까지 대기 시간")] // 마우스를 올리면 설명을 보여줄거에요 -> 툴팁 내용을
|
|
|
|
|
|
[SerializeField] private float respawnCooldown = 3f; // 변수를 선언할거에요 -> 재소환 대기시간(3초)을 respawnCooldown에
|
2026-02-03 14:41:49 +00:00
|
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
|
[Tooltip("오브젝트 풀에서 가져올 몬스터 태그")] // 마우스를 올리면 설명을 보여줄거에요 -> 툴팁 내용을
|
|
|
|
|
|
[SerializeField] private string mobTag = "NormalMob"; // 변수를 선언할거에요 -> 소환할 몬스터의 태그 이름("NormalMob")을 mobTag에
|
2026-02-02 08:30:23 +00:00
|
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
|
[Header("=== 최적화 설정 (Life & Hibernate - 몬스터 기준) ===")] // 인스펙터 창에 제목을 표시할거에요 -> === 최적화 설정 (Life & Hibernate - 몬스터 기준) === 을
|
|
|
|
|
|
[Tooltip("몬스터 본체로부터 이 거리를 벗어나면 강제 동면 (전투 중이어도!)")] // 마우스를 올리면 설명을 보여줄거에요 -> 툴팁 내용을
|
|
|
|
|
|
[SerializeField] private float optimizationRange = 60f; // 변수를 선언할거에요 -> 몬스터 최적화(동면) 거리(60.0)를 optimizationRange에
|
2026-02-03 14:41:49 +00:00
|
|
|
|
|
|
|
|
|
|
// 내부 상태
|
2026-02-12 15:23:25 +00:00
|
|
|
|
private GameObject _myMonster; // 변수를 선언할거에요 -> 소환된 몬스터 게임오브젝트를 담을 _myMonster를
|
|
|
|
|
|
private MonsterClass _monsterScript; // 변수를 선언할거에요 -> 몬스터의 스크립트(MonsterClass)를 담을 _monsterScript를
|
|
|
|
|
|
private Transform _player; // 변수를 선언할거에요 -> 플레이어의 위치 정보를 담을 _player를
|
|
|
|
|
|
private float _nextSpawnTime; // 변수를 선언할거에요 -> 다음 소환 가능 시간을 저장할 _nextSpawnTime을
|
2026-02-02 08:30:23 +00:00
|
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
|
private void Start() // 함수를 실행할거에요 -> 게임 시작 시 호출되는 Start를
|
2026-02-02 08:30:23 +00:00
|
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
|
FindPlayer(); // 함수를 실행할거에요 -> 플레이어를 찾는 FindPlayer를
|
2026-02-02 08:30:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
|
private void FindPlayer() // 함수를 선언할거에요 -> 플레이어를 찾아 변수에 저장하는 FindPlayer를
|
2026-02-03 14:41:49 +00:00
|
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
|
GameObject playerObj = GameObject.FindGameObjectWithTag("Player"); // 오브젝트를 찾을거에요 -> "Player" 태그가 붙은 오브젝트를 playerObj에
|
|
|
|
|
|
if (playerObj != null) _player = playerObj.transform; // 조건이 맞으면 실행할거에요 -> 플레이어를 찾았다면 그 위치 정보를 _player에 저장
|
2026-02-03 14:41:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
|
private void Update() // 함수를 실행할거에요 -> 매 프레임마다 호출되는 Update를
|
2026-02-02 08:30:23 +00:00
|
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
|
if (_player == null) { FindPlayer(); return; } // 조건이 맞으면 실행할거에요 -> 플레이어 정보가 없다면 다시 찾고 이번 프레임은 중단(return)
|
2026-02-02 08:30:23 +00:00
|
|
|
|
|
2026-02-03 14:41:49 +00:00
|
|
|
|
// 몬스터가 없거나 죽었으면 -> Birth
|
2026-02-12 15:23:25 +00:00
|
|
|
|
if (_myMonster == null || (_monsterScript != null && _monsterScript.IsDead)) // 조건이 맞으면 실행할거에요 -> 몬스터가 없거나, 몬스터 스크립트가 있는데 죽은 상태라면
|
2026-02-02 08:30:23 +00:00
|
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
|
HandleBirth(); // 함수를 실행할거에요 -> 몬스터 생성 로직인 HandleBirth를
|
2026-02-02 08:30:23 +00:00
|
|
|
|
}
|
2026-02-03 14:41:49 +00:00
|
|
|
|
// 몬스터가 살아있으면 -> Life & Hibernate
|
2026-02-12 15:23:25 +00:00
|
|
|
|
else // 조건이 틀리면 실행할거에요 -> 몬스터가 살아서 활동 중이라면
|
2026-02-02 08:30:23 +00:00
|
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
|
HandleLifeAndHibernate(); // 함수를 실행할거에요 -> 생존 및 최적화(동면) 로직인 HandleLifeAndHibernate를
|
2026-02-02 08:30:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-03 14:41:49 +00:00
|
|
|
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
|
|
|
|
// 🐣 BIRTH (생성) - 스포너 기준
|
|
|
|
|
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
2026-02-02 08:30:23 +00:00
|
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
|
private void HandleBirth() // 함수를 선언할거에요 -> 몬스터 생성 조건을 확인하는 HandleBirth를
|
2026-02-02 08:30:23 +00:00
|
|
|
|
{
|
2026-02-03 14:41:49 +00:00
|
|
|
|
// ⭐ 스포너 <-> 플레이어 거리
|
2026-02-12 15:23:25 +00:00
|
|
|
|
float distanceFromSpawner = Vector3.Distance(transform.position, _player.position); // 값을 계산할거에요 -> 스포너와 플레이어 사이의 거리를 distanceFromSpawner에
|
2026-02-03 14:41:49 +00:00
|
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
|
if (distanceFromSpawner <= spawnRange && Time.time >= _nextSpawnTime) // 조건이 맞으면 실행할거에요 -> 거리가 생성 범위 이내이고, 쿨타임 시간이 지났다면
|
2026-02-02 15:02:12 +00:00
|
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
|
SpawnMonster(); // 함수를 실행할거에요 -> 실제 몬스터를 소환하는 SpawnMonster를
|
2026-02-02 15:02:12 +00:00
|
|
|
|
}
|
2026-02-03 14:41:49 +00:00
|
|
|
|
}
|
2026-02-02 15:02:12 +00:00
|
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
|
private void SpawnMonster() // 함수를 선언할거에요 -> 오브젝트 풀에서 몬스터를 가져오는 SpawnMonster를
|
2026-02-03 14:41:49 +00:00
|
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
|
_myMonster = GenericObjectPool.Instance.SpawnFromPool(mobTag, transform.position, transform.rotation); // 오브젝트를 가져올거에요 -> 풀(Pool)에서 mobTag에 맞는 몬스터를 스포너 위치에 소환해서 _myMonster에
|
2026-02-02 08:30:23 +00:00
|
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
|
if (_myMonster != null) // 조건이 맞으면 실행할거에요 -> 몬스터 소환에 성공했다면
|
2026-02-02 08:30:23 +00:00
|
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
|
_monsterScript = _myMonster.GetComponent<MonsterClass>(); // 컴포넌트를 가져올거에요 -> 몬스터의 핵심 스크립트(MonsterClass)를 _monsterScript에
|
|
|
|
|
|
if (_monsterScript != null) _monsterScript.ResetStats(); // 조건이 맞으면 실행할거에요 -> 스크립트가 있다면 몬스터의 상태(체력 등)를 초기화(ResetStats)
|
2026-02-02 08:30:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-03 14:41:49 +00:00
|
|
|
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
|
|
|
|
// 💤 LIFE & HIBERNATE (생존/동면) - 몬스터 기준
|
|
|
|
|
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
|
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
|
private void HandleLifeAndHibernate() // 함수를 선언할거에요 -> 몬스터의 활동 및 최적화를 관리하는 HandleLifeAndHibernate를
|
2026-02-02 08:30:23 +00:00
|
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
|
if (_myMonster == null) { _monsterScript = null; return; } // 조건이 맞으면 실행할거에요 -> 몬스터 오브젝트가 사라졌다면 스크립트 변수도 비우고 중단(return)
|
2026-02-03 14:41:49 +00:00
|
|
|
|
|
|
|
|
|
|
// ⭐ 몬스터 본체 <-> 플레이어 거리
|
2026-02-12 15:23:25 +00:00
|
|
|
|
float distanceFromMonster = Vector3.Distance(_myMonster.transform.position, _player.position); // 값을 계산할거에요 -> 몬스터와 플레이어 사이의 거리를 distanceFromMonster에
|
2026-02-03 14:41:49 +00:00
|
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
|
if (distanceFromMonster > optimizationRange) // 조건이 맞으면 실행할거에요 -> 몬스터와 플레이어 거리가 최적화 범위(optimizationRange)보다 멀다면
|
2026-02-02 08:30:23 +00:00
|
|
|
|
{
|
2026-02-03 14:41:49 +00:00
|
|
|
|
// 😴 HIBERNATE (동면)
|
2026-02-12 15:23:25 +00:00
|
|
|
|
HibernateMonster(); // 함수를 실행할거에요 -> 몬스터를 잠재우는 HibernateMonster를
|
2026-02-03 14:41:49 +00:00
|
|
|
|
}
|
2026-02-12 15:23:25 +00:00
|
|
|
|
else // 조건이 틀리면 실행할거에요 -> 거리가 가깝다면
|
2026-02-03 14:41:49 +00:00
|
|
|
|
{
|
|
|
|
|
|
// 👁 WAKE UP (기상)
|
2026-02-12 15:23:25 +00:00
|
|
|
|
WakeUpMonster(); // 함수를 실행할거에요 -> 몬스터를 깨우는 WakeUpMonster를
|
2026-02-03 14:41:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
|
private void HibernateMonster() // 함수를 선언할거에요 -> 몬스터를 비활성화하는 HibernateMonster를
|
2026-02-03 14:41:49 +00:00
|
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
|
if (_myMonster.activeSelf) // 조건이 맞으면 실행할거에요 -> 몬스터가 현재 켜져 있는(Active) 상태라면
|
2026-02-03 14:41:49 +00:00
|
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
|
_myMonster.SetActive(false); // 기능을 끌거에요 -> 몬스터 오브젝트를 비활성화(false)로
|
2026-02-03 14:41:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
|
private void WakeUpMonster() // 함수를 선언할거에요 -> 몬스터를 다시 활성화하는 WakeUpMonster를
|
2026-02-03 14:41:49 +00:00
|
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
|
if (!_myMonster.activeSelf) // 조건이 맞으면 실행할거에요 -> 몬스터가 현재 꺼져 있는(Inactive) 상태라면
|
2026-02-03 14:41:49 +00:00
|
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
|
_myMonster.SetActive(true); // 기능을 켤거에요 -> 몬스터 오브젝트를 활성화(true)로
|
2026-02-03 14:41:49 +00:00
|
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
|
if (_monsterScript != null) // 조건이 맞으면 실행할거에요 -> 몬스터 스크립트가 존재한다면
|
2026-02-02 08:30:23 +00:00
|
|
|
|
{
|
2026-02-03 14:41:49 +00:00
|
|
|
|
// ⭐ AI 복구 (전투 중이었다면 추적 재개)
|
2026-02-12 15:23:25 +00:00
|
|
|
|
_monsterScript.Reactivate(); // 함수를 실행할거에요 -> 몬스터의 AI를 다시 작동시키는 Reactivate를
|
2026-02-02 08:30:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-03 14:41:49 +00:00
|
|
|
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
|
|
|
|
// 💀 DEATH (사망 처리 및 쿨타임)
|
|
|
|
|
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 몬스터가 죽었을 때 호출 (쿨타임 적용)
|
|
|
|
|
|
/// </summary>
|
2026-02-12 15:23:25 +00:00
|
|
|
|
public void NotifyMonsterDead() // 함수를 선언할거에요 -> 외부에서 몬스터 사망을 알릴 때 쓰는 NotifyMonsterDead를
|
2026-02-03 14:41:49 +00:00
|
|
|
|
{
|
|
|
|
|
|
// 쿨타임 계산: 현재 시간 + 대기 시간
|
2026-02-12 15:23:25 +00:00
|
|
|
|
_nextSpawnTime = Time.time + respawnCooldown; // 값을 저장할거에요 -> 현재 시간에 쿨타임을 더해서 다음 소환 시간(_nextSpawnTime)으로
|
2026-02-03 14:41:49 +00:00
|
|
|
|
|
|
|
|
|
|
// 몬스터 참조를 비워서 Update에서 다시 HandleBirth()로 넘어가게 함
|
2026-02-12 15:23:25 +00:00
|
|
|
|
_myMonster = null; // 값을 비울거에요 -> 소환된 몬스터 변수를 null로
|
|
|
|
|
|
_monsterScript = null; // 값을 비울거에요 -> 몬스터 스크립트 변수를 null로
|
2026-02-03 14:41:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
|
|
|
|
// Gizmos
|
|
|
|
|
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
|
|
|
|
|
2026-02-12 15:23:25 +00:00
|
|
|
|
private void OnDrawGizmosSelected() // 함수를 실행할거에요 -> 에디터에서 오브젝트 선택 시 기즈모를 그리는 OnDrawGizmosSelected를
|
2026-02-02 08:30:23 +00:00
|
|
|
|
{
|
2026-02-03 14:41:49 +00:00
|
|
|
|
// 스포너 생성 범위 (빨간색)
|
2026-02-12 15:23:25 +00:00
|
|
|
|
Gizmos.color = Color.red; // 색상을 설정할거에요 -> 기즈모 색을 빨간색으로
|
|
|
|
|
|
Gizmos.DrawWireSphere(transform.position, spawnRange); // 그림을 그릴거에요 -> 스포너 위치에 생성 범위(spawnRange) 크기의 원을
|
2026-02-03 14:41:49 +00:00
|
|
|
|
|
|
|
|
|
|
// 몬스터 최적화 범위 (파란색)
|
2026-02-12 15:23:25 +00:00
|
|
|
|
if (_myMonster != null) // 조건이 맞으면 실행할거에요 -> 소환된 몬스터가 있다면
|
2026-02-03 14:41:49 +00:00
|
|
|
|
{
|
2026-02-12 15:23:25 +00:00
|
|
|
|
Gizmos.color = Color.blue; // 색상을 설정할거에요 -> 기즈모 색을 파란색으로
|
|
|
|
|
|
Gizmos.DrawWireSphere(_myMonster.transform.position, optimizationRange); // 그림을 그릴거에요 -> 몬스터 위치에 최적화 범위(optimizationRange) 크기의 원을
|
2026-02-03 14:41:49 +00:00
|
|
|
|
}
|
2026-02-02 08:30:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|