MENU

2D Roguelike随机地图

July 25, 2021 • 阅读: 2033 • 笔记&折腾

关于 2D roguelike 的随机地图。

当我们想要设计一个 roguelike 类型的游戏时,首先要考虑的是它的 ”随机体验“!比如随机地图、随机武器、随机野怪/BOSS、随机BUFF......从某种角度来说,一款 roguelike 游戏,它的精髓在于它与众不同&出色的随机性。

在端游《元气骑士》中,它的随机地图设计的非常好。玩家在一个地牢中,每次只能在一个房间里活动,在完成本房间的任务后便可进入下一个房间,玩家的视角是一个俯视图,房间的选择只有上下左右。

room

而整个场景的所有房间数以及房间布局在玩家进入该场景时便已经随机生成,它每次随机生成的地图都能让本场景中的最后一个 房间/BOSS 房间尽可能地远离角色进入当前场景时的房间。

这听起来非常有趣,但是实现并不是很难。

首先我们先确定每个场景需要随机生成的房间数,比如5~10。
然后是它的房间生成方式。在每一次生成房间后之后,都要计算出它的下一次随机生成房间的位置。

for (int i =0; i <roomNumber; i++){
    // 生成房间(对象,位置,不旋转)
    rooms.Add(Instantiate(roomPrefab, generatorPoint.position, Quaternion.identity).GetComponent<Room>());
    // 改变位置
    ChangePointPos();
}

在 2D 场景地图中,它们的地图应该是聚合状态(即紧挨在一起)的,而不是随机分布在场景中,并且 2D 地图只有上下左右四个方位,所以在生成地图的时候就必须考虑我们下一次生成的地图位置是否已经有房间了,如果有的话,这将导致房间重叠,应该避开这种情况。

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)); //检测是否已有房间
}

然后,选择 BOSS 房间或者通往下一场景的房间。在随机生成了10个房间之后,就要选择一个尽可能离出生点房间比较远的一个房间,在 2D 场景中,这样的计算非常简单,只需要 x+y 便可,但是我们必须要考虑路径问题。比如,随机生成了一个 U 型地图。

U

玩家出生点房间在绿色房间,按照距离算法当然是选择黄色房间作为场景结束房间,但是显然,红色房间才是在路径上离出生点房间最远的房间,应该选择红色房间作为结束房间而不是黄色房间。此处可使用深度遍历算法进行最终的房间选择。当然,U 型地图生成只是一个小概率事件,可以选择其他的最终房间选择方案。

房间门生成。在将场景最终房间选择好之后,就要考虑每个房间之间的通道。在 2D 房间中,它的通道只有上下左右 4 个方向,所以只需要对每个房间进行简单的”邻居“判断,生成通道数以及通道位置。

switch (newRoom.doorNumber){
    case 1:
        //...
        Instantiate(gameobject,position,rotation);
        break;
    case 2:
        //...
        Instantiate(gameobject,position,rotation);
        break;
    case 3:
        //...
        Instantiate(gameobject,position,rotation);
        break;
    case 4:
        //...
        Instantiate(gameobject,position,rotation);
        break;
}

对模型进行快速验证:

if (Input.anyKeyDown){
    // 按下任意键重新加载地图
    SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}

re

Last Modified: August 8, 2021