Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

Ресурсы

Узлы и Ресурсы

До этого урока мы были сосредоточены на классе Node в Godot, так как именно его вы используете для написания поведения и большинство функций движка полагаются на него. Есть еще один тип данных, который так же важен: Resource.

Узлы дают вам функциональность: они рисуют спрайты, 3D модели, моделируют физику, организовывают UI и т.д. Ресурсы являются контейнерами данных. Они ничего не делают сами по себе: вместо этого узлы используют данные, содержащиеся в ресурсах.

Anything Godot saves or loads from disk is a resource. Be it a scene (a .tscn or an .scn file), an image, a script... Here are some Resource examples:

Когда движок загружает ресурс с диска, он загружает его только один раз. Если копия ресурса уже находится в памяти, при попытке загрузить его снова будет каждый раз возвращаться одна и та же копия. Поскольку ресурсы содержат только данные, нет необходимости их дублировать.

Каждый объект, будь то Узел или Ресурс, может экспортировать свойства. Существует множество типов свойств, таких как String, integer, Vector2 и т.д., и любой из этих типов может стать ресурсом. Это означает, что и узлы, и ресурсы могут содержать ресурсы словно они свойства:

../../_images/nodes_resources.webp

Внешние против Встроенных(Build-it)

Существует два способа сохранения ресурсов. Они могут быть:

  1. Внешний сохраненный в виде отдельных файлов.

  2. Встроенный сохраненный в .tscn или .scn файле, к которому они прикреплены.

To be more specific, here's a Texture2D in a Sprite2D node:

../../_images/spriteprop.webp

Clicking the resource preview allows us to view the resource's properties.

../../_images/resourcerobi.webp

Свойство Path говорит нам, откуда берется ресурс. В данном случае речь идет об PNG изображении, получившем название robi.png. Когда ресурс поступает из такого файла, он является внешним ресурсом. Если вы удалите этот путь или этот путь пуст, он станет встроенным ресурсом.

Переключение между встроенными и внешними ресурсами происходит при сохранении сцены. В приведенном выше примере, если вы удалите путь "res://robi.png" и сохраните, Godot сохранит изображение в файле сцены в формате .tscn.

Примечание

Даже если вы сохраните встроенный ресурс, при многократном копировании сцены, движок загрузит только одну ее копию.

Загрузка ресурсов из кода

Существует два способа загрузки ресурсов из кода. Во-первых, можно воспользоваться функцией load():

func _ready():
    # Godot loads the Resource when it reads this very line.
    var imported_resource = load("res://robi.png")
    $sprite.texture = imported_resource

You can also preload resources. Unlike load, this function will read the file from disk and load it at compile-time. As a result, you cannot call preload with a variable path: you need to use a constant string.

func _ready():
    # Godot loads the resource at compile-time
    var imported_resource = preload("res://robi.png")
    get_node("sprite").texture = imported_resource

Загрузка сцен

Scenes are also resources, but there is a catch. Scenes saved to disk are resources of type PackedScene. The scene is packed inside a Resource.

To get an instance of the scene, you have to use the PackedScene.instantiate() method.

func _on_shoot():
        var bullet = preload("res://bullet.tscn").instantiate()
        add_child(bullet)

Этот метод создает узлы в иерархии сцены, настраивает их и возвращает корневой узел сцены.Вы можете добавить его в качестве ребенка любого другого узла.

The approach has several advantages. As the PackedScene.instantiate() function is fast, you can create new enemies, bullets, effects, etc. without having to load them again from disk each time. Remember that, as always, images, meshes, etc. are all shared between the scene instances.

Очистка(освобождение) ресурсов

When a Resource is no longer in use, it will automatically free itself. Since, in most cases, Resources are contained in Nodes, when you free a node, the engine frees all the resources it owns as well if no other node uses them.

Создание собственных ресурсов

Как и любой объект в Godot, пользователи также могут создавать скрипты Ресурсов. Скрипты ресурса наследуют возможность свободного перемещения между свойствами объекта и сериализованным текстом или двоичными данными (*.tres, *.res). Они также наследуют управление памятью подсчета ссылок из ссылочного типа.

This comes with many distinct advantages over alternative data structures, such as JSON, CSV, or custom TXT files. Users can only import these assets as a Dictionary (JSON) or as a FileAccess to parse. What sets Resources apart is their inheritance of Object, RefCounted, and Resource features:

  • Они могут определять константы, поэтому константы из других полей данных или объектов не нужны.

  • Они могут определять методы, включая методы setter/getter для свойств. Это позволяет абстрагироваться и инкапсулировать исходные данные. Если структура скрипта ресурса должна измениться, то игра, использующая ресурс, не должна.

  • Они могут определять сигналы, чтобы Ресурсы могли инициировать реакцию на изменения в данных, которыми они управляют.

  • Они имеют определенные свойства, поэтому пользователи знают на 100%, что их данные будут существовать.

  • Ресурсы автоматически сериализуются и десериализуются — это встроенная функция движка Godot. Поэтому пользователям не требуется самостоятельно реализовывать логику импорта/экспорта файлов ресурсов.

  • Кроме того, ресурсы могут рекурсивно сериализовывать вложенные ресурсы. Это значит, что пользователи могут проектировать ещё более сложные структуры данных.

  • Пользователи могут сохранять ресурсы как текстовые файлы (*.tres) для лучшей совместимости с системами контроля версий. При экспорте игры Godot сериализует ресурсы в бинарные файлы (*.res) для увеличения быстродействия и экономии места.

  • Инспектор Godot Engine осуществляет рендеринг и редактирование файлов ресурсов "из коробки". Таким образом, пользователям часто не требуется придумывать собственную логику для визуализации или редактирования своих данных. Для этого дважды щелкните по файл ресурса в файловой системе или на значок папки в Инспекторе и выберите файл в диалоговом окне.

  • Они могут расширять другие виды ресурсов, помимо простого базового ресурса.

Godot облегчает создание пользовательских ресурсов в Инспекторе.

  1. Создайте простой объект ресурса в Инспекторе. Это может быть даже тип, производный от ресурса, если сценарий расширяет этот тип.

  2. Установите в свойстве script в Инспекторе Ваш собственный скрипт.

Инспектор теперь отобразит пользовательские свойства вашего скрипта ресурсов. Если отредактировать эти значения и сохранить ресурс, Инспектор также выполнит сериализацию пользовательских свойств! Для сохранения ресурса Инспектора щелкните на меню инструментов Инспектора (вверху справа) и выберите "Сохранить" или "Сохранить как...".

If the script's language supports script classes, then it streamlines the process. Defining a name for your script alone will add it to the Inspector's creation dialog. This will auto-add your script to the Resource object you create.

Let's see some examples. Create a Resource and name it bot_stats. It should appear in your file tab with the full name bot_stats.tres. Without a script, it's useless, so let's add some data and logic! Attach a script to it named bot_stats.gd (or just create a new script, and then drag it to it).

extends Resource

@export var health: int
@export var sub_resource: Resource
@export var strings: PackedStringArray

# Make sure that every parameter has a default value.
# Otherwise, there will be problems with creating and editing
# your resource via the inspector.
func _init(p_health = 0, p_sub_resource = null, p_strings = []):
    health = p_health
    sub_resource = p_sub_resource
    strings = p_strings

Now, create a CharacterBody3D, name it Bot, and add the following script to it:

extends CharacterBody3D

@export var stats: Resource

func _ready():
    # Uses an implicit, duck-typed interface for any 'health'-compatible resources.
    if stats:
        stats.health = 10
        print(stats.health)
        # Prints "10"

Now, select the CharacterBody3D node which we named bot, and drag&drop the bot_stats.tres resource onto the Inspector. It should print 10! Obviously, this setup can be used for more advanced features than this, but as long you really understand how it all worked, you should figure out everything else related to Resources.

Примечание

Скрипты Ресурсов похожи на Unity's ScriptableObjects. Инспектор обеспечивает встроенную поддержку пользовательских ресурсов. При желании пользователи могут даже создавать собственные скрипты инструментов на основе Control и комбинировать их с EditorPlugin для создания собственных визуализаций и редакторов для своих данных.

Unreal Engine's DataTables and CurveTables are also easy to recreate with Resource scripts. DataTables are a String mapped to a custom struct, similar to a Dictionary mapping a String to a secondary custom Resource script.

# bot_stats_table.gd
extends Resource

const BotStats = preload("bot_stats.gd")

var data = {
    "GodotBot": BotStats.new(10), # Creates instance with 10 health.
    "DifferentBot": BotStats.new(20) # A different one with 20 health.
}

func _init():
    print(data)

Instead of inlining the Dictionary values, one could also, alternatively:

  1. Import a table of values from a spreadsheet and generate these key-value pairs.

  2. Design a visualization within the editor and create a plugin that adds it to the Inspector when you open these types of Resources.

CurveTables - это то же самое, за исключением сопоставления с массивом значений с плавающей запятой или ресурсным объектом Curve/Curve2D.

Предупреждение

Помните, что ресурсные файлы (*.tres/*.res) будут содержать путь к скрипту, который они используют. При загрузке они будут получать и загружать этот скрипт в качестве расширения своего типа. Это означает, что попытка присвоить подкласс, т.е. внутренний класс скрипта (например, с помощью ключевого слова class в GDScript'') не сработает. Godot не будет должным образом сериализовать пользовательские свойства в подклассе сценариев.

В примере ниже, Godot загрузил скрипт Node, увидел, что он не расширяет Resource, а затем определил, что скрипт не загрузился для объекта Resource, поскольку типы несовместимы.

extends Node

class MyResource:
    extends Resource
    @export var value = 5

func _ready():
    var my_res = MyResource.new()

    # This will NOT serialize the 'value' property.
    ResourceSaver.save(my_res, "res://my_res.tres")