PackedScene и Resource
Универсальный контейнер сцены/префаба и data-only ассеты.
PackedScene — сцена и префаб в одном
Главная архитектурная особенность Godot: сцена и префаб — одна и та же сущность. Файл
.tscn хранит дерево узлов; этот файл можно открыть как корневую сцену проекта или
инстанциировать как ребёнка в другую сцену. Внутри Engine оба варианта — PackedScene-ресурс.
res://
├── scenes/
│ ├── main.tscn ← главная сцена уровня
│ ├── player.tscn ← "префаб" игрока
│ ├── enemy.tscn ← "префаб" врага
│ └── ui/
│ └── hud.tscn ← UI как сцена
В Inspector у Player.tscn те же узлы и компоненты, что и у Main.tscn. Любая сцена — это
шаблон, который можно встраивать.
Это React: один и тот же <UserCard /> рендерится и как страница /users/me, и как виджет
в списке. Никакого разделения “это компонент, а это страница”.
В Unity Scene (.unity) и Prefab (.prefab) — два разных формата. В Godot — один.
Соответственно, “вложенные префабы” — это просто вложенные сцены.
Инстанцирование
В редакторе: правый клик на узел → Instantiate Child Scene → выбрать .tscn. Или drag-and-drop
из FileSystem в Scene.
В коде:
const ENEMY_SCENE := preload("res://scenes/enemy.tscn")
func spawn_enemy(at: Vector3) -> void:
var enemy = ENEMY_SCENE.instantiate() as Node3D
enemy.global_position = at
add_child(enemy)
instantiate() создаёт корневой узел сцены и все её дети. preload() загружает на этапе компиляции
скрипта (статично), load() — в runtime.
Inherited Scenes — мощнее Prefab Variants
В Godot можно создать сцену, унаследованную от другой. Это даёт ту же модель, что Prefab Variant в Unity, но без отдельного механизма.
- Создайте
Enemy.tscn— базовый враг. - New Inherited Scene → выберите
Enemy.tscn. - Сохраните как
EnemyElite.tscn— это inherited scene. - В Inspector у любого узла переопределяете свойства (выделятся жирным/синим). Можно даже добавлять новые узлы.
Изменения базового Enemy.tscn пропагируются на EnemyElite.tscn, кроме override’ов.
Сцена может содержать другую сцену как ребёнка, а та — третью. Глубина не ограничена.
Получается чёткая иерархия “уровень → комната → дверь”, где каждая дверь — своя .tscn
с собственной логикой. Изменили door.tscn — изменились все двери проекта.
Editable Children
По умолчанию вложенная сцена выглядит как один узел (корень) — дети скрыты, “запечатаны”. Чтобы переопределить дочерний узел вложенной сцены, нажмите Right Click → Editable Children. Тогда дети станут видны и доступны для override.
Resource — data-only ассеты
Resource — базовый класс для всего, что не узел. Сериализуется в .tres (текст) или .res
(бинарный). Подходит для:
- Конфигов (статы оружия, рецепты).
- Базы данных (список предметов).
- Curve / Gradient / Material / Mesh — всё это Resource.
- Custom data объекты в вашей игре.
Resource — reference-counted: автоматически удаляется, когда нет ссылок.
Custom Resource — аналог ScriptableObject
Создаёте GD-скрипт, наследующий Resource, с @export-полями и class_name:
# weapon_data.gd
class_name WeaponData extends Resource
@export var display_name: String = "Pistol"
@export var icon: Texture2D
@export var damage: int = 10
@export var fire_rate: float = 0.4
@export var fire_sound: AudioStream
@export var projectile_scene: PackedScene
Теперь в FileSystem → New Resource → WeaponData → сохранить как pistol.tres, shotgun.tres.
Каждый — независимый ассет с уникальными значениями.
Использование:
extends Node3D
class_name Weapon
@export var data: WeaponData
@onready var muzzle: Marker3D = $Muzzle
var _next_fire_time: float = 0.0
func try_fire() -> void:
var now = Time.get_ticks_msec() / 1000.0
if now < _next_fire_time:
return
_next_fire_time = now + data.fire_rate
var sfx = AudioStreamPlayer3D.new()
sfx.stream = data.fire_sound
add_child(sfx)
sfx.play()
sfx.finished.connect(sfx.queue_free)
var bullet = data.projectile_scene.instantiate()
bullet.global_transform = muzzle.global_transform
get_tree().current_scene.add_child(bullet)
В Inspector перетягиваете pistol.tres в поле data — оружие стреляет как пистолет. Меняете
на shotgun.tres — как дробовик. Изменения в коде не нужны.
По умолчанию, если у двух узлов в Inspector один WeaponData.tres, и вы поменяете значение
в коде (weapon.data.damage = 5), это изменит ассет на диске в редакторе. Для рантайма
это норма, в редакторе — нежелательно (испортите ассет случайно).
Если нужен отдельный экземпляр, делайте data.duplicate() или включите свойство
resource_local_to_scene = true.
Built-in vs External resources
В .tscn-файле сцена может содержать:
- External resources — ссылки на отдельные
.tres-файлы. - Built-in resources — встроенные внутрь сцены (когда вы создаёте ресурс прямо в Inspector
через
[empty]→ New).
Built-in удобно для одноразовых ресурсов конкретной сцены (например, специфический материал
двери). External — для переиспользуемых (одна WeaponData.tres на 10 врагов).
Сравнение с Unity
| Unity | Godot |
|---|---|
| Scene (.unity) | PackedScene (.tscn) — корневая сцена |
| Prefab (.prefab) | PackedScene (.tscn) — инстанцированная |
| Prefab Variant | Inherited Scene |
| Nested Prefab | Просто вложенная сцена |
| Prefab Override | Editable Children + per-property override |
| ScriptableObject | Custom Resource (.tres) |
В следующей главе соберём всё вместе — практический FPS-контроллер на CharacterBody3D.