Recursos

Nós e recursos

Up to this tutorial, we focused on the Node class in Godot as that’s the one you use to code behavior and most of the engine’s features rely on it. There is another datatype that is just as important: Resource.

Nodes give you functionality: they draw sprites, 3D models, simulate physics, arrange user interfaces, etc. Resources are data containers. They don’t do anything on their own: instead, nodes use the data contained in resources.

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: Texture, Script, Mesh, Animation, AudioStream, Font, Translation.

When the engine loads a resource from disk, it only loads it once. If a copy of that resource is already in memory, trying to load the resource again will return the same copy every time. As resources only contain data, there is no need to duplicate them.

Every object, be it a Node or a Resource, can export properties. There are many types of Properties, like String, integer, Vector2, etc., and any of these types can become a resource. This means that both nodes and resources can contain resources as properties:

../../_images/nodes_resources.png

Externo vs embutido

There are two ways to save resources. They can be:

  1. External to a scene, saved on the disk as individual files.
  2. Built-in, saved inside the .tscn or the .scn file they’re attached to.

Para ser mais específico, aqui está uma Textura em um nó Sprite:

../../_images/spriteprop.png

Clicking the resource preview allows us to view and edit the resource’s properties.

../../_images/resourcerobi.png

The path property tells us where the resource comes from. In this case, it comes from a PNG image called robi.png. When the resource comes from a file like this, it is an external resource. If you erase the path or this path is empty, it becomes a built-in resource.

The switch between built-in and external resources happens when you save the scene. In the example above, if you erase the path "res://robi.png" and save, Godot will save the image inside the .tscn scene file.

Nota

Even if you save a built-in resource, when you instance a scene multiple times, the engine will only load one copy of it.

Carregando recursos a partir do código

Existem duas maneiras de carregar recursos por código. Primeiro, você pode usar a função load() a qualquer momento:

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;
}

Você pode também precarregar recursos. Diferente de load, essa função vai ler o arquivo do disco e carregá-lo ao compilar. Como resultado, você não pode chamar preload com um caminho variável: você preciso usar uma string constante.

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.

Carregando cenas

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.instance() method.

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);
}

This method creates the nodes in the scene’s hierarchy, configures them, and returns the root node of the scene. You can then add it as a child of any other node.

The approach has several advantages. As the PackedScene.instance() 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.

Liberando recursos

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.

Creating your own resources

Como qualquer Objeto em Godot, os usuários também podem criar scripts em Recursos. Scripts de recursos herdam a capacidade de traduzir livremente entre propriedades de objetos e texto serializado ou dados binários (/.tres, /.res). Eles também herdam o gerenciamento de memória de contagem de referência do tipo de referência.

Isto apresenta várias vantagens distintas sobre estruturas de dados alternativas como JSON, CSV ou arquivos TXT customizados. Os usuários só podem importar estes assets como um Dictionary (JSON) ou como um File. O que diferencia os recursos são suas características de herança de Object, Reference e Resource:

  • Eles podem definir constantes, portanto, constantes de outros campos ou objetos de dados não são necessárias.
  • Eles podem definir métodos, incluindo métodos setter / getter para propriedades. Isso permite abstração e encapsulamento dos dados subjacentes. Se a estrutura do script Resource precisar ser alterada, o jogo usando o recurso também não precisará ser alterado.
  • They can define signals, so Resources can trigger responses to changes in the data they manage.
  • Eles definiram propriedades, portanto, os usuários sabem 100% que seus dados existirão.
  • A serialização automática e desserialização do recurso é um recurso interno do Godot Engine. Os usuários não precisam implementar a lógica personalizada para importar / exportar os dados de um arquivo de recurso.
  • Os recursos podem até serializar os sub-recursos recursivamente, o que significa que os usuários podem criar estruturas de dados ainda mais sofisticadas.
  • Os usuários podem salvar recursos como arquivos de texto amigáveis ao controle de versões (*. Tres). Ao exportar um jogo, Godot serializa os arquivos de recursos como arquivos binários (*. Res) para maior velocidade e compactação.
  • O Inspetor do Godot Engine processa e edita arquivos de recursos prontos para uso. Dessa forma, os usuários geralmente não precisam implementar lógica personalizada para visualizar ou editar seus dados. Para fazer isso, clique duas vezes no arquivo de recursos na estação FileSystem ou clique no ícone da pasta no Inspetor e abra o arquivo na caixa de diálogo.
  • Eles podem estender ** outros ** tipos de recursos além do recurso base.

Aviso

Resources and Dictionaries are both passed by reference, but only Resources are reference-counted. This means that if a Dictionary is passed between objects and the first object is deleted, all other objects’ references to the Dictionary will be invalidated. Conversely, Resources will not be freed from memory until all the objects are deleted.

extends Node

class MyObject:
    extends Object
    var dict = {}

func _ready():
    var obj1 = MyObject.new()
    var obj2 = MyObject.new()
    obj1.dict.greeting = "hello"
    obj2.dict = obj1.dict             # 'obj2.dict' now references 'obj1's Dictionary.
    obj1.free()                       # 'obj1' is freed and the Dictionary too!
    print(obj2.dict.greeting)         # Error! 'greeting' index accessed on null instance!

    # To avoid this, we must manually duplicate the Dictionary.
    obj1 = MyObject.new()
    obj1.dict.greeting = "hello"
    obj2.dict = obj1.dict.duplicate() # Now we are passing a copy, not a reference.
    obj1.free()                       # obj2's Dictionary still exists.
    print(obj2.dict.greeting)         # Prints 'hello'.

Godot facilita a criação de recursos personalizados no Inspetor.

  1. Crie um objeto de recurso simples no Inspetor. Isso pode até ser um tipo que deriva Resource, desde que seu script esteja estendendo esse tipo.
  2. Defina a propriedade `` script`` no Inspector como seu script.

O inspetor agora exibirá as propriedades personalizadas do script de recursos. Se você editar esses valores e salvar o recurso, o inspetor serializará as propriedades personalizadas também! Para salvar um recurso do Inspetor, clique no menu de ferramentas do Inspetor (canto superior direito) e selecione “Salvar” ou “Salvar como …”.

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.

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

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; }

        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'.
            }
        }
    }
}

Nota

Scripts de recursos são semelhantes aos ScriptableObjects da Unity. O Inspector fornece suporte interno para recursos personalizados. Se desejado, os usuários podem até criar seus próprios scripts de ferramentas baseados em Control e combiná-los com um: ref: EditorPlugin <class_EditorPlugin> para criar visualizações e editores personalizados para seus dados.

O DataTables e o CurveTables do Unreal Engine 4 também são fáceis de recriar com scripts de recursos. DataTables são uma String mapeada para uma estrutura personalizada, semelhante a um dicionário que mapeia uma String para um script de recurso personalizado secundário.

# 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);
    }
}

Em vez de apenas inlining os valores do dicionário, um poderia também, alternativamente …

  1. Importe uma tabela de valores de uma planilha e gere esses pares de valores-chave ou …
  2. Projete uma visualização no editor e crie um plug-in simples que a adicione ao Inspector ao abrir esses tipos de recursos.

CurveTables are the same thing, except mapped to an Array of float values or a Curve/Curve2D resource object.

Aviso

Esteja ciente de que os arquivos de recursos (*. Tres / *. Res) armazenarão o caminho do script que eles usam no arquivo. Quando carregados, eles buscarão e carregarão esse script como uma extensão do tipo. Isso significa que tentar atribuir uma subclasse, ou seja, uma classe interna de um script (como usar a palavra-chave `` class`` no GDScript) não funcionará. Godot não serializará as propriedades customizadas na subclasse do script adequadamente.

No exemplo abaixo, Godot carregaria o script `` Node``, veria que ele não estende `` Resource`` e determinará que o script falhou ao carregar o objeto Resource, pois os tipos são incompatíveis.

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 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);
    }
}