地牢房间
互不重叠地摆放房间,再用随机游走在每一对之间挖出走廊。
上方的编辑器正在运行这个项目。改一行代码,它就会重新加载。
用到的 Godot 类
Polygon2DLine2DRect2i
代码
scripts/main.gd
extends Node
const COLUMNS: int = 52
const ROWS: int = 28
const ROOMS: int = 12
const TRIES: int = 160
const WANDER: float = 0.18
const SEED: int = 20260814
const DIRECTIONS: Array = [Vector2i.RIGHT, Vector2i.LEFT, Vector2i.DOWN, Vector2i.UP]
var rng: RandomNumberGenerator = RandomNumberGenerator.new()
var origin: Vector2
var cell: float
func _ready() -> void:
_add_background()
rng.seed = SEED
var view: Vector2 = get_viewport().get_visible_rect().size
cell = minf(view.x / COLUMNS, view.y / ROWS)
origin = (view - Vector2(COLUMNS, ROWS) * cell) * 0.5
var rooms: Array[Rect2i] = _place_rooms()
for i in rooms.size() - 1:
_add_corridor(_walk(rooms[i].get_center(), rooms[i + 1].get_center()))
for i in rooms.size():
_add_room(rooms[i], i)
print('rooms first, then a random walk digs between them, change SEED for another dungeon')
func _place_rooms() -> Array[Rect2i]:
var rooms: Array[Rect2i] = []
for attempt in TRIES:
if rooms.size() == ROOMS:
break
var size: Vector2i = Vector2i(rng.randi_range(5, 10), rng.randi_range(4, 7))
var candidate: Rect2i = Rect2i(
Vector2i(rng.randi_range(2, COLUMNS - size.x - 2), rng.randi_range(2, ROWS - size.y - 2)),
size
)
var free: bool = true
for room in rooms:
if room.grow(2).intersects(candidate):
free = false
break
if free:
rooms.append(candidate)
rooms.sort_custom(func(a: Rect2i, b: Rect2i) -> bool: return a.position.x < b.position.x)
return rooms
func _walk(from: Vector2i, to: Vector2i) -> PackedVector2Array:
var path: PackedVector2Array = PackedVector2Array()
var at: Vector2i = from
path.append(_to_point(at))
while at != to:
var step: Vector2i = Vector2i(signi(to.x - at.x), 0)
if rng.randf() < WANDER:
step = DIRECTIONS[rng.randi_range(0, DIRECTIONS.size() - 1)]
elif at.x == to.x:
step = Vector2i(0, signi(to.y - at.y))
at = (at + step).clamp(Vector2i.ONE, Vector2i(COLUMNS - 2, ROWS - 2))
path.append(_to_point(at))
return path
func _add_corridor(path: PackedVector2Array) -> void:
var corridor: Line2D = Line2D.new()
corridor.points = path
corridor.width = cell
corridor.default_color = Color('#2a2f3d')
corridor.joint_mode = Line2D.LINE_JOINT_ROUND
corridor.begin_cap_mode = Line2D.LINE_CAP_BOX
corridor.end_cap_mode = Line2D.LINE_CAP_BOX
add_child(corridor)
func _add_room(room: Rect2i, index: int) -> void:
var top_left: Vector2 = origin + Vector2(room.position) * cell
var size: Vector2 = Vector2(room.size) * cell
var floor_rect: Polygon2D = Polygon2D.new()
floor_rect.polygon = PackedVector2Array([top_left, top_left + Vector2(size.x, 0), top_left + size, top_left + Vector2(0, size.y)])
floor_rect.color = Color('#39415a')
add_child(floor_rect)
var border: Line2D = Line2D.new()
border.points = floor_rect.polygon
border.closed = true
border.width = 2.0
border.default_color = Color('#8da5f3')
add_child(border)
var label: Label = Label.new()
label.text = str(index + 1)
label.size = size
label.position = top_left
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
label.add_theme_color_override('font_color', Color('#8da5f3'))
add_child(label)
func _to_point(at: Vector2i) -> Vector2:
return origin + (Vector2(at) + Vector2(0.5, 0.5)) * cell
func _add_background() -> void:
var background: ColorRect = ColorRect.new()
background.color = Color('#12141a')
add_child(background)
background.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)