用代码生成 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)