【Godot】生成 2D 随机地图的一种方法

Godot 3.2.4

roguelike 中生成 2D 地图非常常见,现在我也用到,所以写下代码以供参考

根节点为 PanelContainer 节点,命名为 MapPanel。在它下面是一个 ScrollContainer 节点。最后是一个 Control 节点,命名为 Container
在这里插入图片描述

在脚本里写入如下代码

## Map Panel
## 地图面板
extends PanelContainer


# 方向列表
const DirectionList = [
    Vector2.LEFT, Vector2.RIGHT, 
    Vector2.UP, Vector2.DOWN
]

# 产生下一个方向的砖块的几率
const Chance = 0.4

export var map_size = Vector2(40, 40)   # 地图大小
export var tile_size = Vector2(32, 32)  # 砖块大小
export var separate = Vector2(8, 8) # 每个 tile 分隔的距离


onready var scroll_container = ScrollContainer # 滚动条容器
onready var container =ScrollContainer/Container  # 添加 tile 的容器


# 地图 tile 位置列表
var map_tile_pos_list = {}



#==============================
# 内置方法
#==============================
func _ready() -> void:
    # 每次随机不一样
    randomize()

    # 初始化地图
    init_map()

    # tile 偏移距离
    var offset_size = tile_size + separate

    # tile 容器大小
    container.rect_min_size = offset_size * map_size + Vector2.ONE * 600

    # 添加 tile 
    var tile_pos_list = map_tile_pos_list.keys()
    var tile_list = []
    for pos in tile_pos_list:
        var button = Button.new()
        button.rect_position = pos * offset_size
        button.rect_size = tile_size
        if map_tile_pos_list[pos] == 1000:
            print(button)
        container.add_child(button)
        tile_list.push_back(button)

    # 将起始位置的 tile 设置为红色
    var first_tile = tile_list[0] as Button
    first_tile.modulate = Color.red

    # Scroll 容器滚动条的值发生改变的时候
    # 滚动条滚动到第一个 tile 的位置
    yield(scroll_container.get_h_scrollbar(), "changed")
    scroll_container.scroll_horizontal = int(first_tile.rect_position.x - 200)
    scroll_container.scroll_vertical = int(first_tile.rect_position.y - 200)



#==============================
# 自定义方法
#==============================
## 初始化地图
func init_map():
    # 起始位置在地图中间
    var start_pos = map_size / 2
    add_pos(start_pos, 1000)

    # 开始延伸向四周
    for dir in DirectionList:
        next_tile(start_pos, dir)


## 下一个瓷砖
## @pos 移动前的位置
## @dir 移动的方向
## @count 递归次数
func next_tile(pos: Vector2, dir: Vector2, count=0):
    for temp_dir in DirectionList:
        if temp_dir == dir * -1:
            # 方向不能向反方向移动
            # 当前 dir 是向左移动,即 dir 为 Vector2.LEFT
            # 那么 temp_dir 就不能向右移动,即不能为 Vector2.RIGHT
            continue

        # 随机 `Chance` 的几率添加下一个
        var rand = randf()
        if rand <= Chance:
            var temp_pos = pos + temp_dir
            add_pos(temp_pos)

            # 开始进行添加下一个位置
            if valid_next_pos(temp_pos):
                # 防止堆栈溢出,超过 1024 则错误
                if count > 1000:
                    return
                next_tile(temp_pos, temp_dir, count+1)


## 返回当前位置还能否向其他位置移动
## @pos 位置
func valid_next_pos(pos: Vector2) -> bool:
    var left = pos + Vector2.LEFT
    var right = pos + Vector2.RIGHT
    var up = pos + Vector2.UP
    var down = pos + Vector2.DOWN
    if (has_pos(left)
        && has_pos(right)
        && has_pos(up)
        && has_pos(down)
    ):
        # 所有方向已经存在,则返回 false
        return false

    return true


## 添加位置
## @pos 位置
## @type 地图 tile 类型
func add_pos(pos: Vector2, type = 1) -> void:
    if pos > Vector2.ZERO && pos <= map_size:
        map_tile_pos_list[pos] = type

## 是否已经有了这个位置
func has_pos(pos: Vector2) -> bool:
    return map_tile_pos_list.has(pos)

下图是生成后的效果图:
在这里插入图片描述

发表评论