[TIL] Unity - 3D 러너 맵 생성

MINO·2024년 6월 20일
0
post-thumbnail

2024-06-20


무한히 생성되는 맵

최종 프로젝트 전, 마지막 팀 프로젝트로 3D 종스크롤 게임을 만들게 되었다.

그 중, 무한히 생성되는 맵 구현을 담당하였다.

레퍼런스 게임 - Arrow a Row


설계 (1)

  1. 큰 원기둥, 구 오브젝트를 설치한다.
  2. 주변 환경 오브젝트를 자식 오브젝트로 설치한다.
  3. 부모 오브젝트를 회전 시켜 무한히 맵이 생성되는 것 처럼 보이게 한다.

플레이어는 맵 오브젝트 밖에 위치하고,
맵 오브젝트를 회전시켜 맵이 계속 반복해서 생성되게 만들었다.


문제점 (1)

플레이어는 투사체를 발사하고,
몬스터는 Ray를 발사하여 플레이어를 감지해야한다.

그러나, Ray 를 곡선으로 발사할 수 없으며
투사체를 곡선 형태로 발사하기에 계산량이 너무 많아진다.


설계 (2)

  1. 맵 타일을 여러 개 프리팹으로 생성해둔다.
  2. 오브젝트 풀을 통해 타일을 생성한다.
  3. 플레이어가 앞으로 이동한다.
  4. 맵 타일이 플레이어보다 뒤에 위치한다면 반환한다.
  5. 맵 타일을 앞 쪽에 설치한다.
  6. 2~4 를 반복하여 맵이 무한히 생성된다.

문제점 (2)

하나의 맵 타일 안에 많은 오브젝트가 존재해, 맵 타일을 움직이는 것이
많은 계산을 요구한다고 생각하여
맵이 아닌 플레이어가 이동하는 방법으로 기능을 구현하였다.

그러나, 플레이어가 투사체를 발사하거나, 몬스터가 앞으로 이동할 때,
따로 계산을 해주는 작업이 필요하다고 생각하여
팀원들과 상의 후, 맵 타일을 움직이는 방식으로 수정하게 되었다.


설계 (3)

설계 (2) 와 마찬가지로, 오브젝트 풀을 사용하고,
플레이어는 고정, 맵이 뒤에서 앞으로 움직이게 하였다.


public class TileManager : MonoBehaviour
{
    public static TileManager Instance;

    public GameObject[] tilePrefabs;
    public Dictionary<int, Queue<GameObject>> TilePool;

    public float tileLength = 40.06f; // 타일 길이
    public int numberOfTiles = 5;


    private void Awake()
    {
        if (Instance == null)
            Instance = this;

        TilePool = new Dictionary<int, Queue<GameObject>>();
        CreatePool();
    }

    private void CreatePool()
    {
        for (int i = 0; i < tilePrefabs.Length; i++)
        {
            Queue<GameObject> queue = new Queue<GameObject>();
            for (int j = 0; j < 5; j++)
            {
                GameObject go = Instantiate(tilePrefabs[i], transform);
                go.SetActive(false);
                queue.Enqueue(go);
            }
            int key = tilePrefabs[i].GetComponent<Tile>().TileId;
            TilePool.Add(key, queue);
        }
    }

    public GameObject Get(int id)
    {
        return TilePool[id].Dequeue();
    }

    public void Release(int id, GameObject go)
    {
        TilePool[id].Enqueue(go);
    }


    void Start()
    {
        for (int i = 0; i < numberOfTiles; i++)
        {
            int idx = Random.Range(0, tilePrefabs.Length);
            GameObject go = Get(idx);
            go.transform.position = transform.forward * (i * tileLength);

            go.SetActive(true);
        }
    }

    public void SpawnTile()
    {
        int idx = Random.Range(0, tilePrefabs.Length);

        GameObject go = Get(idx);
        go.transform.position = transform.forward * (tileLength * 2);

        go.SetActive(true);
        Debug.Log("SpawnTile");
    }
}

// Tile.cs
public class Tile : MonoBehaviour
{
    public int TileId;
    
    private void OnEnable()
    {
        StartCoroutine(Move());
    }

    private void Update()
    {
        if(transform.position.z < -20f)
        {
            gameObject.SetActive(false);
            TileManager.Instance.Release(TileId,gameObject);
            TileManager.Instance.SpawnTile();
        }
    }

    IEnumerator Move()
    {
        while(true)
        {
            yield return null;
            transform.position -= Vector3.forward * 0.1f;
        } 
    }
}

결과물


TIL 마무리

2D 횡스크롤 맵 생성에 대한 참고 자료는 많았으나,
3D 종스크롤 맵 생성에 대한 자료는 찾기 힘들어 많은 고민을 하였다.

처음에는 오브젝트 풀도 List 로 구현하였으나,
Queue 로 구현하는 방식으로 수정하였다.

한 가지 기능을 구현하는 데, 대략 4 - 5번의 수정을 통해 완성하게 되었다.
길고 긴 한 주였다.

profile
안녕하세요 게임 개발하는 MINO 입니다.

0개의 댓글