GridMap — модульные уровни
MeshLibrary + GridMap для сборки локаций из переиспользуемых блоков.
GridMap — встроенный Godot узел для сборки уровней из модульных блоков на 3D-сетке. Думайте: Minecraft, Dungeon Crawler, Cities Skylines — где мир состоит из переиспользуемых “кирпичиков” размером с тайл.
Идея
- Сделайте MeshLibrary-ресурс — набор mesh’ей с заранее заданными CollisionShape3D.
- Положите его на узел GridMap.
- В Scene View рисуете “кистью” по 3D-сетке — каждая клетка получает выбранный из библиотеки mesh.
- Godot рендерит весь GridMap одним вызовом на тип меша (внутреннее instancing).
Это и быстрее, и компактнее, чем десять тысяч отдельных MeshInstance3D-узлов.
CSS-grid с tile-images. Только в 3D, и с physical colliders.
Создание MeshLibrary
MeshLibrary — это Resource со словарём id → { mesh, collision, navigation, preview }. Создать
можно двумя способами:
Способ 1. Конвертировать сцену с детьми-mesh
- Создайте сцену
tiles.tscnс корнемNode3D. - Дочерние —
MeshInstance3Dс уникальными именами (wall_corner,floor_plain,door_wood). - Каждый mesh —
StaticBody3Dребёнком сCollisionShape3D, чтобы collision запеклась. - Scene → Convert To → MeshLibrary → сохранить
.tresили.res.
Способ 2. Программно
var lib := MeshLibrary.new()
var id := 0
lib.create_item(id)
lib.set_item_name(id, "wall_corner")
lib.set_item_mesh(id, preload("res://meshes/wall_corner.obj"))
lib.set_item_shapes(id, [my_box_shape]) # массив Shape3D
ResourceSaver.save(lib, "res://tiles/library.tres")
Использование GridMap
- Добавьте на сцену GridMap.
- В Inspector укажите Mesh Library → ваш
.tres. - Настройте Cell Size (например,
(2, 2, 2)— кубики 2×2×2 метра). - В Scene View — GridMap toolbar с палитрой ваших mesh’ей. Кликами рисуете в сетке.
Управление из кода
extends GridMap
const FLOOR := 0
const WALL := 1
const DOOR := 2
func _ready() -> void:
# Простая комната 5×5 из стен и пола
for x in range(5):
for z in range(5):
set_cell_item(Vector3i(x, 0, z), FLOOR)
if x == 0 or x == 4 or z == 0 or z == 4:
set_cell_item(Vector3i(x, 1, z), WALL)
# Дверь в (2, 1, 0)
set_cell_item(Vector3i(2, 1, 0), DOOR)
func is_cell_empty(pos: Vector3i) -> bool:
return get_cell_item(pos) == GridMap.INVALID_CELL_ITEM
Vector3i — целочисленные координаты ячейки. set_cell_item(pos, item_id, orientation) —
последний параметр — поворот блока (24 ориентации куба, через GridMap.get_orthogonal_index_from_basis).
Procedural dungeon generation
GridMap идеально подходит для процедурной генерации:
extends GridMap
const FLOOR := 0
const WALL := 1
@export var size_x: int = 30
@export var size_z: int = 30
@export var iterations: int = 5
func _ready() -> void:
randomize()
_generate_cellular()
func _generate_cellular() -> void:
# 1. Заполнить случайно
var grid: Array = []
for x in size_x:
grid.append([])
for z in size_z:
grid[x].append(randf() < 0.45)
# 2. Cellular automata smoothing
for _i in iterations:
var next: Array = []
for x in size_x:
next.append([])
for z in size_z:
var n := _count_neighbors(grid, x, z)
# классическое cave-generation правило
next[x].append(n >= 5 if grid[x][z] else n >= 6)
grid = next
# 3. Применить в GridMap
for x in size_x:
for z in size_z:
set_cell_item(Vector3i(x, 0, z), FLOOR)
if grid[x][z]:
set_cell_item(Vector3i(x, 1, z), WALL)
func _count_neighbors(g: Array, x: int, z: int) -> int:
var count := 0
for dx in [-1, 0, 1]:
for dz in [-1, 0, 1]:
if dx == 0 and dz == 0:
continue
var nx := x + dx
var nz := z + dz
if nx < 0 or nx >= size_x or nz < 0 or nz >= size_z:
count += 1 # за границей — стена
elif g[nx][nz]:
count += 1
return count
Запускаете — получаете cave-style dungeon. С добавлением decoration-layer (статуи, факелы как отдельные ID в MeshLibrary) — выглядит уже как готовый уровень.
GridMap может автоматически выдавать navmesh-baking. Установите NavigationRegion3D с ребёнком
GridMap, и в Bake Navigation Mesh Geometry Source поставьте Group → дети_с_коллизиями.
Получаете navmesh без отдельного навиграционного слоя. См. главу про
Navigation.
MeshLibrary vs MultiMeshInstance3D
Когда выбирать одно или другое:
| GridMap + MeshLibrary | MultiMeshInstance3D | |
|---|---|---|
| Структура | Сетка с фиксированным шагом | Произвольные позиции |
| Редактирование | В Scene View “кистью” | Только из кода |
| Кол-во разных mesh’ей | До нескольких сотен | Один на узел |
| Per-instance variants | Через orientations | Через transform-buffer |
| Animation | Нет | Нет (для анимированных — другой подход) |
| Collision | Автоматическое из MeshLibrary | Нужно строить отдельно |
Простое правило: сетка → GridMap; trava/декорации без сетки → MultiMesh.
Когда GridMap — НЕ выбор
- Open-world террейн с нерегулярной топологией — лучше
Terrain3Dплагин. - Динамическая деструкция мира (Minecraft-стиль с реальным изменением) — работает, но оптимизация тонкая; рассмотрите ECS-style approach с GDExtension.
- Сцены с уникальной геометрией каждого блока — теряется выгода MeshLibrary, проще класть отдельные MeshInstance3D в PackedScene.
Альтернативы
- TileMap (2D) — то же самое для 2D-сцен.
- CSG (Constructive Solid Geometry) узлы — для прототипирования архитектуры без модульных мешей. Не для финального уровня.
- Terrain3D плагин (через AssetLib) — для terrain’а с heightmap.