用代码生成 TileMap

图集是逐像素绘制的 Image 的 TileSet,再一格一格摆到 TileMapLayer 上。

上方的编辑器正在运行这个项目。改一行代码,它就会重新加载。

用到的 Godot 类

TileMapLayerTileSetTileSetAtlasSource

代码

scripts/main.gd
extends Node

const TILE: int = 16
const COLUMNS: int = 22
const ROWS: int = 12
const SEED: int = 20260814

const TILES: Array = [
	{name = 'water', color = '#2a3a63', grain = 0.16, limit = 0.36},
	{name = 'sand', color = '#f3d48d', grain = 0.10, limit = 0.44},
	{name = 'grass', color = '#8eef97', grain = 0.18, limit = 0.68},
	{name = 'rock', color = '#2a2f3d', grain = 0.22, limit = 2.00},
]

var rng: RandomNumberGenerator = RandomNumberGenerator.new()


func _ready() -> void:
	_add_background()

	rng.seed = SEED

	var source: TileSetAtlasSource = TileSetAtlasSource.new()
	# the texture and the region size have to be set before create_tile, the tile is validated against them
	source.texture = _make_atlas()
	source.texture_region_size = Vector2i(TILE, TILE)

	for i in TILES.size():
		source.create_tile(Vector2i(i, 0))

	var tile_set: TileSet = TileSet.new()
	tile_set.tile_size = Vector2i(TILE, TILE)

	var source_id: int = tile_set.add_source(source)

	var view: Vector2 = get_viewport().get_visible_rect().size
	var zoom: float = maxf(1.0, floorf(minf(view.x / (COLUMNS * TILE), view.y / (ROWS * TILE))))

	var layer: TileMapLayer = TileMapLayer.new()
	layer.tile_set = tile_set
	layer.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
	layer.scale = Vector2(zoom, zoom)
	layer.position = (view - Vector2(COLUMNS, ROWS) * TILE * zoom) * 0.5
	add_child(layer)

	var noise: FastNoiseLite = FastNoiseLite.new()
	noise.seed = SEED
	noise.noise_type = FastNoiseLite.TYPE_SIMPLEX
	noise.fractal_octaves = 4
	noise.frequency = 0.075

	for x in COLUMNS:
		for y in ROWS:
			layer.set_cell(Vector2i(x, y), source_id, Vector2i(_pick(noise.get_noise_2d(x, y) * 0.5 + 0.5), 0))

	_add_label('one atlas source, four 16x16 tiles drawn into an Image', Vector2(layer.position.x, maxf(16.0, layer.position.y - 38.0)))

	print('no png anywhere, the atlas is painted pixel by pixel, change SEED for another map')


func _pick(height: float) -> int:
	for i in TILES.size():
		if height < TILES[i].limit:
			return i

	return TILES.size() - 1


func _make_atlas() -> ImageTexture:
	var image: Image = Image.create(TILE * TILES.size(), TILE, false, Image.FORMAT_RGBA8)

	for i in TILES.size():
		var base: Color = Color(TILES[i].color)

		for y in TILE:
			for x in TILE:
				var shade: float = rng.randf_range(-TILES[i].grain, TILES[i].grain)

				if x == TILE - 1 or y == TILE - 1:
					shade -= 0.12

				image.set_pixel(i * TILE + x, y, base.lightened(shade) if shade > 0.0 else base.darkened(-shade))

	return ImageTexture.create_from_image(image)


func _add_label(text: String, at: Vector2) -> void:
	var label: Label = Label.new()
	label.text = text
	label.position = at
	label.add_theme_color_override('font_color', Color('#999999'))
	add_child(label)


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)

更多程序化生成示例