Up to date

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

Ressourcen

Nodes und Ressourcen

Bisher haben wir uns auf die Node-Klasse in Godot konzentriert, da dies die Klasse ist, die Sie zum Programmieren von Verhalten verwenden. Die meisten Funktionen der Engine sind darauf angewiesen. Ein anderer Datentyp ist genauso wichtig: Resource.

Nodes geben Ihnen Funktionalität: Sie zeichnen Sprites, 3D-Modelle, simulieren Physik, ordnen Benutzeroberflächen usw. an. Ressourcen sind Datencontainer. Sie machen nichts alleine: Stattdessen verwenden Nodes die in Ressourcen enthaltenen Daten.

Alles, was Godot speichert oder von der Festplatte lädt, ist eine Ressource. Sei es eine Szene (eine .tscn oder eine .scn Datei), ein Bild, ein Skript... Hier sind einige Resource-Beispiele:

Wenn die Engine eine Ressource von der Festplatte lädt, wird sie nur einmal geladen. Wenn sich bereits eine Kopie dieser Ressource im Speicher befindet, wird beim erneuten Laden der Ressource jedes Mal dieselbe Kopie zurückgegeben. Da Ressourcen nur Daten enthalten, müssen sie nicht dupliziert werden.

Jedes Objekt, sei es ein Node oder eine Ressource, kann Propertys exportieren. Es gibt viele Typen von Propertys wie String, Integer, Vector2 etc., und jeder dieser Typen kann zu einer Ressource werden. Dies bedeutet, dass sowohl Nodes als auch Ressourcen weitere Ressourcen als Propertys enthalten können:

../../_images/nodes_resources.webp

Extern im Vergleich Built-in

Es gibt zwei Möglichkeiten, Ressourcen zu speichern. Diese können sein:

  1. Extern zu einer Szene, die als einzelne Dateien auf der Festplatte gespeichert sind.

  2. Built-in, gespeichert in der .tscn oder .scn Datei, an die sie angehängt sind.

Um genauer zu sein: Hier ist ein Texture2D in einem Sprite2D-Node:

../../_images/spriteprop.webp

Wenn Sie auf die Ressourcenvorschau klicken, können Sie die Propertys der Ressource anzeigen.

../../_images/resourcerobi.webp

Die path-Property sagt uns, woher die Ressource stammt. In diesem Fall stammt sie aus einem PNG-Bild namens robi.png. Wenn die Ressource aus einer Datei wie dieser stammt, handelt es sich um eine externe Ressource. Wenn Sie den Pfad löschen oder dieser Pfad leer ist, wird sie zu einer Built-in-Ressource.

Der Wechsel zwischen Built-in- und externen Ressourcen erfolgt beim Speichern der Szene. Wenn Sie im obigen Beispiel den Pfad "res://robi.png " löschen und speichern, speichert Godot das Bild in der .tscn Szenen-Datei.

Bemerkung

Selbst wenn Sie eine Built-in-Ressource speichern, lädt die Engine bei einer mehrmaligen Instanziierung einer Szene nur eine einzige Kopie davon.

Laden von Ressourcen via Code

Es gibt zwei Möglichkeiten, Ressourcen aus dem Code zu laden. Erstens können Sie jederzeit die Funktion load() verwenden:

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

Sie können Ressourcen auch vorladen. Im Gegensatz zu load liest diese Funktion die Datei von der Festplatte und lädt sie zur Kompilierzeit. Daher können Sie Preload nicht mit einem variablen Pfad aufrufen: Sie müssen einen konstanten String verwenden.

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

Laden von Szenen

Szenen sind auch Ressourcen, aber es gibt einen Haken. Auf der Festplatte gespeicherte Szenen sind Ressourcen vom Typ PackedScene. Die Szene ist in eine Ressource gepackt.

Um eine Instanz der Szene zu erhalten, müssen Sie die Methode PackedScene.instantiate() verwenden.

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

Diese Methode erstellt die Nodes in der Hierarchie der Szene, konfiguriert sie und gibt den Root-Node der Szene zurück. Sie können es dann als Child-Element eines anderen Nodes hinzufügen.

Dieser Ansatz hat mehrere Vorteile. Da die Funktion PackedScene.instantiate() schnell ist, können Sie neue Feinde, Geschosse, Effekte etc. erstellen, ohne sie jedes Mal neu von der Festplatte laden zu müssen. Denken Sie daran, dass Bilder, Meshes etc. wie immer von allen Szeneninstanzen gemeinsam genutzt werden.

Freigeben von Ressourcen

Wenn eine Ressource nicht mehr benutzt wird, gibt sie sich automatisch selbst frei. Da Ressourcen in den meisten Fällen in Nodes enthalten sind, gibt die Engine beim Freigeben eines Nodes auch alle Ressourcen frei, die ihm gehören, wenn kein anderer Node sie verwendet.

Erstellen Sie Ihre eigenen Ressourcen

Wie jedes Objekt in Godot können Benutzer auch Ressourcen skripten. Ressourcenskripte erben die Fähigkeit, frei zwischen Objekt-Propertys und serialisiertem Text oder Binärdaten (*.tres, *.res) zu übersetzen. Sie erben auch die Verwaltung des Reference-Counting-Speichers vom Referenztypen.

Dies bringt viele Vorteile gegenüber alternativen Datenstrukturen wie JSON, CSV oder benutzerdefinierten TXT-Dateien mit sich. Benutzer können diese Assets nur als Dictionary (JSON) oder als FileAccess zum Parsen importieren. Das Besondere an Ressourcen ist die Vererbung der Funktionen Object, RefCounted und Resource:

  • Sie können Konstanten definieren, so dass Konstanten aus anderen Datenfeldern oder Objekten nicht benötigt werden.

  • Sie können Methoden definieren, einschließlich Setter/Getter-Methoden für Propertys. Dies ermöglicht die Abstraktion und Kapselung der zugrunde liegenden Daten. Wenn die Struktur des Ressourcenskripts geändert werden muss, braucht sich das Spiel, das die Ressource verwendet, nicht zu ändern.

  • Sie können Signale definieren, sodass Ressourcen Reaktionen auf Änderungen in den von ihnen verwalteten Daten auslösen können.

  • Sie verfügen über definierte Propertys, sodass Benutzer zu 100% wissen, dass ihre Daten vorhanden sind.

  • Die automatische Serialisierung und Deserialisierung von Ressourcen ist eine Built-in-Funktion der Godot Engine. Benutzer müssen keine benutzerdefinierte Logik implementieren, um die Daten einer Ressourcendatei zu importieren oder zu exportieren.

  • Ressourcen können sogar Teilressourcen rekursiv serialisieren, sodass Benutzer noch ausgefeiltere Datenstrukturen entwerfen können.

  • Benutzer können Ressourcen als Versionsverwaltungs-freundliche Textdateien (*. tres) speichern. Beim Exportieren eines Spiels serialisiert Godot Ressourcendateien als Binärdateien (*.res), um Geschwindigkeit und Komprimierung zu erhöhen.

  • Der Inspektor der Godot Engine rendert die Ressourcendateien sofort. Daher müssen Benutzer häufig keine eigene Logik implementieren, um ihre Daten zu visualisieren oder zu bearbeiten. Doppelklicken Sie dazu auf die Ressourcendatei im Dateisystemdock oder klicken im Inspektor auf das Ordner-Icon und öffnen die Datei im Dialogfeld.

  • Sie können andere Ressourcentypen als nur die Basisressource erweitern.

Godot erleichtert es, im Inspektor benutzerdefinierte Ressourcen zu erstellen.

  1. Erstellen Sie im Inspektor ein einfaches Ressourcenobjekt. Dies kann sogar ein Typ sein, der von Ressource ableitet, solange Ihr Skript diesen Typ erweitert.

  2. Stellen Sie die script-Property im Inspektor als Ihr Skript ein.

Der Inspektor zeigt jetzt die benutzerdefinierten Propertys Ihres Ressourcen-Skripts an. Wenn Sie diese Werte bearbeiten und die Ressource speichern, serialisiert der Inspektor auch die benutzerdefinierten Propertys! Um eine Ressource aus dem Inspektor zu speichern, klicken Sie auf das Menü des Inspektors (oben rechts) und wählen "Speichern" oder "Speichern unter ...".

Wenn die Sprache des Skripts Skriptklassen unterstützt, wird der Prozess dadurch vereinfacht. Wenn Sie nur einen Namen für Ihr Skript festlegen, wird es zum Erstellungsdialog des Inspektors hinzugefügt. Dadurch wird Ihr Skript automatisch zu dem von Ihnen erstellten Ressourcenobjekt hinzugefügt.

Lassen Sie uns einige Beispiele sehen. Erstellen Sie eine Ressource und nennen Sie sie bot_stats. Sie sollte in Ihrem Datei-Tab mit dem vollen Namen bot_stats.tres erscheinen. Ohne ein Skript ist sie nutzlos, also fügen wir ein paar Daten und Logik hinzu! Hängen Sie ein Skript mit dem Namen bot_stats.gd an (oder erstellen Sie einfach ein neues Skript und ziehen Sie es dann dorthin).

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

Erstellen Sie nun ein CharacterBody3D, nennen Sie es Bot, und fügen Sie das folgende Skript hinzu:

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"

Wählen Sie nun den CharacterBody3D-Node, den wir bot genannt haben, und ziehen Sie die Ressource bot_stats.tres auf den Inspector. Sie sollte 10 ausgeben! Natürlich kann dieses Setup auch für fortgeschrittenere Funktionen als diese verwendet werden, aber solange Sie wirklich verstehen, wie das alles funktioniert, sollten Sie alles andere im Zusammenhang mit Ressourcen herausfinden können.

Bemerkung

Ressourcenskripte ähneln den ScriptableObjects von Unity. Der Inspektor bietet eine integrierte Unterstützung für benutzerdefinierte Ressourcen an. Auf Wunsch können Benutzer sogar eigene, auf Controls basierende Tool-Skripte erstellen und diese mit einem EditorPlugin kombinieren, um benutzerdefinierte Visualisierungen und Editoren für ihre Daten zu erstellen.

Die DataTables und CurveTables der Unreal-Engine lassen sich ebenfalls leicht mit Ressourcenskripten nachbilden. DataTables sind ein String, der auf eine benutzerdefinierte Struktur abgebildet wird, ähnlich wie ein Dictionary, das einen String auf ein sekundäres benutzerdefiniertes Ressourcenskript abbildet.

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

Anstatt die Dictionary-Werte zu inlinen, könnte man alternativ auch:

  1. Eine Wertetabelle aus einer Tabellenkalkulation importieren und diese Key-Value-Paare generieren.

  2. Eine Visualisierung im Editor entwerfen und ein Plugin erstellen, das sie dem Inspektor hinzufügt, wenn Sie diese Art von Ressourcen öffnen.

CurveTables sind dasselbe wie DataTables, außer dass sie einem Array von Float-Werten oder einem Ressourcenobjekt, wie Curve/Curve2D zugeordnet sind.

Warnung

Beachten Sie, dass Ressourcendateien (*.tres /*.res) den Pfad des von Ihnen verwendeten Skripts in der Datei speichern. Wenn sie geladen sind, werden sie dieses Skript als Erweiterung ihres Typs abrufen und laden. Das bedeutet, dass der Versuch, eine Unterklasse zuzuordnen, d.h. eine innere Klasse eines Skripts (z.B. das Schlüsselwort class 'in GDScript), nicht funktioniert. Godot wird die benutzerdefinierten Propertys der Skriptunterklasse nicht ordnungsgemäß serialisieren.

Im folgenden Beispiel würde Godot das Node-Skript laden, prüfen, dass es Resource nicht erweitert, und dann feststellen, dass das Skript nicht für das Ressourcenobjekt geladen wurde, da die Typen nicht kompatibel sind.

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")