생성될 방의 종류별 프리팹을 가지고 있고 각각의 방이 생성될때 마다 맵들을 리스트에 담아 관리하는 RoomTemplates 스MonoBehaviour 오브젝트 스크립트
보스방의 생성 조건은 맵 생성시 가장 마지막에 생성된 방 ( 시작 방과 가장 멀리 떨어져 있는 방을 보스방으로 설정)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RoomTemplates : MonoBehaviour {
public GameObject[] bottomRooms;
public GameObject[] topRooms;
public GameObject[] leftRooms;
public GameObject[] rightRooms;
public List<GameObject> rooms;
//아래부터는 보스 생성코드
//public float waitTime;
//private bool spawnedBoss;
//public GameObject boss;
//void Update()
//{
// if (this.waitTime <= 0 && this.spawnedBoss == false)
// {
// for (int i = 0; i < this.rooms.Count; i++)
// {
// if (i == this.rooms.Count - 1)
// {
// //Instantiate(boss, rooms[i].transform.position, Quaternion.identity);
// this.spawnedBoss = true;
// }
// }
// }
// else
// {
// this.waitTime -= Time.deltaTime;
// }
//}
}
각 맵들을 4이하의 스폰포인트 ( 해당 맵이 가지고 있는 포탈의 위치와 연결되는 포인트) 를 가지고 있고 해당 포인트 들은 RigidBody2D와 Colider2D 컴포넌트를 가지는 오브젝트들이다.
해당 오브젝트의 타입( 아래 코드에서는 openingDirection 이라는 int 타입으로 임시 설정 하였다) 에 따라 포탈이 연결될수 있는 맵들을 RoomTemplates 스크립트의 오브젝트에서 가져와 자신의 위치에 생성 ( 여기선 맵의 피벗이 중앙이 아닌 관계로 약간의 로컬 포지션 조정이 있었음)
만약 RoomTemplates 스크립트의 list가 일정 갯수 이상의 맵을 가지고 있을 시 더이상 맵을 생성할수 없게 조건절을 넣었다.
혹시라도 룸 스폰 포인트가 남아있어 메모리를 점유하는 것을 고려해 일정 시간이 지나도 해당 오브젝트가 살아있다면 해당 룸 스폰 포인트를 파괴하도록 지시. *해당 옵션을 좀더 길게 줘야 할듯 싶다
혹시라도 룸이 중복 생성되는 것을 피하기 위해 Test 라는 이름의 임시 TileMap 레이어를 맵마다 추가해 두었고 (해당 타일 레이어들은 콜라이더를 가지고 있다) 만약 해당 타입의 콜라이더와 맵 스폰포인트가 부딪힐시 맵을 생성하지 못하게 파괴하도록 하였다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RoomSpawner : MonoBehaviour {
public int openingDirection;
// 1 --> need bottom door
// 2 --> need top door
// 3 --> need left door
// 4 --> need right door
private RoomTemplates templates;
private int rand;
public bool spawned = false;
private float waitTime = 4f;
void Start(){
//Destroy(this.gameObject, this.waitTime);
this.templates = GameObject.FindGameObjectWithTag("Rooms").GetComponent<RoomTemplates>();
Invoke("Spawn", 0.1f);
}
// Invoke 메서드
void Spawn(){
if(this.spawned == false && this.templates.rooms.Count < 10)
{
if(this.openingDirection == 1){
// Need to spawn a room with a BOTTOM door.
this.rand = Random.Range(0, this.templates.bottomRooms.Length);
var go = Instantiate(this.templates.bottomRooms[this.rand], this.transform.position, this.templates.bottomRooms[this.rand].transform.rotation);
go.transform.position = go.transform.position - new Vector3(6.8f, 9.3f, 0);
}
else if(openingDirection == 2){
// Need to spawn a room with a TOP door.
this.rand = Random.Range(0, templates.topRooms.Length);
var go = Instantiate(this.templates.topRooms[this.rand], this.transform.position, this.templates.topRooms[this.rand].transform.rotation);
go.transform.position = go.transform.position - new Vector3(7.2f, 9.5f, 0);
}
else if(this.openingDirection == 3){
// Need to spawn a room with a LEFT door.
this.rand = Random.Range(0, this.templates.leftRooms.Length);
var go = Instantiate(this.templates.leftRooms[this.rand], this.transform.position, this.templates.leftRooms[this.rand].transform.rotation);
go.transform.position = go.transform.position - new Vector3(7f, 9.7f, 0);
}
else if(openingDirection == 4){
// Need to spawn a room with a RIGHT door.
this.rand = Random.Range(0, this.templates.rightRooms.Length);
var go = Instantiate(this.templates.rightRooms[this.rand], this.transform.position, this.templates.rightRooms[this.rand].transform.rotation);
go.transform.position = go.transform.position - new Vector3(7f, 9.7f, 0);
}
this.spawned = true;
}
}
void OnTriggerEnter2D(Collider2D other)
{
Debug.Log("이미 자리에 맵 있음");
Debug.LogFormat("<color=green>부딪힌 오브젝트 : {0}</color>", other.gameObject.name);
Debug.LogFormat("<color=cyan>룸 스포너 오브젝트 : {0}</color>", this.gameObject.name);
if (other.gameObject.name.Contains("Test"))
{
this.spawned = true;
Destroy(this.gameObject);
}
}
}
모든 맵들은 AddRoom 이라는 이름의 스크립트를 가지고 있는데 해당 스크립트들을 자신이 스폰되었을때 RoomTemplates오브젝트를 찾아 자신을 리스트에 넣는역할을 한다. (이걸로 일정양 이상의 오브젝트가 생성되는것을 방지)
만약 시작룸이라면 4방향으로 맵이 생성되는 것을 막기위해 (이유는 고정적인 모양이 나올수도 있기 때문) 시작시 랜덤하게 4방향의 룸 스폰 포인트 오브젝트를 파괴하도록 지시하였다.
*해당 옵션을 두개의 스폰 포인트를 삭제하도록 지시하기 맵이 좀더 이쁜 모양으로 나오는 듯하여 2개의 랜덤 스폰 포인트를 삭제해주는것으로 바꾸었다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AddRoom : MonoBehaviour {
private RoomTemplates templates;
void Awake(){
this.templates = GameObject.FindGameObjectWithTag("Rooms").GetComponent<RoomTemplates>();
this.templates.rooms.Add(this.gameObject);
int ran = Random.Range(1, 3);
int ran2 = Random.Range(3, 5);
if (this.gameObject.name.Contains("EntryRoom"))
{
Debug.Log("시작룸 입니다.");
string spawnPointname = string.Format("MapSpawnpoint{0}",ran);
string spawnPointname2 = string.Format("MapSpawnpoint{0}",ran2);
string portal = string.Format("portal{0}", ran);
string portal2 = string.Format("portal{0}", ran2);
Debug.Log(spawnPointname);
Debug.Log(spawnPointname2);
Destroy(this.transform.Find(spawnPointname).gameObject);
Destroy(this.transform.Find(spawnPointname2).gameObject);
Destroy(this.transform.Find(portal).gameObject);
Destroy(this.transform.Find(portal2).gameObject);
}
}
}
현재 까지 구현된 부분은 아래와 같다.
- 맵이 중복되어 겹치지 않게 생성
- 지정된 수만큼 맵이 생성
- 맵의 가장 먼 곳에 있는 맵을 보스방 맵으로 변경
문제점 혹은 구현해야할 사항은 아래와 같다.
- 모든 맵은 최소 한개 이상의 포탈 인접한 방과 연결되어 있어야 하는데 그렇지 못한 룸들이 있다 ( 해당 룸들은 시크릿 룸으로 만들수도 있지만 현재로서는 제어가 안되는 상황이기 때문에 논외)
- 어떤 이유에서인지 맵이 1개씩 부족하게 스폰되는 문제가 있다 ( 아이작 또한 랜덤하게 1개 정도의 맵이 덜 생성될수는 있었기에 큰 문제는 아니지만 그래도 완벽한 제어를 위해서는 원인을 찾아야 한다)
- 인접한 룸들을 이어줄 포탈들을 어떤 조건으로 포탈을 생성하여 이어줄지에 대한 조건을 생각해 보아야 한다.
- 현재 생각중인 부분은 인접한 포탈이 있는 맵들은 포탈을 생성해주는 로직을 생각중이다. ( 콜라이더 사용 혹은 맵 생성시 조건을 붙여 생성등)
참고영상
https://www.youtube.com/watch?v=qAf9axsyijY&list=RDCMUC9Z1XWw1kmnvOOFsj6Bzy2g&index=3
'프로젝트 > 건즈앤 레이첼스' 카테고리의 다른 글
| [유니티 프로젝트] A* 알고리즘을 활용한 랜덤 맵생성 (1) | 2023.03.15 |
|---|---|
| [유니티 프로젝트] 이차원 배열과 A* 알고리즘을 사용한 랜덤 맵 생성 .2 (0) | 2023.03.14 |
| [유니티 프로젝트] 절차적 맵생성 알고리즘을 이용한 랜덤 맵 생성 .1 (1) | 2023.03.10 |
| [유니티 프로젝트] 타일맵 콜라이더 충돌 컴포넌트 구성과 rigidbody의 velocity 그리고 맵 제작시 레이어 설정 (1) | 2023.03.09 |
| [프로젝트] 상자 오브젝트 접근시 상자 오픈 & 깜빡이다 사라지게 하기 (0) | 2023.03.07 |