Ресурси

Вузли та ресурси

В цьому уроці ми зосередилися на класі Node в Godot, оскільки саме його ви використовуєте для кодування поведінки, і більшість функцій движка покладаються саме на нього. Є ще один тип даних, який так само важливий: Resource.

Вузли надають вам функціональність: вони малюють спрайти, тривимірні моделі, імітують фізику, впорядковують інтерфейс користувача тощо. Ресурси - це контейнери даних. Вони нічого не роблять самостійно: натомість, вузли використовують дані, що містяться в ресурсах.

Все, що Godot зберігає, або завантажує, з диска, - це ресурс. Будь це сцена (`` .tscn``, або .scn файл), зображення, скрипт ... Ось кілька Resource прикладів: Texture, Script, Mesh, Animation, AudioStream, Font, Translation.

Коли двигун завантажує ресурс з диска, він завантажує його лише один раз. Якщо копія цього ресурсу вже є в пам'яті, при спробі знову завантажити ресурс, кожен раз буде повертатися та сама копія. Оскільки ресурси містять лише дані, не потрібно дублювати їх.

Кожен об'єкт, чи то Вузол, чи Ресурс, може експортувати властивості. Існує багато типів властивостей, таких як String, integer, Vector2 тощо, і будь-який з цих типів може стати ресурсом. Це означає, що і вузли, і ресурси можуть містити ресурси типу властивостей:

../../_images/nodes_resources.png

Зовнішні та вбудовані

Існує два способи зберігання ресурсів:

  1. Зовнішнє, збережене на диску у вигляді окремих файлів.

  2. Вбудований, збережений всередині .tscn, або у .scn, файлу.

Щоб бути більш конкретним, ось Texture у вузлі Sprite:

../../_images/spriteprop.png

Клацання попереднього перегляду ресурсу дозволяє нам переглядати та редагувати властивості ресурсу.

../../_images/resourcerobi.png

Властивість Path (шлях) повідомляє нам, звідки береться ресурс. У цьому випадку воно походить від зображення PNG robi.png. Коли ресурс походить з такого файлу, це зовнішній ресурс. Якщо стерти шлях, або цей шлях порожній, він стане вбудованим ресурсом.

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

Примітка

Навіть якщо ви зберігаєте вбудований ресурс, коли ви копіюєте сцену кілька разів, движок завантажить лише одну його копію.

Завантаження ресурсів з коду

Існує два способи завантаження ресурсів з коду. По-перше, ви в будь-який час можете використовувати функцію load():

func _ready():
        var res = load("res://robi.png") # Godot loads the Resource when it reads the line.
        get_node("sprite").texture = res
public override void _Ready()
{
    var texture = (Texture)GD.Load("res://robi.png"); // Godot loads the Resource when it reads the line.
    var sprite = (Sprite)GetNode("sprite");
    sprite.Texture = texture;
}

Ви також можете використовувати ресурси preload (попередньо завантажені). На відміну від load, ця функція буде читати файл з диска і завантажувати його під час компіляції. Як результат, ви не можете викликати попереднє завантаження зі шляхом у змінній: вам потрібно використовувати постійний рядок (текст).

func _ready():
        var res = preload("res://robi.png") # Godot loads the resource at compile-time
        get_node("sprite").texture = res
// 'preload()' is unavailable in C Sharp.

Завантаження сцен

Сцени теж ресурси, але так і не так. Сцени, збережені на диску, - це ресурси типу PackedScene. Сцена упакована всередині ресурсу.

Щоб отримати екземпляр сцени, ви повинні використовувати метод PackedScene.instance().

func _on_shoot():
        var bullet = preload("res://bullet.tscn").instance()
        add_child(bullet)
private PackedScene _bulletScene = (PackedScene)GD.Load("res://bullet.tscn");

public void OnShoot()
{
    Node bullet = _bulletScene.Instance();
    AddChild(bullet);
}

Цей метод створює вузли в ієрархії сцени, налаштовує їх і повертає кореневий вузол сцени. Потім ви можете додати його в вигляді нащадка будь-якому іншому вузлу.

Цей підхід має ряд переваг. Оскільки функція PackedScene.instance() швидка, ви можете створювати нових ворогів, нові кулі, ефекти, тощо, не потребуючи кожного разу їх завантаження з диска. Пам’ятайте, що, як завжди, зображення, меші (моделі) тощо поділяються між екземплярами сцени.

Звільнення ресурсів

Якщо Resource більше не використовується, він автоматично звільняється (очищається). Оскільки в більшості випадків Ресурси містяться у Вузлах, коли ви звільняєте вузол, двигун звільняє всі ресурси, якими він володіє, якщо жоден інший вузол не використовує їх.

Створення власних ресурсів

Як і будь-який об'єкт у Godot, користувачі також можуть скриптувати ресурси. Скрипти ресурсів успадковують можливість вільного переходу між властивостями об'єкта та серіалізованим текстом, чи двійковими (бінарними) даними (*.tres, *.res). Вони також успадковують управління пам'яттю підрахунком посилань типу Reference.

Вони мають багато чітких переваг перед альтернативними структурами даних, такими як JSON, CSV, або файли TXT. Користувачі можуть імпортувати ці ресурси лише у вигляді Dictionary (JSON), або як File для розбору. Ресурси відрізняються їх нащадками Object, Reference та Resource:

  • Вони можуть визначати константи, тому константи з інших полів даних, або об'єктів, не потрібні.

  • Вони можуть визначати методи, включаючи методи встановлення/отримання (setter/getter) для властивостей. Це дозволяє абстрагувати та інкапсулювати основні дані. Якщо структура скрипту Resource потребує змін, то гра, що використовує Resource, не потребує їх.

  • Вони можуть визначати сигнали, тому Ресурси можуть реагувати на зміни в даних, якими вони керують.

  • Вони мають визначені властивості, тому користувачі впевнені на 100%, що їх дані будуть існувати.

  • Автосеріалізація та десеріалізація ресурсів є вбудованою функцією Godot Engine. Користувачам не потрібно застосовувати власну логіку для імпорту/експорту даних файлу ресурсу.

  • Ресурси можуть навіть використовувати вкладені ресурси рекурсивно, тобто користувачі можуть створювати ще більш складні структури даних.

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

  • Інспектор Godot Engine виводить і редагує файли ресурсів з коробки. Таким чином, користувачам часто не потрібно застосовувати власну логіку для візуалізації, чи редагування, своїх даних. Для цього двічі клацніть файл ресурсу на панелі Файлова система, або натисніть значок папки в інспекторі та відкрийте файл у діалоговому вікні.

  • Вони можуть розширювати інші типи ресурсів, окрім базового ресурсу.

Godot дозволяє легко створювати власні ресурси в Інспекторі.

  1. Створіть звичайний об'єкт Resource в інспекторі. Це навіть може бути тип, який походить від ресурса, якщо ваш скрипт розширює цей тип.

  2. Встановіть властивість script в Інспекторі для вашого скрипту.

Інспектор тепер покаже ваші власні властивості скрипту Resource. Якщо редагувати ці значення і економити ресурс, Інспектор також серіалізує власні властивості! Щоб зберегти ресурс у Інспекторі, натисніть меню інструментів Інспектора (вгорі праворуч) та виберіть "Зберегти", або "Зберегти як ...".

Якщо мова скрипту підтримує скриптові класи, то він упорядковує процес. Визначення імені лише для вашого скрипту додасть його до інспекторського діалогового вікна створення . Це автоматично додасть ваш скрипт до створеного вами об'єкта Resource.

Давайте подивимось кілька прикладів.

# bot_stats.gd
extends Resource
export(int) var health
export(Resource) var sub_resource
export(Array, String) var strings

# 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

# bot.gd
extends KinematicBody

export(Resource) var stats

func _ready():
    # Uses an implicit, duck-typed interface for any 'health'-compatible resources.
    if stats:
        print(stats.health) # Prints '10'.
// BotStats.cs
using System;
using Godot;

namespace ExampleProject {
    public class BotStats : Resource
    {
        [Export]
        public int Health { get; set; }

        [Export]
        public Resource SubResource { get; set; }

        [Export]
        public String[] Strings { get; set; }

        // Make sure that every parameter has a default value.
        // Otherwise, there will be problems with creating and editing
        // your resource via the inspector.
        public BotStats(int health = 0, Resource subResource = null, String[] strings = null)
        {
            Health = health;
            SubResource = subResource;
            Strings = strings ?? new String[0];
        }
    }
}

// Bot.cs
using System;
using Godot;

namespace ExampleProject {
    public class Bot : KinematicBody
    {
        [Export]
        public Resource Stats;

        public override void _Ready()
        {
            if (Stats != null && Stats is BotStats botStats) {
                GD.Print(botStats.Health); // Prints '10'.
            }
        }
    }
}

Примітка

Скрипти ресурсів схожі на ScriptableObjects в Unity. Інспектор забезпечує вбудовану підтримку користувацьких ресурсів. За бажанням користувачі можуть навіть розробити власні скрипти інструментів на основі контролю (Control) та комбінувати їх з EditorPlugin, щоб створити власні візуалізації та редактори для своїх даних.

DataTables (Таблиці даних) та CurveTables (Таблиці кривих) в Unreal Engine 4 також легко відтворити за допомогою скриптів Resource. DataTables - це рядок (строка, текст), відображений у користувацькій структурі, подібно до словника, що відображає рядок у вторинному скрипті користувацького ресурсу.

# 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)
using System;
using Godot;

public class BotStatsTable : Resource
{
    private Godot.Dictionary<String, BotStats> _stats = new Godot.Dictionary<String, BotStats>();

    public BotStatsTable()
    {
        _stats["GodotBot"] = new BotStats(10); // Creates instance with 10 health.
        _stats["DifferentBot"] = new BotStats(20); // A different one with 20 health.
        GD.Print(_stats);
    }
}

Замість того, щоб просто вписати значення Словника, можна також альтернативно…

  1. Імпортувати таблицю значень із електронної таблиці та згенерувати пари ключ-значення або…

  2. Створити візуалізацію в редакторі та створити простий плагін, який додасть його в Інспектор, коли ви відкриєте ці типи ресурсів.

CurveTables - це те саме, за винятком відображення масиву з десяткових значень, або об'єкта ресурсу Curve/Curve2D .

Попередження

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

У наведеному нижче прикладі Godot завантажує скрипт Node, бачить, що він не розширюється 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("res://my_res.tres", my_res)
using System;
using Godot;

public class MyNode : Node
{
    public class MyResource : Resource
    {
        [Export]
        public int Value { get; set; } = 5;
    }

    public override void _Ready()
    {
        var res = new MyResource();

        // This will NOT serialize the 'Value' property.
        ResourceSaver.Save("res://MyRes.tres", res);
    }
}