godot-tilemap-mastery

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

TileMap Mastery

TileMap精通指南

TileMapLayer grids, TileSet atlases, terrain autotiling, and custom data define efficient 2D level systems.
TileMapLayer网格、TileSet图集、地形自动铺砖及自定义数据构成了高效的2D关卡系统。

Available Scripts

可用脚本

tilemap_data_manager.gd

tilemap_data_manager.gd

Expert TileMap serialization and chunking manager for large worlds.
适用于大型世界的专业TileMap序列化与分块管理器。

NEVER Do in TileMaps

TileMap使用禁忌

  • NEVER use set_cell() in loops without batching — 1000 tiles ×
    set_cell()
    = 1000 individual function calls = slow. Use
    set_cells_terrain_connect()
    for bulk OR cache changes, apply once.
  • NEVER forget source_id parameter
    set_cell(pos, atlas_coords)
    without source_id? Wrong overload = crash OR silent failure. Use
    set_cell(pos, source_id, atlas_coords)
    .
  • NEVER mix tile coordinates with world coordinates
    set_cell(mouse_position)
    without
    local_to_map()
    ? Wrong grid position. ALWAYS convert:
    local_to_map(global_pos)
    .
  • NEVER skip terrain set configuration — Manual tile assignment for organic shapes? 100+ tiles for grass patch. Use
    set_cells_terrain_connect()
    with terrain sets for autotiling.
  • NEVER use TileMap for dynamic entities — Enemies/pickups as tiles? No signals, physics, scripts. Use Node2D/CharacterBody2D, reserve TileMap for static/destructible geometry.
  • NEVER query get_cell_tile_data() in _physics_process — Every frame tile data lookup? Performance tank. Cache tile data in dictionary:
    tile_cache[pos] = get_cell_tile_data(pos)
    .

  • 绝对不要在循环中无批处理调用set_cell() — 1000个瓦片 ×
    set_cell()
    = 1000次独立函数调用 = 性能低下。批量操作请使用
    set_cells_terrain_connect()
    ,或先缓存修改再一次性应用。
  • 绝对不要遗漏source_id参数 — 调用
    set_cell(pos, atlas_coords)
    却不带source_id?错误的重载会导致崩溃或静默失败。请使用
    set_cell(pos, source_id, atlas_coords)
  • 绝对不要混淆瓦片坐标与世界坐标 — 直接用
    set_cell(mouse_position)
    却不调用
    local_to_map()
    ?会得到错误的网格位置。务必转换坐标:
    local_to_map(global_pos)
  • 绝对不要跳过地形集配置 — 手动为有机形状分配瓦片?一片草地要选100+个瓦片。请结合地形集使用
    set_cells_terrain_connect()
    实现自动铺砖。
  • 绝对不要用TileMap承载动态实体 — 把敌人/道具设为瓦片?这样无法使用信号、物理系统和脚本。请使用Node2D/CharacterBody2D,TileMap仅用于静态或可破坏几何体。
  • 绝对不要在_physics_process中调用get_cell_tile_data() — 每帧查询瓦片数据?会导致性能暴跌。请把瓦片数据缓存到字典中:
    tile_cache[pos] = get_cell_tile_data(pos)

Step 1: Create TileSet Resource

步骤1:创建TileSet资源

  1. Create a
    TileMapLayer
    node
  2. In Inspector: TileSet → New TileSet
  3. Click TileSet to open bottom TileSet editor
  1. 创建
    TileMapLayer
    节点
  2. 在检查器中:TileSet → 新建TileSet
  3. 点击TileSet打开底部的TileSet编辑器

Step 2: Add Tile Atlas

步骤2:添加瓦片图集

  1. In TileSet editor: + → Atlas
  2. Select your tile sheet texture
  3. Configure grid size (e.g., 16x16 pixels per tile)
  1. 在TileSet编辑器中:+ → 图集
  2. 选择你的瓦片集纹理
  3. 配置网格大小(例如:16x16像素/瓦片)

Step 3: Add Physics, Collision, Navigation

步骤3:添加物理、碰撞与导航

gdscript
undefined
gdscript
undefined

Each tile can have:

每个瓦片可包含:

- Physics Layer: CollisionShape2D for each tile

- 物理层:为每个瓦片添加CollisionShape2D

- Terrain: Auto-tiling rules

- 地形:自动铺砖规则

- Custom Data: Arbitrary properties

- 自定义数据:任意属性


**Add collision to tiles:**
1. Select tile in TileSet editor
2. Switch to "Physics" tab
3. Draw collision polygon

**为瓦片添加碰撞:**
1. 在TileSet编辑器中选择瓦片
2. 切换到「物理」标签页
3. 绘制碰撞多边形

Using TileMapLayer

使用TileMapLayer

Basic Tilemap Setup

基础TileMap设置

gdscript
extends TileMapLayer

func _ready() -> void:
    # Set tile at grid coordinates (x, y)
    set_cell(Vector2i(0, 0), 0, Vector2i(0, 0))  # source_id, atlas_coords
    
    # Get tile at coordinates
    var atlas_coords := get_cell_atlas_coords(Vector2i(0, 0))
    
    # Clear tile
    erase_cell(Vector2i(0, 0))
gdscript
extends TileMapLayer

func _ready() -> void:
    # 在网格坐标(x, y)处设置瓦片
    set_cell(Vector2i(0, 0), 0, Vector2i(0, 0))  # source_id, atlas_coords
    
    # 获取指定坐标的瓦片
    var atlas_coords := get_cell_atlas_coords(Vector2i(0, 0))
    
    # 清除瓦片
    erase_cell(Vector2i(0, 0))

Runtime Tile Placement

运行时瓦片放置

gdscript
extends TileMapLayer

func _input(event: InputEvent) -> void:
    if event is InputEventMouseButton and event.pressed:
        var global_pos := get_global_mouse_position()
        var tile_pos := local_to_map(global_pos)
        
        # Place grass tile (assuming source_id=0, atlas=(0,0))
        set_cell(tile_pos, 0, Vector2i(0, 0))
gdscript
extends TileMapLayer

func _input(event: InputEvent) -> void:
    if event is InputEventMouseButton and event.pressed:
        var global_pos := get_global_mouse_position()
        var tile_pos := local_to_map(global_pos)
        
        # 放置草地瓦片(假设source_id=0,atlas坐标为(0,0))
        set_cell(tile_pos, 0, Vector2i(0, 0))

Flood Fill Pattern

洪水填充模式

gdscript
func flood_fill(start_pos: Vector2i, tile_source: int, atlas_coords: Vector2i) -> void:
    var cells_to_fill: Array[Vector2i] = [start_pos]
    var original_tile := get_cell_atlas_coords(start_pos)
    
    while cells_to_fill.size() > 0:
        var current := cells_to_fill.pop_back()
        
        if get_cell_atlas_coords(current) != original_tile:
            continue
        
        set_cell(current, tile_source, atlas_coords)
        
        # Add neighbors
        for dir in [Vector2i.UP, Vector2i.DOWN, Vector2i.LEFT, Vector2i.RIGHT]:
            cells_to_fill.append(current + dir)
gdscript
func flood_fill(start_pos: Vector2i, tile_source: int, atlas_coords: Vector2i) -> void:
    var cells_to_fill: Array[Vector2i] = [start_pos]
    var original_tile := get_cell_atlas_coords(start_pos)
    
    while cells_to_fill.size() > 0:
        var current := cells_to_fill.pop_back()
        
        if get_cell_atlas_coords(current) != original_tile:
            continue
        
        set_cell(current, tile_source, atlas_coords)
        
        # 添加相邻瓦片
        for dir in [Vector2i.UP, Vector2i.DOWN, Vector2i.LEFT, Vector2i.RIGHT]:
            cells_to_fill.append(current + dir)

Terrain Auto-Tiling

地形自动铺砖

Setup Terrain Set

设置地形集

  1. In TileSet editor: Terrains tab
  2. Add Terrain Set (e.g., "Ground")
  3. Add Terrain (e.g., "Grass", "Dirt")
  4. Assign tiles to terrain by painting them
  1. 在TileSet编辑器中:地形标签页
  2. 添加地形集(例如:「地面」)
  3. 添加地形(例如:「草地」、「泥土」)
  4. 通过绘制为瓦片分配地形

Use Terrain in Code

代码中使用地形

gdscript
extends TileMapLayer

func paint_terrain(start: Vector2i, end: Vector2i, terrain_set: int, terrain: int) -> void:
    for x in range(start.x, end.x + 1):
        for y in range(start.y, end.y + 1):
            set_cells_terrain_connect(
                [Vector2i(x, y)],
                terrain_set,
                terrain,
                false  # ignore_empty_terrains
            )
gdscript
extends TileMapLayer

func paint_terrain(start: Vector2i, end: Vector2i, terrain_set: int, terrain: int) -> void:
    for x in range(start.x, end.x + 1):
        for y in range(start.y, end.y + 1):
            set_cells_terrain_connect(
                [Vector2i(x, y)],
                terrain_set,
                terrain,
                false  # ignore_empty_terrains
            )

Multiple Layers Pattern

多层级模式

gdscript
undefined
gdscript
undefined

Scene structure:

场景结构:

Node2D (Level)

Node2D (关卡)

├─ TileMapLayer (Ground)

├─ TileMapLayer (地面)

├─ TileMapLayer (Decoration)

├─ TileMapLayer (装饰)

└─ TileMapLayer (Collision)

└─ TileMapLayer (碰撞)

Each layer can have different:

每个层级可配置不同的:

- Rendering order (z_index)

- 渲染顺序(z_index)

- Collision layers/masks

- 碰撞层/掩码

- Modulation (color tint)

- 调制颜色(色调)

undefined
undefined

Physics Integration

物理集成

Enable Physics Layer

启用物理层

  1. TileSet editor → Physics Layers
  2. Add physics layer
  3. Assign collision shapes to tiles
Check collision from code:
gdscript
func _physics_process(delta: float) -> void:
    # TileMapLayer acts as StaticBody2D
    # CharacterBody2D.move_and_slide() automatically detects tilemap collision
    pass
  1. TileSet编辑器 → 物理层
  2. 添加物理层
  3. 为瓦片分配碰撞形状
代码中检测碰撞:
gdscript
func _physics_process(delta: float) -> void:
    # TileMapLayer可作为StaticBody2D使用
    # CharacterBody2D.move_and_slide()会自动检测TileMap碰撞
    pass

One-Way Collision Tiles

单向碰撞瓦片

gdscript
undefined
gdscript
undefined

In TileSet physics layer settings:

在TileSet物理层设置中:

- Enable "One Way Collision"

- 启用「单向碰撞」

- Set "One Way Collision Margin"

- 设置「单向碰撞边距」

Character can jump through from below

角色可从下方跳跃穿过

undefined
undefined

Custom Tile Data

自定义瓦片数据

Define Custom Data Layer

定义自定义数据层

  1. TileSet editor → Custom Data Layers
  2. Add property (e.g., "damage_per_second: int")
  3. Set value for specific tiles
  1. TileSet编辑器 → 自定义数据层
  2. 添加属性(例如:"damage_per_second: int")
  3. 为特定瓦片设置值

Read Custom Data

读取自定义数据

gdscript
func get_tile_damage(tile_pos: Vector2i) -> int:
    var tile_data := get_cell_tile_data(tile_pos)
    if tile_data:
        return tile_data.get_custom_data("damage_per_second")
    return 0
gdscript
func get_tile_damage(tile_pos: Vector2i) -> int:
    var tile_data := get_cell_tile_data(tile_pos)
    if tile_data:
        return tile_data.get_custom_data("damage_per_second")
    return 0

Performance Optimization

性能优化

Use TileMapLayer Groups

使用TileMapLayer分组

gdscript
undefined
gdscript
undefined

Static geometry: Single large TileMapLayer

静态几何体:单个大型TileMapLayer

Dynamic tiles: Separate layer for runtime changes

动态瓦片:单独层级用于运行时修改

undefined
undefined

Chunking for Large Worlds

大型世界分块

gdscript
undefined
gdscript
undefined

Split world into multiple TileMapLayer nodes

将世界拆分为多个TileMapLayer节点

Load/unload chunks based on player position

根据玩家位置加载/卸载区块

const CHUNK_SIZE := 32
func load_chunk(chunk_coords: Vector2i) -> void: var chunk_name := "Chunk_%d_%d" % [chunk_coords.x, chunk_coords.y] var chunk := TileMapLayer.new() chunk.name = chunk_name chunk.tile_set = base_tileset add_child(chunk) # Load tiles for this chunk...
undefined
const CHUNK_SIZE := 32
func load_chunk(chunk_coords: Vector2i) -> void: var chunk_name := "Chunk_%d_%d" % [chunk_coords.x, chunk_coords.y] var chunk := TileMapLayer.new() chunk.name = chunk_name chunk.tile_set = base_tileset add_child(chunk) # 加载该区块的瓦片...
undefined

Navigation Integration

导航集成

Setup Navigation Layer

设置导航层

  1. TileSet editor → Navigation Layers
  2. Add navigation layer
  3. Paint navigation polygons on tiles
Use with NavigationAgent2D:
gdscript
undefined
  1. TileSet编辑器 → 导航层
  2. 添加导航层
  3. 为瓦片绘制导航多边形
结合NavigationAgent2D使用:
gdscript
undefined

Navigation automatically created from TileMap

导航系统会自动从TileMap生成导航数据

NavigationAgent2D.get_next_path_position() works immediately

NavigationAgent2D.get_next_path_position()可直接使用

undefined
undefined

Best Practices

最佳实践

1. Organize TileSet by Purpose

1. 按用途组织TileSet

TileSet Layers:
- Ground (terrain=grass, dirt, stone)
- Walls (collision + rendering)
- Decoration (no collision, overlay)
TileSet层级:
- 地面(地形:草地、泥土、石头)
- 墙体(碰撞 + 渲染)
- 装饰(无碰撞,叠加层)

Available Scripts

可用脚本

MANDATORY: Read before implementing terrain systems or runtime placement.
必须阅读:在实现地形系统或运行时放置功能前请先阅读。

terrain_autotile.gd

terrain_autotile.gd

Runtime terrain autotiling with
set_cells_terrain_connect
batching and validation.
基于
set_cells_terrain_connect
批处理与验证的运行时地形自动铺砖脚本。

tilemap_chunking.gd

tilemap_chunking.gd

Chunk-based TileMap management with batched updates - essential for large procedural worlds.
基于分块的TileMap管理脚本,支持批处理更新——是大型程序化世界的必备工具。

2. Use Terrain for Organic Shapes

2. 用地形实现有机形状

gdscript
undefined
gdscript
undefined

✅ Good - smooth terrain transitions

✅ 推荐 - 平滑地形过渡

set_cells_terrain_connect(tile_positions, 0, 0)
set_cells_terrain_connect(tile_positions, 0, 0)

❌ Bad - manual tile assignment for organic shapes

❌ 不推荐 - 手动为有机形状分配瓦片

for pos in positions: set_cell(pos, 0, Vector2i(0, 0))
undefined
for pos in positions: set_cell(pos, 0, Vector2i(0, 0))
undefined

3. Layer Z-Index Management

3. 层级Z轴顺序管理

gdscript
undefined
gdscript
undefined

Background layers

背景层

$Background.z_index = -10
$Background.z_index = -10

Ground layer

地面层

$Ground.z_index = 0
$Ground.z_index = 0

Foreground decoration

前景装饰层

$Foreground.z_index = 10
undefined
$Foreground.z_index = 10
undefined

Common Patterns

常见模式

Destructible Tiles

可破坏瓦片

gdscript
func destroy_tile(world_pos: Vector2) -> void:
    var tile_pos := local_to_map(world_pos)
    var tile_data := get_cell_tile_data(tile_pos)
    
    if tile_data and tile_data.get_custom_data("destructible"):
        erase_cell(tile_pos)
        # Spawn particle effect, drop items, etc.
gdscript
func destroy_tile(world_pos: Vector2) -> void:
    var tile_pos := local_to_map(world_pos)
    var tile_data := get_cell_tile_data(tile_pos)
    
    if tile_data and tile_data.get_custom_data("destructible"):
        erase_cell(tile_pos)
        # 生成粒子效果、掉落物品等

Tile Highlighting

瓦片高亮

gdscript
@onready var highlight_layer: TileMapLayer = $HighlightLayer

func highlight_tile(tile_pos: Vector2i) -> void:
    highlight_layer.clear()
    highlight_layer.set_cell(tile_pos, 0, Vector2i(0, 0))
gdscript
@onready var highlight_layer: TileMapLayer = $HighlightLayer

func highlight_tile(tile_pos: Vector2i) -> void:
    highlight_layer.clear()
    highlight_layer.set_cell(tile_pos, 0, Vector2i(0, 0))

Reference

参考资料

Related

相关技能

  • Master Skill: godot-master
  • 大师技能:godot-master