Community编程与开发github.com

ziva-sh/godot-tilemaplayer

修正 Godot 4.3+ 的 TileMapLayer 代码 —— 使用 set_cell(coords, source_id, atlas_coords)、读取单元格和自定义数据、擦除、坐标转换、地形、通过 TileSet 物理层实现格子碰撞、查找身体碰撞到的瓷砖,以及从已弃用的 TileMap 节点及其层索引 API 迁移。在编写 GDScript 放置、读取或擦除瓷砖、代码构建 TileSet、程序化生成关卡、处理格子碰撞或从 Godot 3 或 Godot 4.0 移植 TileMap 代码时使用。

godot-tilemaplayer 是什么?

该技能是一个针对 Godot 4.3+ 引擎的综合指南,专注于新的 TileMapLayer 节点,取代了已弃用的 TileMap。它解释了如何使用新的单元格 API 进行放置、读取和擦除操作,包括三个关键标识符:source_id、atlas_coords 和 alternative_tile。技能覆盖了自定义数据读取、基于地形集合的自动拼接(autotiling)以及通过 TileSet 物理层实现格子碰撞检测等高级功能。此外,它还提供了代码构建 TileSet 的示例,以及从旧版 TileMap 迁移到 TileMapLayer 的完整映射表,并指出了常见错误如传递像素坐标代替地图坐标、忘记 source_id 导致单元格被擦除等。支持 GDScript 脚本编写,适用于任何使用 Godot 4.3+ 进行 2D 游戏开发的开发者。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/ziva-sh/godot-skills/tree/HEAD/skills/godot-tilemaplayer

Installed? Explore more 编程与开发 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

TileMapLayer (Godot 4.3+)

Godot 4.3 添加了 TileMapLayer 节点并弃用了 TileMap。每个 TileMapLayer 是一个图层,因此一个包含地面、墙壁和装饰的关卡是三个同级 TileMapLayer 节点共享一个 TileSet。不要针对 TileMap 编写新代码,也永远不要使用 Godot 3 的 API(set_cellv、world_to_map、set_cell(x, y, tile_id))。

单元格 API

单元格由整数地图坐标(Vector2i)标识。一个图块由三个值标识:

  • source_id:TileSet 中源的 ID(图集纹理或场景集合)。第一个图集通常为 0;请检查 TileSet 编辑器或 TileSet.add_source() 的返回值。
  • atlas_coords:图块在图集中的列和行(以图块为单位,非像素)。
  • alternative_tile:除非您创建了翻转或旋转的替代图块,否则为 0。
extends TileMapLayer

const SOURCE_ID := 0
const GRASS := Vector2i(0, 0)
const WALL := Vector2i(1, 0)


func _ready() -> void:
	for x in range(10):
		set_cell(Vector2i(x, 5), SOURCE_ID, GRASS)
	set_cell(Vector2i(3, 4), SOURCE_ID, WALL)

	var source_id := get_cell_source_id(Vector2i(3, 4))  # 单元格为空时返回 -1
	var atlas := get_cell_atlas_coords(Vector2i(3, 4))  # 为空时返回 Vector2i(-1, -1)
	print(source_id, atlas)

	erase_cell(Vector2i(3, 4))
	print(get_used_cells().size(), " 个单元格,范围 ", get_used_rect())


func cell_at_global(global_point: Vector2) -> Vector2i:
	return local_to_map(to_local(global_point))


func center_of_cell(coords: Vector2i) -> Vector2:
	# map_to_local 返回该图层局部空间中单元格的中心。
	return to_global(map_to_local(coords))

重要事实:

  • 使用默认参数调用 set_cell(coords) 会擦除该单元格(source_id 默认为 -1)。放置图块时始终要传递 source_id 和 atlas_coords。
  • 设置图集中不存在的 atlas_coords 会存储该单元格而不报错(get_cell_source_id() 返回源),但之后调用 get_cell_tile_data() 会记录 The TileSetAtlasSource atlas has no tile at (x, y). 并返回 null。
  • local_to_map() 和 map_to_local() 在图层的局部空间中工作。当图层被移动或缩放时,请使用 to_local() / to_global() 进行转换。
  • get_used_cells_by_id(source_id, atlas_coords) 查找使用某个图块的所有单元格。
  • 更改会批量处理到帧末尾。如果希望碰撞或场景图块立即更新,请调用 update_internals()。

每个图块的自定义数据

在 TileSet 中添加自定义数据层(Inspector > TileSet > Custom Data Layers,例如类型为 int 的 damage),在 TileSet 编辑器中绘制值,然后读取:

extends TileMapLayer


func damage_at(coords: Vector2i) -> int:
	var data := get_cell_tile_data(coords)
	if data == null:
		return 0
	return data.get_custom_data("damage")

get_cell_tile_data() 对于空单元格和场景集合图块返回 null,因此调用其方法前请进行检查。

地形(自动拼接)

Godot 3 的自动图块位掩码已转为地形集合。在 TileSet 中,添加地形集合和地形,然后在 TileSet 编辑器中绘制每个图块的地形衔接位。在代码中,传入需要填充的每个单元格:

extends TileMapLayer

const TERRAIN_SET := 0
const DIRT := 0


func paint_room(rect: Rect2i) -> void:
	var cells: Array[Vector2i] = []
	for x in range(rect.position.x, rect.end.x):
		for y in range(rect.position.y, rect.end.y):
			cells.append(Vector2i(x, y))
	set_cells_terrain_connect(cells, TERRAIN_SET, DIRT)

set_cells_terrain_connect() 选择图块以使边缘连接;set_cells_terrain_path() 用于绘制路径形状的道路和河流。如果结果显示图块错误或缺失,说明 TileSet 中的衔接位不完整;代码无法修复此问题。

碰撞

只有 TileSet 具有物理层(TileSet 检查器 > Physics Layers > Add Element)且图块在该层上具有碰撞多边形(TileSet 编辑器,选择图块,Physics > Physics Layer 0;按 F 键可添加一个满图块矩形)时,图块才会发生碰撞。图层的 collision_enabled 属性默认开启。物理层还带有 collision_layer 和 collision_mask,这些在 TileSet 上设置,而不是在节点上。

要查找 CharacterBody2D 接触了哪个图块,请使用碰撞点。get_coords_for_body_rid() 返回物理象限的坐标,而不是单元格的坐标,除非 physics_quadrant_size 为 1(Godot 4.5+ 默认将最多 16x16 个单元格合并为一个物体):

extends CharacterBody2D

@export var level: TileMapLayer


func _physics_process(delta: float) -> void:
	velocity += get_gravity() * delta
	move_and_slide()
	for i in get_slide_collision_count():
		var collision := get_slide_collision(i)
		if collision.get_collider() == level:
			# 稍微进入图块内部,因为接触点在其边缘上。
			var inside := collision.get_position() - collision.get_normal()
			var coords := level.local_to_map(level.to_local(inside))
			print("接触图块 ", coords)

在代码中构建 TileSet

适用于程序化关卡和无法使用 TileSet 编辑器的测试。碰撞多边形点相对于图块中心。

extends Node2D


func _ready() -> void:
	var tile_set := TileSet.new()
	tile_set.tile_size = Vector2i(16, 16)
	tile_set.add_physics_layer()

	var source := TileSetAtlasSource.new()
	source.texture = load("res://tiles.png") as Texture2D
	source.texture_region_size = Vector2i(16, 16)
	source.create_tile(Vector2i(0, 0))
	var source_id := tile_set.add_source(source)

	var data := source.get_tile_data(Vector2i(0, 0), 0)
	data.add_collision_polygon(0)
	data.set_collision_polygon_points(0, 0, PackedVector2Array([
		Vector2(-8, -8), Vector2(8, -8), Vector2(8, 8), Vector2(-8, 8),
	]))

	var layer := TileMapLayer.new()
	layer.tile_set = tile_set
	add_child(layer)
	layer.set_cell(Vector2i(0, 0), source_id, Vector2i(0, 0))

从 TileMap 迁移

在编辑器中:选择 TileMap,打开 TileMap 底部面板,点击工具箱图标并选择“将 TileMap 图层提取为独立的 TileMapLayer 节点”。然后更新脚本:每个接受 layer 索引的 TileMap 方法都将变为该图层节点上的方法,无需索引。

旧代码TileMapLayer
tile_map.set_cell(0, coords, source_id, atlas_coords)ground.set_cell(coords, source_id, atlas_coords)
tile_map.get_cell_source_id(1, coords)walls.get_cell_source_id(coords)
tile_map.get_used_cells(0)ground.get_used_cells()
tile_map.set_layer_enabled(2, false)decoration.enabled = false
tile_map.set_layer_z_index(1, 5)walls.z_index = 5
Godot 3 set_cellv(pos, id), world_to_map(p), map_to_world(c)set_cell(coords, source_id, atlas_coords), local_to_map(p), map_to_local(c)

常见错误

  • 将像素位置传递给 set_cell()。请先使用 local_to_map() 转换。
  • 用单个数字调用 set_cell(coords, tile_index):第二个参数是源 ID,而 atlas_coords 默认为 Vector2i(-1, -1),这会擦除单元格。
  • 期望从没有物理层的 TileSet 或没有绘制多边形的图块产生碰撞。
  • 使用默认 physics_quadrant_size 时调用 get_coords_for_body_rid() 得到的是象限坐标。
  • 未进行 null 检查就调用 get_cell_tile_data() 的方法。
  • 因为较旧的教程使用了 TileMap 节点而继续用于新工作。每个图层请使用一个 TileMapLayer。

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

ziva-sh/godot-4-api-pitfalls

Fixes Godot 3 APIs and GDScript syntax that AI assistants write into Godot 4 projects, such as KinematicBody2D, move_and_slide(velocity), export, onready, setget, yield, string connect(), instance(), rand_range, the Tween node, File, change_scene, Spatial, rect_* properties, Pool*Array, OS time functions and the old TileMap API. Use when writing, reviewing or porting GDScript for Godot 4.x, when a script fails with "not declared", "not found in base", "was removed in Godot 4" or "is not present

ziva-sh/godot-control-layout

Builds Godot 4 Control UIs that resize instead of clipping or overlapping - containers versus anchors, anchor presets from code, size flags, custom_minimum_size, MarginContainer margins, Label wrapping, mouse_filter, CanvasLayer HUDs, Godot 4 theme override names (add_theme_color_override and friends) and the display/window/stretch settings for multiple resolutions. Use when creating or fixing menus, HUDs, dialogs and inventory screens, when UI elements are cut off, zero-sized, overlapping, igno

ziva-sh/godot-scene-organization

Structures Godot 4 projects so scenes stay reusable and references do not break - when to make a scene, a script or a Resource, instancing with @export PackedScene, preload and load, @onready and %UniqueName node access, _ready order, groups, autoloads, class_name, and resource UIDs with the .uid files added in Godot 4.4, including how to move or rename files safely. Use when creating new scenes or scripts, deciding how nodes find each other, adding global state or an event bus, moving or renami

ziva-sh/godot-signals

Godot 4 signal patterns and their failure modes - declaring typed signals, emit(), connect() and disconnect() with Callables, bind() and unbind(), lambdas, CONNECT_ONE_SHOT and CONNECT_DEFERRED, await on signals and timers, editor-made connections stored in .tscn files, and the call down, signal up rule. Use when wiring UI buttons, timers, Area2D or body events and custom game events, when replacing Godot 3 connect("signal", self, "method") or yield code, or when debugging "already connected", "

相关技能