Roguelike随机地下城 | 二、设置门和优化最终房间的选择

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++;
    }
}

猜你喜欢

转载自blog.csdn.net/m0_64058685/article/details/124762874