기존에는 매일 밤이되면 GameManager
가 몬스터웨이브를 시작하는 구조로 작성돼있었다.
이제는 Artifact
클래스가 몬스터 웨이브와 통신할 것이기 때문에 MosnterWave
클래스의 구조를 살짝 변경했다.
그리고, MonsterWave
클래스가 해야할 것만 같은 일을 GameManager
에서 하고 있었기 때문에, 리팩토링도 살짝했다.
GameManager.cs
private void SpawnMonster()
{
int cnt = 1;
if (Season.CurrentValue >= 0f)
{
for (int i = 0; i < cnt; ++i)
MonsterWave.AddOverFlowedMonster(FireIsland.Spawn());
}
else
{
for (int i = 0; i < cnt; ++i)
MonsterWave.AddOverFlowedMonster(IceIsland.Spawn());
}
}
private void StartMonsterWave()
{
CoroutineManagement.Instance.StartCoroutine(MonsterWaveCoroutine());
}
private IEnumerator MonsterWaveCoroutine()
{
var delay = UnityEngine.Random.Range(0.0f, 60.0f);
yield return new WaitForSeconds(delay);
MonsterWave.Start();
}
MonsterWave.cs
public void Start()
{
// 몬스터 웨이브 시작
}
public void Start(float delayTime)
{
CoroutineManagement.Instance.StartCoroutine(MonsterWaveCoroutine(delayTime));
}
private IEnumerator MonsterWaveCoroutine(float delayTime)
{
yield return new WaitForSeconds(delayTime);
Start();
}
게임매니저가 랜덤한 지연시간만큼 뒤에 몬스터웨이브를 시작하는 기능을 갖고 있었는데, MosnterWave
클래스의 Start 메서드를 오버로딩하여 지연하여 시작할 수 있도록 구조를 변경했다.
Artifact.cs
public void SetInfo(Island island, Vector3 pos, Transform root, ArtifactData data, float? currentHP = null)
{
...
_dayCycle = Managers.Game.DayCycle;
_dayCycle.OnEveningCame += SpawnOverflowedMonster;
}
public void SpawnOverflowedMonster()
{
Managers.Game.MonsterWave.AddOverFlowedMonster(_island.Spawn());
}
public void DestroyArtifact()
{
...
_dayCycle.OnEveningCame -= SpawnOverflowedMonster;
OnDestroy?.Invoke(this);
Destroy(gameObject);
}
아티팩트가 몬스터 웨이브 클래스에게 생성될 몬스터를 추가할 수 있게 했다.
매일 저녁, 자신의 속성 (자신이 속한 섬)에 알맞는 몬스터를 소환하고, 해당 몬스터를 몬스터 웨이브에게 OverFlowedMonster
로 설정하도록 했다.
OverFlowedMonster
란, 생성되어 있는 몬스터 중 몬스터 웨이브가 시작되면 플레이어에게 달려들게 되는 몬스터다.
몬스터 웨이브는 OverFlowedMonster
를 컬렉션으로 관리하며, 웨이브가 시작되면 컬렉션 안에 있는 몬스터들을 플레이어 주변으로 텔레포트 시킨다음 달려들게 한다.
바로 위에서 설명한 OverFlowedMonster
의 설명만 들으면 바로 알아차릴 수 있는 문제가 있다.
아티팩트에 의해 생성된 몬스터는 OverFlowedMonster
고, 밤이 되면 몬스터웨이브에 의해 플레이어 시야 밖으로 텔레포트 된다.
그런데 지금은 이미 플레이어 시야 안에 있는지를 걸러주지 않고 텔레포트 시키기 때문에, 플레이어와 치고받던 몬스터가 밤이되면 갑자기 텔레포트된다. (플레이어 입장에선 사라지는 것 처럼 느껴질 것이다.)
MonsterWave.cs
while(waveMonsters.Count != 0)
{
var point = wavePoints.Pop();
var monster = waveMonsters.Pop().GetComponent<Monster>();
monster.NavMeshAgent.Warp(point);
monster.SetBerserkMode();
monster.SetIsland(null);
monsters.Add(monster);
}
MonsterWave.cs
while(waveMonsters.Count != 0)
{
var point = wavePoints.Pop();
var monster = waveMonsters.Pop().GetComponent<Monster>();
if (!monster.GetCurrentChunkActive())
monster.NavMeshAgent.Warp(point);
monster.SetBerserkMode();
monster.SetIsland(null);
monsters.Add(monster);
}
비활성화된 청크에 있는 몬스터만 워프시키도록 변경했다.
Monster
클래스는 생성 시 ManagementedObject
컴포넌트를 추가하여 자신의 렌더러와 콜라이더, Monster
Behaviour를 관리하도록 하고 있다.
자신이 위치한 청크의 활성화 여부에 따라 각종 컴포넌트를 껐다 키는 방식이다.
따라서 플레이어와 먼 거리에 있는 몬스터들은 Update
메서드의 호출이나 물리연산, 렌더링 등을 하지 않도록 관리하고 있다.
하지만 MonsterWave
는 비활성화된 청크에 있는 몬스터들을 플레이어 근처로 텔레포트 시킨다.
이 과정에서 텔레포트된 몬스터는 각종 컴포넌트가 꺼져있는 상태로 유지되고, 플레이어가 다른 청크로 이동하기 전까지 그 상태를 유지한다.
따라서 몬스터를 워프 시킨 후, ManagementedObject
에게 컴포넌트 상태를 스위칭하도록 요청했다.
하지만 오류가 났다. 몬스터 웨이브는 기존에 생성돼있던 몬스터만 워프시키는 것이 아니라 자체적으로 생성한 몬스터를 워프시키기도 하는데, Monster
컴포넌트는 Start
메서드에서 MangementedObject
를 AddComponent하기 때문에, 생성하자 마자 바로 워프시키는 지금의 구조에선 ManagementedObject
가 아직 게임오브젝트에 등록되기 전이라 null 에러가 났다.
이 부분은 간단하게 널 체크 만으로 해결 가능했다.
MonsterWave.cs
while(waveMonsters.Count != 0)
{
var point = wavePoints.Pop();
var monster = waveMonsters.Pop().GetComponent<Monster>();
if (!monster.GetCurrentChunkActive())
{
monster.NavMeshAgent.Warp(point);
monster.GetComponent<ManagementedObject>()?.SwitchEnabled();
}
monster.SetBerserkMode();
monster.SetIsland(null);
monsters.Add(monster);
}