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)
下图是生成后的效果图: