공부/인프런 - Rookiss

Part 3-13-9. 미니 RPG : 몬스터 자동 생성, 完

셩잇님 2023. 9. 2. 13:29
반응형

 

 

미니 RPG

몬스터 자동 리스폰

서버 측에서 한다. 몬스터가 죽으면 몬스터의 전체적인 숫자를 맞춰주기 위해 랜덤한 위치에서 스폰을 한다. 이렇게 서버측에서 스폰을 한 후 클라이언트에게 알려준다.

 

📜 GameManager

    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;
            case Define.WorldObject.Player:
                _player = go;
                break;
        }

        return go;
    }

    public void Despawn(GameObject go)
    {
        Define.WorldObject type = GetWorldObjectType(go);

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

        Managers.Resource.Destroy(go);
    }
                if (OnSpawnEvent != null)
                    OnSpawnEvent.Invoke(1);
                
                //...

                if (OnSpawnEvent != null)
                    OnSpawnEvent.Invoke(-1);

 

OnSpawnEvent

이 액션에 어떤 함수들이 등록되어 있을진 모르겟지만! 몬스터를 스폰할 때, 디스폰할 때 이 액션을 실행한다. 이 액션에는 파라미터만큼 몬스터를 증가 및 감소시키는 함수가 등록될 것이다.

 

스폰시 👉 몬스터 1마리 증가 OnSpawnEvent.Invoke(1);

디스폰시 👉 몬스터 1마리 감소 OnSpawnEvent.Invoke(-1);

 

📜 SpawningPool

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

public class SpawningPool : MonoBehaviour
{
    [SerializeField] int _monsterCount = 0;     // 현재 몬스터 수
    [SerializeField] int _keepMonsterCount = 0; // 월드 상에 유지되야 하는 몬스터 수
    int _reserveCount = 0;  // ReserveSpawn 코루틴 함수 당 곧 생성할 몬스터 수

    [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)
        {
            StartCoroutine(ReserveSpawn());
        }
    }

    IEnumerator ReserveSpawn()
    {
        _reserveCount++;
        yield return new WaitForSeconds(Random.Range(0, _spawnTime));
        GameObject obj = Managers.Game.Spawn(Define.WorldObject.Monster, "Knight");

        Vector3 randPos;
        NavMeshAgent nma = obj.GetOrAddComponent<NavMeshAgent>();
        while(true)
        {
            Vector3 randDir = Random.insideUnitSphere * Random.Range(0, _spawnRadius);
            randDir.y = 0;
            randPos = _spawnPos + randDir;

            NavMeshPath path = new NavMeshPath();
            if (nma.CalculatePath(randPos, path))
                break;
        }

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

 

AddMonsterCount : 몬스터를 파라미터만큼 누적 증가시키기 위해 _monsterCount 업뎃

게임 시작시 📜GameManager의 OnSpawnEvent에 등록

 

SetKeepMonsterCount : 몬스터를 파라미터만큼 증가시키기 위해 _keepMonsterCount 업뎃

 

Update

monsterCount가 keepMonsterCount에 다다를 떄까지 몬스터 생성

reserveCount가 쓰이는 이유는 아래에 서술한다.

 

ReserveSpawn

이 코루틴 함수는 한 마리의 몬스터 스폰을 위한 함수다. 랜덤으로 0 ~ _spawnTime 시간 내의 1️⃣ 랜덤한 시간을 “대기”한 후에 2️⃣ 몬스터를 생성하고 3️⃣ 스폰된 몬스터의 위치를 랜덤하게 세팅하는 함수다.

 

1. _reserveCount 1 증가

1 - 1. _reserveCount을 증가시키는 이유

  • 이 코루틴이 yield return을 한 후 WaitForSeconds로 인하여 대기하는 시간 동안 코루틴의 호출점이였던 Update의 while문이 마저 실행된다. (코루틴은 대기 시간동안 코루틴 호출점을 마저 실행한다. 그리고 대기 시간이 다 끝나면 다시 코루틴 함수 내부로 돌아온다.)
  • _monsterCount는 대기시간을 가진 이후에 📜GameManager의 Spawn 함수의 OnSpawnEvent.Invoke을 통해 세팅된다. 따라서 📜SpawningPool의 Update의 while 문에서는 아직 이 함수가 정상적으로 설정되지 않았기 때문에 무한루프가 돌게 된다.
  • 따라서 대기시간을 가지는 동안 코루틴을 나와 while문을 다시 실행할 때는 _monsterCount가 아직 1 증가가 되지 않은 상태이기 때문에 while문이 무한 루프로 돌게 된다. _monsterCount < _keepMonsterCount 이 조건이 만족하니까 ReserveSpawn 을 또 호출 하고 또 호출하고.. 대기 시간동안 ReserveSpawn 을 엄청 많이 호출하게 될 것이다.
  • 따라서 아직 증가되기 전인 _monsterCount을 1 증가 후로 반영하여 while문 조건에 넣기 위해 _reserveCount를 1로 세팅하고 while문 조건을 while (_reserveCount + _monsterCount < _keepMonsterCount)로 해주는 것이다!
  • 이러면 증가되야 하는 수만큼만 ReserveSpawn 코루틴 함수가 호출이 될 수 있다.

 

 

2. 랜덤한 대기 시간 가지기

 

3. 몬스터 스폰 📜GameManager의 Spawn 호출

 

4. 스폰한 몬스터를 랜덤한 위치로 설정 (= 랜덤한 위치에 스폰)

4 - 1. 랜덤으로 뽑은 위치가 갈 수 있는 곳으로 나올 때까지 랜덤 뽑기

4 - 2. 랜덤 위치 뽑기 (Random.insideUnitSphere)

  • (0, 0, 0) 위치를 기준으로 반지름 1 의 반경 원에서 랜덤한 Vector3 값을 리턴한다.
  • 2D 게임이므로 랜덤 y값은 반영 X (이렇게 안해주면 하늘이나 땅에서 스폰될 수도 있다.)

 

 

4 - 3. 뽑은 위치가 갈 수 있는 곳인지

  • nma.CalculatePath(randPos, path) 가 True 이면 랜덤뽑기 그만 하고 break.

 

 

5. 다시 _reserveCount 1 감소

 

 

📜 GameScene

public class GameScene : BaseScene
{
    protected override void Init()
    {
        //...

        //Managers.Game.Spawn(Define.WorldObject.Monster, "Knight");
        GameObject go = new GameObject { name = "SpawningPool" };
        SpawningPool pool = go.GetOrAddComponent<SpawningPool>();
        pool.SetKeepMonsterCount(5);
    }

 

“GameScene”씬이 시작되면 “SpawningPool” 객체를 생성하고 이에 📜SpawningPool 를 바로 붙인다. 그리고 몬스터는 5 마리를 유지하도록 SetKeepMonsterCount(5) 호출.


수직으로 이동하려는 버그

📜PlayerController

	protected override void UpdateMoving()
	{
		// 몬스터가 내 사정거리보다 가까우면 공격
        //...

		// 이동
		Vector3 dir = _destPos - transform.position;
		dir.y = 0;

 

클릭 위치에 따라 y좌표도 반영될 수 있다. 목표 방향인 dir에 y까지 포함되면 플레이어가 조금 붕 뜰 수도 있기 때문에 dir.y은 반영하지 않도록 0 으로 세팅한다.

 

 


 

 

완강. 😎

 

 

 

반응형

'공부 > 인프런 - Rookiss' 카테고리의 다른 글

Part 4-1-1. 서버 OT 및 환경 설정  (1) 2023.09.03
Part 4. 게임 서버  (0) 2023.09.03
Part 3-13-8. 미니 RPG : 레벨업  (0) 2023.09.02
Part 3-13-7. 미니 RPG : Destory  (0) 2023.09.01
Part 3-13-6. 미니 RPG : 몬스터 AI  (0) 2023.09.01