Recursos

Nós e recursos

Até agora, focamos muito na classe de Nós na Godot, já que a maioria dos comportamentos e funcionalidades do motor são implementados através deles. Existe outro tipo de dados que é igualmente importante: Recurso.

Nós dão funcionalidade: eles desenham sprites, modelos 3D, simulam física, organizam a interface do usuário, etc. Recursos são containers de dados. Eles não fazem nada sozinhos: ao invés disso, os nós usam os dados contidos em recursos.

Qualquer coisa que a Godot salva ou carrega do disco é um recurso, seja ele uma cena (um arquivo .tscn or .scn), uma imagem, um script… Exemplos de Recursos são: Textura, Script, Malha, Animação, Fluxo de áudio, Fonte, Tradução

Quando um recurso é carregado do disco, é carregado só uma vez. Se houver uma cópia desse recurso já carregado na memória, tentar carregar o recurso novamente retornará a mesma cópia várias vezes. Como recursos são apenas contêineres de dados, não é necessário duplicá-los.

Todos os objetos em Godot (Nós, Recursos ou qualquer outra coisa) podem exportar propriedades. Elas podem ser de vários tipos (como texto, número inteiro, Vector2, etc), e qualquer desses tipos pode virar um recurso. Isso significa que os nós e os recursos podem conter recursos como propriedades:

../../_images/nodes_resources.png

Externo vs embutido

Existem duas maneiras de salvar recursos. Pode ser:

  1. Externo a uma cena, salvo no disco como arquivos individuais.
  2. Embutido, salvo dentro do arquivo .tscn ou .scn ao qual estão anexados.

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

../../_images/spriteprop.png

Clicar no recurso permite que você veja e edite suas propriedades.

../../_images/resourcerobi.png

A propriedade caminho nos diz de onde o recurso vem. Nesse caso ele vem de uma imagem PNG chamada robi.png. Quando o recurso vem de um arquivo como esse, ele é um recurso externo. Se você apagar o caminho ou esse caminho estiver vazio, ele se transforma em um recurso embutido.

A mudança entre recursos embutidos e externos acontece quando você salva a cena. No exemplo acima, se você apaga o caminho "res://robi.png" e salva, a Godot vai salvar a imagem dentro do arquivo de cena .tscn.

Nota

Mesmo se você salvar um recurso interno, quando você instancia uma cena várias vezes, o motor carregará apenas uma cópia dela.

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

Cenas também são recursos, mas há um detalhe. Cenas salvas no disco são recursos do tipo PackedScene. Isso significa que a cena é compactada dentro de um recurso.

Para obter uma instância da cena, o método PackedScene.instance() deve ser usado.

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

Esse método cria os nós na hierarquia da cena, configura-os, e retorna o nó raiz da cena, que pode ser adicionado como filho de qualquer outro nó.

Essa abordagem tem várias vantagens. Como a função PackedScene.instance() é bastante rápida, novos inimigos, tiros, efeitos, etc. podem ser adicionados ou removidos rapidamente, sem ter que carregá-los novamente do disco toda vez. É importante lembrar que, como sempre, imagens, malhas, etc. são todos compartilhados entre as instâncias da cena.

Liberando recursos

Quando um ``Recurso ``não estiver mais em uso, ele será automaticamente liberado. Como, na maioria dos casos, os recursos estão contidos em nós, scripts ou outros recursos, quando um nó é removido ou liberado, todos os recursos filhos são liberados também se nenhum outro nó os usa.

Criando seus próprios recursos

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.
  • Eles podem definir sinais, para que os Recursos possam acionar respostas a alterações nos dados que eles gerenciam.
  • 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.

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 …”.

Se a linguagem do script oferecer suporte a classes de script, então o processo fica mais simples. Definir um nome para o seu script já irá adicioná-lo ao diálogo de criação do Inspetor. Isso adicionará automaticamente seu script ao objeto Resource criado por você.

Vamos ver alguns exemplos.

# 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 são a mesma coisa, mas mapeadas em uma matriz de valores float ou um objeto de recurso :ref: Curve <class_Curve> /: ref: Curve2D <class_Curve2D>.

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