Godot 3.4 使用 TileMap 实例化场景

主要思路参考 timothyqiu 的《如何在 TileMap 中使用实例场景【Godot 教程】》,略有优化。

原理是将取好名的 TileSet 作为「占位图」画好,加载的时候通过脚本代码自动用「场景实例」替换掉「该名称的占位图」。

原因

为什么要这样?关卡里不是所有元素都是一张图:比如一棵树,它不只是一张图,它有自己的碰撞体(可以通俗地理解为有模型),因此不能直接用(目前仅限图像)的 Autotile 来画。
包括关卡里的小怪,它们不止有模型,甚至还有 AI(实现运行逻辑的脚本)。

代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# utils.gd
extends Node
# autoload 自动加载的工具类


func instance_tiles(tilemap, tile_name, scene_path):
	var tile_id = tilemap.tile_set.find_tile_by_name(tile_name)
	assert(tile_id != -1)
	var scene = load(scene_path)
	var tile_region_size = tilemap.tile_set.tile_get_region(tile_id).size
	for pos in tilemap.get_used_cells_by_id(tile_id):
		var scene_node = scene.instance()  # 实例化一个新的场景节点
		tilemap.get_parent().add_child(scene_node)  # 把场景实例挂载到场景树
		scene_node.position = tilemap.map_to_world(pos) + tile_region_size / 2  # 修正位置
		tilemap.set_cellv(pos, -1)  # 清除占位图

然后在项目设置 > AutoLoad 加载并启用该工具脚本。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# level.gd
extends Node2D
# 关卡脚本


onready var _models_tilemap = {
	# 前三个节点都是 YSort 便于自动排序
	# 最后是设定好的 TileMap 节点
	"tree": $Entities/Plants/Trees/TreeTileMap,
	"bush": $Entities/Plants/Bushes/BushTileMap,
	"grass": $Entities/Plants/Grasses/GrassTileMap,
}


func _ready():
	for model in _models_tilemap:
		Utils.instance_tiles(_models_tilemap[model], model,
				"res://src/models/" + model + ".tscn")

与开头提到的视频教程有什么区别呢?
主要的问题在于 timothyqiu 大佬的演示视频是用的他平台跳跃系列教程的关卡素材,实例化的场景是金币(标准的方格)。可是其他类型游戏(比如俯视角 ARPG)实际应用时可能不是标准的没有偏移量的正方形 tile,而是长方形、也许材质还带有偏移量。
此时再用视频教程里用的 cell_size 来计算修正坐标就会出现错位的 bug。

解释

优化之处是利用图块tile本身的尺寸大小来修正坐标,并利用 Tex Offset 来校正偏移量。
场景本身的 Sprite 节点用的材质图偏移量有多少(注意是 Offset 不要和 Position 弄混了),TileMap > TileSet > Selected Tile > Tex Offset 就应该是多少。
最后的效果才是「所见即所得」,编辑器里预览是什么样,实际游戏运算也一样,不存在错位。

为什么一些材质必须有 Offset?因为 YSort 节点的自动排序功能是按「原点」来排序的。
因此 Sprite 节点的原点应该和(视觉上的)图像中心同步,否则排序后就会出现穿模 bug。

而且俯视角 2D 游戏还有个需求是材质可能重叠,比如紧挨着的两棵树上下是会重叠一部分的。
解决方案是将设置好的 不同 规格的 TileMap > Cell > Size 统一 改为固定的值(即正方形方格,比如 16 × 16)。这样就能统一按照同一标准(比例尺)对齐了。
而且允许多个材质之间错开一部分而不是「要么完全错开,要么完全重叠」。
另外 TileMap > Tile Origin 也可以设置为 Center 而不是默认的左上角Top Left,这样相对来说大尺寸的材质就比较好操作。

updatedupdated2022-05-012022-05-01