Roguelike随机地下城
设置门&优化最终房间的选择
一、设置门&优化最终房间的选择
首先我们先添加四个方向的门。
点击Prefab,然后我们建立一个方形暂时代替我们的门。
右键|creat|2D|Sprites|Square。
然后拖入我们的Prefab中复制三个,放置到如图的位置。
接下来建立一个Room脚本挂载到Prefab上。
首先我们先声明这四个GameObject。
public GameObject doorLeft,doorRight,doorUp,doorDown;
回到Unity对齐进行赋值。
这里将会用到Sorting Layer,他表示我们的显示层,为了我们的门可以显示出来,我们这里建立一个Room层,同时把Ground和Player层建立。
接下来我们将判断这个房间上下左右是否有门,我们要生成四个布尔值。
public bool roomLeft,roomRight, roomUp, roomDown;
然后我们在之后需要考虑它们是否显示出来,所以我们在Start方法中定义一下显示的方法。
void Start()
{
doorLeft.SetActive(roomLeft);
doorRight.SetActive(roomRight);
doorUp.SetActive(roomUp);
doorDown.SetActive(roomDown);
}
我们会判断当前的房间上下左右是否有房间,有房间就显示门,没有就不显示。
接下来我们判断上下左右是否有房间。
为了获得每一个房间上面的变量,将我们一开始设置的rooms的List的类型改成Room,下面的地方并做适当的更改。
public List<Room> rooms = new List<Room>();
void Start()
{
for (int i = 0; i < rooNumber; i++)
{
rooms.Add(Instantiate(roomPrefab, generatorPoint.position, Quaternion.identity).GetComponent<Room>());
//改变point位置
ChangePointPos();
}
rooms[0].GetComponent<SpriteRenderer>().color = startColor;
endRoom = rooms[0].gameObject;
foreach (var room in rooms)
{
if (room.transform.position.sqrMagnitude > endRoom.transform.position.sqrMagnitude)
{
endRoom = room.gameObject;
}
}
endRoom.GetComponent<SpriteRenderer>().color = endColor;
}
接下来我们要设置一个方法,来判断上下左右是否有房间,并对Room进行赋值。
方法中需要加入两个参数,一个用于识别Room,一个用于获得房间的位置。
public void SetupRoom(Room newRoom,Vector3 roomPosition)
{
newRoom.roomUp = Physics2D.OverlapCircle(roomPosition + new Vector3(0, yOffset, 0), 0.2f, roomLayer);
newRoom.roomDown = Physics2D.OverlapCircle(roomPosition + new Vector3(0, -yOffset, 0), 0.2f, roomLayer);
newRoom.roomLeft = Physics2D.OverlapCircle(roomPosition + new Vector3(-xOffset, 0, 0), 0.2f, roomLayer);
newRoom.roomRight = Physics2D.OverlapCircle(roomPosition + new Vector3(xOffset, 0, 0), 0.2f, roomLayer);
}
调用这个方法,可以先将之前找到最后一个房间的方法注释掉。
void Start()
{
for (int i = 0; i < rooNumber; i++)
{
rooms.Add(Instantiate(roomPrefab, generatorPoint.position, Quaternion.identity).GetComponent<Room>());
//改变point位置
ChangePointPos();
}
rooms[0].GetComponent<SpriteRenderer>().color = startColor;
endRoom = rooms[0].gameObject;
foreach (var room in rooms)
{
//if (room.transform.position.sqrMagnitude > endRoom.transform.position.sqrMagnitude)
//{
//endRoom = room.gameObject;
//}
SetupRoom(room,room.transform.position);
}
endRoom.GetComponent<SpriteRenderer>().color = endColor;
}
这样我们的门就设置好了。
二、优化最终房间的选择
我们要选择离我们出生点最远的那个房间,所以接下来我添加一个Text方便显示,每一个房间到我出生点房间的网格的步数。
首先为我们的Prefab建立一个画布Canvas在建立一个Text,把文字改为0。
设置好之后,我们要在脚本中获得这个Text,并且更改他的数值。
这里我们在Room脚本中新建一个方法。
public void UpdateRoom()
{
}
我们的步数怎么计算呢?
可以通过我们当前房间的坐标,x、y值除以我们的x、y的偏移量就可以得到步数!
我们要获得这个数值,所以一开始先定义一个变量。
因为我们的坐标可能是负数,所以我们要用到数学方法中的绝对值。
这里我为了方便直接除以了固定的值。
public int stepToStart;
public void UpdateRoom()
{
stepToStart = (int)(Mathf.Abs(transform.position.x / 18) + Mathf.Abs(transform.position.y / 9));
}
然后我们定义一个变量来获得我们的文本。
然后让这个文本显示我们的stepToStart。
public Text text;
public void UpdateRoom()
{
stepToStart = (int)(Mathf.Abs(transform.position.x / 18) + Mathf.Abs(transform.position.y / 9));
text.text = stepToStart.ToString();
}
然后我们这个方法要在RoomGenerator脚本中的SetupRoom方法中调用。
public void SetupRoom(Room newRoom,Vector3 roomPosition)
{
newRoom.roomUp = Physics2D.OverlapCircle(roomPosition + new Vector3(0, yOffset, 0), 0.2f, roomLayer);
newRoom.roomDown = Physics2D.OverlapCircle(roomPosition + new Vector3(0, -yOffset, 0), 0.2f, roomLayer);
newRoom.roomLeft = Physics2D.OverlapCircle(roomPosition + new Vector3(-xOffset, 0, 0), 0.2f, roomLayer);
newRoom.roomRight = Physics2D.OverlapCircle(roomPosition + new Vector3(xOffset, 0, 0), 0.2f, roomLayer);
newRoom.UpdateRoom();
}
接下来我们怎么设置我们的最终房间?
我们的思路是找到步数最大的和次大的房间,找到只有一个门的房间,随机选择一个当作最终房间,如果没有只有一个门的房间,那么我们就随机选择一个步数最大的房间当作最终房间。
接下来要设计三个列表,一个用来存放步数最大的房间,一个存放步数次大的房间,一个存放两种房间只有一个门的房间。
首先回到Room脚本,首先我们先找到一个房间有几个门。
我们先定义一个变量,用来记录门的数量。
然后我们有四个布尔值,每有一个布尔值为true,我们的数量就加1。
public int doorNumber;
public void UpdateRoom()
{
stepToStart = (int)(Mathf.Abs(transform.position.x / 18) + Mathf.Abs(transform.position.y / 9));
text.text = stepToStart.ToString();
if (roomUp)
doorNumber++;
if (roomDown)
doorNumber++;
if (roomLeft)
doorNumber++;
if (roomRight)
doorNumber++;
}
接下来回到RoomGenerator脚本中定义三个数值。
List<GameObject> farRooms = new List<GameObject>();
List<GameObject> lessFarRooms = new List<GameObject>();
List<GameObject> oneWayRooms = new List<GameObject>();
然后我们再定义一个变量,用来表示步数最大的那个数字。
public int maxStep;
新定义一个方法,用来找到最终的房间。
首先我们用一个for循环,找到所有房间里面步数最大的房间,把他加入farRooms数组中,把次大的房间放入lessFarRooms数组中。
//获得最大数值
for (int i = 0; i < rooms.Count; i++)
{
if (rooms[i].stepToStart > maxStep)
maxStep = rooms[i].stepToStart;
}
//获得最大数值房间和次大数值房间
foreach (var room in rooms)
{
if (room.stepToStart == maxStep)
farRooms.Add(room.gameObject);
if(room.stepToStart == maxStep-1)
lessFarRooms.Add(room.gameObject);
}
接下来判断数组中有没有只有一个门的房间。
for (int i = 0; i < farRooms.Count; i++)
{
if (farRooms[i].GetComponent<Room>().doorNumber == 1)
oneWayRooms.Add(farRooms[i]);
}
for (int i = 0; i < lessFarRooms.Count; i++)
{
if (lessFarRooms[i].GetComponent<Room>().doorNumber == 1)
oneWayRooms.Add(lessFarRooms[i]);
}
接下来如果两个oneWayRooms数组中有数值,就随机在这个数组中随机取一个数值,作为对应的最终房间,如果数值为0,则在farRooms数值中随机取一个数值,作为对应的最终房间。
if (oneWayRooms.Count != 0)
{
endRoom = oneWayRooms[Random.Range(0, oneWayRooms.Count)];
}
else
{
endRoom = farRooms[Random.Range(0, farRooms.Count)];
}
最后在上面调用就可以了。
void Start()
{
for (int i = 0; i < rooNumber; i++)
{
rooms.Add(Instantiate(roomPrefab, generatorPoint.position, Quaternion.identity).GetComponent<Room>());
//改变point位置
ChangePointPos();
}
rooms[0].GetComponent<SpriteRenderer>().color = startColor;
endRoom = rooms[0].gameObject;
foreach (var room in rooms)
{
//if (room.transform.position.sqrMagnitude > endRoom.transform.position.sqrMagnitude)
//{
// endRoom = room.gameObject;
//}
SetupRoom(room,room.transform.position);
}
FindEndRoom();
endRoom.GetComponent<SpriteRenderer>().color = endColor;
}
最后运行一下,效果如下。
源码
现阶段我们的完整代码如下:
RoomGenerator脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class RoomGenerator : MonoBehaviour
{
public enum Direction {
up,down,left,right };
public Direction direction;
[Header("房间信息")]
public GameObject roomPrefab;
public int rooNumber;
public Color startColor,endColor;
private GameObject endRoom;
[Header("位置控制")]
public Transform generatorPoint;
public float xOffset;
public float yOffset;
public LayerMask roomLayer;
public int maxStep;
public List<Room> rooms = new List<Room>();
List<GameObject> farRooms = new List<GameObject>();
List<GameObject> lessFarRooms = new List<GameObject>();
List<GameObject> oneWayRooms = new List<GameObject>();
// Start is called before the first frame update
void Start()
{
for (int i = 0; i < rooNumber; i++)
{
rooms.Add(Instantiate(roomPrefab, generatorPoint.position, Quaternion.identity).GetComponent<Room>());
//改变point位置
ChangePointPos();
}
rooms[0].GetComponent<SpriteRenderer>().color = startColor;
endRoom = rooms[0].gameObject;
foreach (var room in rooms)
{
//if (room.transform.position.sqrMagnitude > endRoom.transform.position.sqrMagnitude)
//{
// endRoom = room.gameObject;
//}
SetupRoom(room,room.transform.position);
}
FindEndRoom();
endRoom.GetComponent<SpriteRenderer>().color = endColor;
}
// Update is called once per frame
void Update()
{
if (Input.anyKeyDown)
{
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
}
public void ChangePointPos()
{
do
{
direction = (Direction)Random.Range(0, 4);
switch (direction)
{
case Direction.up:
generatorPoint.position += new Vector3(0, yOffset, 0);
break;
case Direction.down:
generatorPoint.position += new Vector3(0, -yOffset, 0);
break;
case Direction.left:
generatorPoint.position += new Vector3(-xOffset, 0, 0);
break;
case Direction.right:
generatorPoint.position += new Vector3(xOffset, 0, 0);
break;
}
}while (Physics2D.OverlapCircle(generatorPoint.position,0.2f,roomLayer));
}
public void SetupRoom(Room newRoom,Vector3 roomPosition)
{
newRoom.roomUp = Physics2D.OverlapCircle(roomPosition + new Vector3(0, yOffset, 0), 0.2f, roomLayer);
newRoom.roomDown = Physics2D.OverlapCircle(roomPosition + new Vector3(0, -yOffset, 0), 0.2f, roomLayer);
newRoom.roomLeft = Physics2D.OverlapCircle(roomPosition + new Vector3(-xOffset, 0, 0), 0.2f, roomLayer);
newRoom.roomRight = Physics2D.OverlapCircle(roomPosition + new Vector3(xOffset, 0, 0), 0.2f, roomLayer);
newRoom.UpdateRoom();
}
public void FindEndRoom()
{
//获得最大数值
for (int i = 0; i < rooms.Count; i++)
{
if (rooms[i].stepToStart > maxStep)
maxStep = rooms[i].stepToStart;
}
//获得最大数值房间和次大数值房间
foreach (var room in rooms)
{
if (room.stepToStart == maxStep)
farRooms.Add(room.gameObject);
if(room.stepToStart == maxStep-1)
lessFarRooms.Add(room.gameObject);
}
for (int i = 0; i < farRooms.Count; i++)
{
if (farRooms[i].GetComponent<Room>().doorNumber == 1)
oneWayRooms.Add(farRooms[i]);
}
for (int i = 0; i < lessFarRooms.Count; i++)
{
if (lessFarRooms[i].GetComponent<Room>().doorNumber == 1)
oneWayRooms.Add(lessFarRooms[i]);
}
if (oneWayRooms.Count != 0)
{
endRoom = oneWayRooms[Random.Range(0, oneWayRooms.Count)];
}
else
{
endRoom = farRooms[Random.Range(0, farRooms.Count)];
}
}
}
Room脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Room : MonoBehaviour
{
public GameObject doorLeft,doorRight,doorUp,doorDown;
public bool roomLeft,roomRight, roomUp, roomDown;
public Text text;
public int stepToStart;
public int doorNumber;
// Start is called before the first frame update
void Start()
{
doorLeft.SetActive(roomLeft);
doorRight.SetActive(roomRight);
doorUp.SetActive(roomUp);
doorDown.SetActive(roomDown);
}
// Update is called once per frame
public void UpdateRoom()
{
stepToStart = (int)(Mathf.Abs(transform.position.x / 18) + Mathf.Abs(transform.position.y / 9));
text.text = stepToStart.ToString();
if (roomUp)
doorNumber++;
if (roomDown)
doorNumber++;
if (roomLeft)
doorNumber++;
if (roomRight)
doorNumber++;
}
}