0. 들어가기 전에
이번엔 프리팹으로 만든 맵을 일정 시간마다 게임에 생성하도록 했다. 그리고 기존에 만들어놨던 ScrollingBackground 스크립트를 부착해서 맵이 이동하도록 했다.
1. 프리팹
일단 맵 프리팹을 4개 만들어놨다. 지금은 원하는 기능을 구현하기 위해 맵을 대충 만들었는데 다음에 맵 종류도 더 늘릴 것이다. 일단 Grid 에 4개의 프리팹을 놓으면 아래 사진처럼 보인다.
맵에 장애물과, 몬스터가 있는 경우가 있는데 이는 맵의 자식으로 넣어놓은 것이다.
1. 인스펙터 창
맵의 인스펙터 창의 상태는 다음 사진과 같다. Scrolling Background 스크립트를 부착해서 맵이 이동할 수 있도록 했다.

2. 게임 오브젝트
이번에 새로 만든 게임 오브젝트는 MapSpawner 이다. Grid 의 자식으로 넣어놨다.
1. 인스펙터 창
맵 스포너의 Transform 을 보면 위치가 (7, 0, 0) 인 걸 확인 할 수 있다. 맵을 생성하면 MapSpawner 에 자식으로 들어가는데 자식은 저절로 MapSpawner 의 위치를 반영해서 게임 화면 속에 위치한다.
Map 이 생성될 때마다 시작 지점을 직접 설정해주기 보다는 MapSpawner 에 자식으로 들어가도록 하는게 훨씬 편해서 이렇게 했다.
MapSpawner 는 스크립트로 MapSpawner 를 가지는데 맵 프리팹 변수로 이번에 만든 맵 프리팹들을 넣었다. 이제 이 프리팹으로 pool 을 만들어서 사용한다.

3. 스크립트
이번에 새로 만든 스크립트는 MapSpawner 와 MapLoop 이다.
3.1 MapSpawner 스크립트
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Unity.VisualScripting;
using UnityEngine;
public class MapSpawner : MonoBehaviour
{
[Header("Map Contoller")]
public GameObject[] mapPrefabs;
public List<GameObject>[] pool;
public float curTime = 0;
public float targetTime = 0.5f; // targetTime 마다 맵 생성
public int mapCount;
private Coroutine mapCoroutine;
private void Awake()
{
mapCount = mapPrefabs.Length; // 프리팹 개수
pool = new List<GameObject>[mapCount]; // 배열 만들기
for (int i=0; i<mapCount; i++)
{
pool[i] = new List<GameObject>(); // 리스트 새로 만들기
}
}
private void Start()
{
mapCoroutine = StartCoroutine(SpawnMap()); // 맵 생성 코루틴 시작
}
private IEnumerator SpawnMap()
{
while (true)
{
int mapIdx = Random.Range(0, mapCount);
GameObject select = null;
foreach (GameObject map in pool[mapIdx])
{
// 만약 놀고 있는 맵 게임 오브젝트를 발견하면 그거 활성화
if (map.activeSelf == false)
{
select = map;
map.SetActive(true); // 맵 활성화
break;
}
}
// 발견 못 하면 새로 생성
if (select == null)
{
select = Instantiate(mapPrefabs[mapIdx], transform);
pool[mapIdx].Add(select); // 새로 생성한 게임 오브젝트를 풀에 넣기
}
yield return new WaitForSeconds(targetTime); // targetTime 만큼 기다리기
}
}
}
3.2 MapSpawner 스크립트 설명
1. 변수
선언한 변수는 다음과 같다.
미리 만들어놓은 맵 프리팹을 저장하기 위한 mapPrefabs 변수와 생성된 맵을 관리하기 위한 pool 변수를 선언했다.
맵을 일정 시간마다 생성하기 위해서 curTime, targetTime 변수를 선언했다. 맵을 생성하는 로직을 코루틴으로 만들었는데 코루틴을 시작하고 이를 저장해놓기 위해 mapCoroutine 변수를 선언했다.
[Header("Map Contoller")]
public GameObject[] mapPrefabs;
public List<GameObject>[] pool;
public float curTime = 0;
public float targetTime = 0.5f; // targetTime 마다 맵 생성
public int mapCount;
private Coroutine mapCoroutine;
2. Awake()
mapCount 에 현재 프리팹의 개수를 넣는다. 이 값으로 배열의 크기를 설정했다. List 에 요소를 넣기 위해서는 무조건 List 가 만들어져 있어야 하기 때문에 반복문을 돌면서 하나하나 만들어줬다.
private void Awake()
{
mapCount = mapPrefabs.Length; // 프리팹 개수
pool = new List<GameObject>[mapCount]; // 배열 만들기
for (int i=0; i<mapCount; i++)
{
pool[i] = new List<GameObject>(); // 리스트 새로 만들기
}
}
3. Start()
맵 생성 코루틴을 시작하고 변수에 저장했다. 나중에 코루틴을 직접 종료시킬 때 이용하기 위함이다.
private void Start()
{
mapCoroutine = StartCoroutine(SpawnMap()); // 맵 생성 코루틴 시작
}
4. SpawnMap()
맵 생성 코루틴이다. 랜덤으로 받은 값으로 해당 맵을 생성하도록 했다.
풀을 돌면서 놀고 있는 맵이 있으면 활성화시키도록 했고, 없다면 해당 맵을 새로 생성하도록 했다. 새로 생성한 맵은 pool 에 Add 해서 추가했다.
targetTime 동안 기다리도록 해서 맵이 일정 시간마다 생성되도록 했다.
private IEnumerator SpawnMap()
{
while (true)
{
int mapIdx = Random.Range(0, mapCount);
GameObject select = null;
foreach (GameObject map in pool[mapIdx])
{
// 만약 놀고 있는 맵 게임 오브젝트를 발견하면 그거 활성화
if (map.activeSelf == false)
{
select = map;
map.SetActive(true); // 맵 활성화
break;
}
}
// 발견 못 하면 새로 생성
if (select == null)
{
select = Instantiate(mapPrefabs[mapIdx], transform);
pool[mapIdx].Add(select); // 새로 생성한 게임 오브젝트를 풀에 넣기
}
yield return new WaitForSeconds(targetTime); // targetTime 만큼 기다리기
}
}
3.3 MapLoop 스크립트
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Tilemaps;
public class MapController : MonoBehaviour
{
[Header("Map Info")]
public float curTime = 0;
public float targetTime = 2f; // 활성화된지 2초가 지나면 활성화 끄기
private void OnEnable()
{
// 활성화 되면 자동으로 호출됨
ResetMap();
}
private void Update()
{
curTime += Time.deltaTime;
if (curTime >= targetTime)
gameObject.SetActive(false); // 비활성화!
}
public void ResetMap()
{
curTime = 0;
transform.position = Vector3.zero; // (0, 0, 0) 로 위치 설정
}
}
3.4 MapLoop 스크립트 설명
1. 변수
선언한 변수는 다음과 같다. 생성된 맵이 카메라를 벗어났는데도 계속해서 활성화 된 상태로 있으면 안 되기 때문에 일정 시간이 지나면 비활성화 하도록 했다.
targetTime 변수를 만들어 놓고 이 시간이 지나면 자기 자신을 비활성화 하도록 했다.
[Header("Map Info")]
public float curTime = 0;
public float targetTime = 2f; // 활성화된지 2초가 지나면 활성화 끄기
2. OnEnable()
맵이 활성화 될 때 자동으로 호출되는 메서드이다. 활성화 될 때 맵의 상태를 초기화할 수 있도록 ResetMap 메서드를 호출했다.
private void OnEnable()
{
// 활성화 되면 자동으로 호출됨
ResetMap();
}
3. Update()
curTime 이 targetTime 보다 크거나 같아지면 스스로를 비활성화 하도록 했다.
private void Update()
{
curTime += Time.deltaTime;
if (curTime >= targetTime)
gameObject.SetActive(false); // 비활성화!
}
4. ResetMap()
맵의 상태를 초기화 하는 메서드이다.
public void ResetMap()
{
curTime = 0;
transform.position = Vector3.zero; // (0, 0, 0) 로 위치 설정
}
4. 결과물
맵이 일정 시간마다 생성되는 것을 확인할 수 있다. 그리고 이제 맵이 이동한다.