[Unity] 몬스터 자동 생성

Jihoon·2022년 5월 3일
0

MMO_Unity

목록 보기
22/22

이번엔 몬스터를 게임이 실행되기 전에 미리 배치하지 않고 게임이 실행되고 나서 자동으로 생성되게 해 볼 것이다.

Spawn

먼저 몬스터를 스폰하는 스크립트를 작성해준다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

public class SpawningPool : MonoBehaviour
{
    [SerializeField]
    int _monsterCount = 0;
	int _reserveCount = 0; // 코루틴을 생성할 때 현재 예약된 코루틴이 몇개인지 판단하기 위해
    [SerializeField] 
    int _keepMonsterCount = 0;
    [SerializeField]
    Vector3 _spawnPos;
    [SerializeField]
    float _spawnRadius = 15.0f;
    [SerializeField]
    float _spawnTime = 5.0f;

    public void AddMonsterCount(int value) { _monsterCount += value; }
    public void SetKeepMonsterCount(int count) { _keepMonsterCount = count; }

    void Start()
    {
        Managers.Game.OnSpawnEvent -= AddMonsterCount;
        Managers.Game.OnSpawnEvent += AddMonsterCount;
    }

    void Update()
    {
        while (_reserveCount + _monsterCount < _keepMonsterCount)
        // 현재 몬스터의 수와 예약된 코루틴의 수가 _keepMonsterCount보다 적다면 코루틴 실행
        {
            StartCoroutine("ReserveSpawn");
        }
    }

    IEnumerator ReserveSpawn()
    {
        _reserveCount++;
        yield return new WaitForSeconds(Random.Range(0, _spawnTime));
        GameObject obj = Managers.Game.Spawn(Define.WorldObject.Monster, "DogPolyart");
        // _monsterCount를 이 함수에서 늘리지 않아도 GameManagerEx에서 Spawn함수가 실행될 때 Invoke로 _monsterCount를 늘려준다.
        NavMeshAgent nma =  obj.GetOrAddComponent<NavMeshAgent>();

        Vector3 randPos;

        while (true)
        {
            Vector3 randDir = Random.insideUnitSphere * Random.Range(0, _spawnRadius);
            randDir.y = 0;
            randPos = _spawnPos + randDir;
            // 랜덤한 방향벡터를 생성한다.
            // 몬스터는 평면에 위치해야 하기 때문에 생성된 랜덤벡터의 y좌표를 0으로 만들어준다.

            NavMeshPath path = new NavMeshPath();
            if (nma.CalculatePath(randPos, path))
                break;
            // 생성된 랜덤벡터에 갈 수 있나 없나 계산
        }

        obj.transform.position = randPos;
        _reserveCount--;
    }
}

코루틴을 사용해 랜덤 생성한 벡터에 적을 생성하도록 하였다.
필드 내에 총 몇마리의 적이 있어야 되는지 정하고, 그 정한 수보다 현재 몬스터가 적다면 몬스터를 생성한다. 생성되는 주기는 0~5초 사이의 랜덤한 값이 된다.

현재 몬스터 수의 계산은 GameManagerEx 클래스의 이벤트를 받아서 계산한다.

public class GameManagerEx
{
	...
    public Action<int> OnSpawnEvent;
    ...
    public GameObject Spawn(Define.WorldObject type, string path, Transform parent = null)
    {
        GameObject go = Managers.Resource.Instantiate(path, parent);
        switch (type)
        {
            case Define.WorldObject.Monster:
                _monsters.Add(go);
                if (OnSpawnEvent != null)
                    OnSpawnEvent.Invoke(1);
                break;
                ...
    }
	...
    public void Despawn(GameObject go)
    {
        Define.WorldObject type = GetWorldObjectType(go);

        switch (type)
        {
            case Define.WorldObject.Monster:
                {
                    if (_monsters.Contains(go))
                    {
                        if (OnSpawnEvent != null)
                            OnSpawnEvent.Invoke(-1);
                        _monsters.Remove(go);
                    }
                }
                ...
}

GameManagerEx의 Spawn()함수가 실행될 때 몬스터가 스폰된다는 이벤트를 Invoke하며 인자로 1의 값을 보낸다. (몬스터가 1마리 늘었다는 뜻)

Despawn() 함수가 실행될 때는 Invoke하며 인자로 -1의 값을 보낸다.

public class GameScene : BaseScene
{

    protected override void Init()
    {
        ...
        GameObject go = new GameObject { name = "SpawningPool" };
        SpawningPool pool = go.GetOrAddComponent<SpawningPool>();
        pool.SetKeepMonsterCount(5);
    }
}

후에 GameScene 클래스에서 코드를 추가해준다.

이후 게임을 실행해보니 원하는대로 작동하는것을 확인했다.

하지만 몬스터 생성 관련 버그는 아니지만 버그를 하나 발견했는데, 몬스터를 클릭할 때 가끔식 몬스터 위를 걸어가는 버그가 발생한다. 이는 플레이어의 좌표를 변경할 때 y좌표를 0으로 처리하지 않아서 생기는 문제이다.

Vector3 dir = _destPos - transform.position;
dir.y = 0; // 추가

PlayerController 클래스의 UpdateMoving()함수를 이런식으로 수정하니 해결되었다.

profile
클라이언트 개발자 지망생

0개의 댓글