想做一个类似以撒的肉鸽游戏,然后记录一下进度,也做个学习的总结笔记吧,目前想到的要素:随机地图生成,敌人AI,角色控制和攻击,一些动画,还有随机掉落物品,商店。

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 roomNumber;
    public Color startColor, endColor;
    private GameObject endRoom;

    [Header("位置控制")]
    public Transform generatorPoint;
    public float xOffset;
    public float yOffset;
    public LayerMask roomLayer;

    //这边以后可以考虑用自定义类,储存Room的详细数据
    public List<GameObject> rooms = new List<GameObject>();

    void Start()
    {
        for (int i = 0; i < roomNumber; i++)
        {
            rooms.Add(Instantiate(roomPrefab, generatorPoint.position, Quaternion.identity));

            //改变point位置
            ChangePointPos();
        }

        rooms[0].GetComponent<SpriteRenderer>().color = startColor;

        endRoom = rooms[0];
        foreach(var room in rooms)
        {
            //如果当前的room的向量长度比endRoom大,则将endRoom重新赋值
            //sqrMagnitude是长度的平方,要比开方magnitude快很多
            if (room.transform.position.sqrMagnitude > endRoom.transform.position.sqrMagnitude)
            {
                endRoom = room;
            }
            endRoom.GetComponent<SpriteRenderer>().color = endColor;
        }
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.anyKeyDown)
        {
            //随机键刷新新地图
            SceneManager.LoadScene(SceneManager.GetActiveScene().name);
        }
    }

    private 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));
    }
}

        大致代码就如上所示,目前只是初步完成了房间的随机生成,以及初始和结束BOSS房的计算,目前是用List<GameObject>来储存生成的房间,之后可以考虑改成二维数组或者自定义类,可以储存更多信息,比如生成的房间类型、房间的Position、以及更细致的设定(比如当前房间生成怪物位置之类的)。

        房间生成逻辑稍微简单总结一下:先生成一个起始点的初始房间,然后根据偏移量以及随机方向去寻找下一个房间,找到下一个房间生成点之后判断是否有房间,如果有的话就继续根据这个新点的上下左右继续寻找生成,如果没有的话就直接生成,这样可以避免一个房间四周都有房间时,导致无法生成新房间。

        然后初始房间设定没问题,但是结束房间的判断可能还需要改进一下,目前这个性能比较好,但是有时候可能判断的不是最远房间。


        上次说到要优化的内容,储存生成的房间改成了List<Room>,然后可以根据个人需要声明你想要用的变量,这边初步想要房间类型,以及判断房间上下左右是否有门的变量,最后四个bool值类型是用于BFS查找时,防止往来的方向查找的变量。

public enum RoomType
    {
        normalRoom,
        startRoom,
        treasureRoom,
        bossRoom,
        shop
    }
    public RoomType roomType;
    public GameObject doorLeft, doorRight, doorUp, doorDown;
    //上下左右是否有房间
    public bool roomLeft, roomRight, roomUp, roomDown;
    //判断上一个房间来源方向,用于查找步数
    public bool fromLeft = false;
    public bool fromRight = false;
    public bool fromUp = false;
    public bool fromDown = false;

        还有最远房间判断优化,这里可以用xy偏移量大小来查找,但是由于Room的X为18,y为8,所以需要将X除以2在进行查找会更好,然后标出距离原点房间的step,但是如果遇到U型地图,我们需要的最末端应该是另一端的U,但是实际偏移量最大的可能不是另一端,因此需要优化一下查找算法,这边使用了BFS算法,计算了实际需要经过最多房间的房间设置为最终房间。

private GameObject GetFarthestRoom()
    {
        GameObject lastRoom = rooms[0].gameObject;
        Room currentRoom = rooms[0];
        currentRoomQue.Enqueue(rooms[0]);

        //一层一层查找符合条件的房间
        while (currentRoomQue.Count > 0)
        {
            currentRoom = currentRoomQue.Dequeue();

            FindNextRoom(currentRoom.fromUp, currentRoom.roomUp, currentRoom.transform.position, Direction.up);
            FindNextRoom(currentRoom.fromDown, currentRoom.roomDown, currentRoom.transform.position, Direction.down);
            FindNextRoom(currentRoom.fromLeft, currentRoom.roomLeft, currentRoom.transform.position, Direction.left);
            FindNextRoom(currentRoom.fromRight, currentRoom.roomRight, currentRoom.transform.position, Direction.right);
        }
        lastRoom = currentRoom.gameObject;

        return lastRoom;        
    }

    private void FindNextRoom(bool fromBool, bool dirBool, Vector3 currentPos, Direction dir)
    {
        Vector3 offset = new Vector3(0, 0, 0);
        switch (dir)
        {
            case Direction.up:
                offset = new Vector3(0, yOffset, 0);
                break;
            case Direction.down:
                offset = new Vector3(0, -yOffset, 0);
                break;
            case Direction.left:
                offset = new Vector3(-xOffset, 0, 0);
                break;
            case Direction.right:
                offset = new Vector3(xOffset, 0, 0);
                break;
        }
        //方向上有房间时,且排除上一个房间
        if (dirBool && !fromBool)
        {
            for (int i = 0; i < rooms.Count; i++)
            {
                //找到下一个的房间
                if (rooms[i].transform.position == currentPos + offset)
                {
                    currentRoomQue.Enqueue(rooms[i]);
                    switch (dir)
                    {
                        case Direction.up:
                            rooms[i].fromDown = true;
                            break;
                        case Direction.down:
                            rooms[i].fromUp = true;
                            break;
                        case Direction.left:
                            rooms[i].fromRight = true;
                            break;
                        case Direction.right:
                            rooms[i].fromLeft = true;
                            break;
                        default:
                            break;
                    }

                    break;
                }
            }
        }
    }

        然后关于BOSS房我想的游戏逻辑是:最终房间再上下左右生成一个新房间,作为最后的BOSS房,这样BOSS房就可以保证只有一个通路通向它。

        这边不复用之前的生成房间的逻辑有两个理由,第一是因为最终房间不一定是rooms列表中的最后一个,因此此时的generatorPoint还在最后生成的那个房间处,此时如果将generatorPoint=endroom.transform的话,再方法中更改generatorPoint时,endroom.transform也会随之更改,因为这是引用类型,他们都引用了内存堆上的地址,需要使用深拷贝来解决。但还有第二个问题,就是如果新生成的房间方向上有房间,那他会从此处再寻找上下左右,此时新生成的房间就会离最终房间有>2的房间距离了,因此新写一个单独生成单个房间的方法,如果以后还要加隐藏房间的话可以用他来实现。

private GameObject CreateOneWayRoom(Room currentRoom)
    {
        Vector3 nextRoom;
        if (!currentRoom.roomRight)
        {
            nextRoom = new Vector3(currentRoom.transform.position.x + xOffset, currentRoom.transform.position.y, currentRoom.transform.position.z);
            rooms.Add(Instantiate(roomPrefab, nextRoom, Quaternion.identity).GetComponent<Room>());
            currentRoom.roomRight = true;
            rooms[rooms.Count - 1].roomLeft = true;
        }
        else if (!currentRoom.roomLeft)
        {
            nextRoom = new Vector3(currentRoom.transform.position.x - xOffset, currentRoom.transform.position.y, currentRoom.transform.position.z);
            rooms.Add(Instantiate(roomPrefab, nextRoom, Quaternion.identity).GetComponent<Room>());
            currentRoom.roomLeft = true;
            rooms[rooms.Count - 1].roomRight = true;
        }
        else if (!currentRoom.roomDown)
        {
            nextRoom = new Vector3(currentRoom.transform.position.x, currentRoom.transform.position.y - yOffset, currentRoom.transform.position.z);
            rooms.Add(Instantiate(roomPrefab, nextRoom, Quaternion.identity).GetComponent<Room>());
            currentRoom.roomDown = true;
            rooms[rooms.Count - 1].roomUp = true;
        }
        else if (!currentRoom.roomUp)
        {
            nextRoom = new Vector3(currentRoom.transform.position.x, currentRoom.transform.position.y + yOffset, currentRoom.transform.position.z);
            rooms.Add(Instantiate(roomPrefab, nextRoom, Quaternion.identity).GetComponent<Room>());
            currentRoom.roomUp = true;
            rooms[rooms.Count - 1].roomDown = true;
        }
        return rooms[rooms.Count - 1].gameObject;
    }

         然后宝箱房也可以按照这个逻辑生成,但是传过来的currentRoom需要判断一下是否有至少一个的方向上没有房间 ,Boss房不用考虑最终房间四个方向都占满的情况,是因为既然是最远的房间,那不可能四个方向都有房间。可以在Room类型中提供一个方法获取本房间一共有多少门。

        我还设置了商店,商店的生成逻辑是直接从rooms列表中随机一个房间,只要他是normalRoom的房间类型,就将它修改成shop类型,这样避免覆盖其他的特殊类型房间。

        按照目前的生成逻辑的话,三种特殊类型房间生成顺序可以随意,因为他们互相不干扰,即使宝箱房和BOSS房都选中了最后一个房间衍生,因为最远房间的特性至少会让他空出两个方向,因此也没有大碍。

        随机生成几个:(红色为BOSS房,绿色为起点,黄色为宝箱房,蓝色为商店)

unity ongui显示分数 unity dem_unity ongui显示分数

 

unity ongui显示分数 unity dem_unity_02

unity ongui显示分数 unity dem_unity_03

unity ongui显示分数 unity dem_unity_04