Up to date

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

Godot-Schnittstellen

Oft benötigt man Skripte, deren Features auf anderen Objekten basieren. Dieser Prozess besteht aus 2 Teilen:

  1. Erfassen einer Referenz auf das Objekt, das vermutlich die Merkmale aufweist.

  2. Zugriff auf die Daten oder die Logik des Objekts.

Der Rest dieser Anleitung beschreibt die verschiedenen Möglichkeiten, all dies zu tun.

Objektreferenzen erfassen

Für alle Objects besteht die grundlegendste Art der Referenzierung darin, einen Verweis auf ein vorhandenes Objekt von einer anderen erfassten Instanz abzurufen.

var obj = node.object # Property access.
var obj = node.get_object() # Method access.

Das gleiche Prinzip gilt für RefCounted-Objekte. Obwohl Benutzer häufig auf Node und Resource auf diese Weise zugreifen, gibt es alternative Maßnahmen.

Anstelle von Property- oder Methodenzugriff können Ressourcen durch Ladezugriff erfasst werden.

# If you need an "export const var" (which doesn't exist), use a conditional
# setter for a tool script that checks if it's executing in the editor.
# The `@tool` annotation must be placed at the top of the script.
@tool

# Load resource during scene load.
var preres = preload(path)
# Load resource when program reaches statement.
var res = load(path)

# Note that users load scenes and scripts, by convention, with PascalCase
# names (like typenames), often into constants.
const MyScene = preload("my_scene.tscn") # Static load
const MyScript = preload("my_script.gd")

# This type's value varies, i.e. it is a variable, so it uses snake_case.
@export var script_type: Script

# Must configure from the editor, defaults to null.
@export var const_script: Script:
    set(value):
        if Engine.is_editor_hint():
            const_script = value

# Warn users if the value hasn't been set.
func _get_configuration_warnings():
    if not const_script:
        return ["Must initialize property 'const_script'."]

    return []

Beachten Sie Folgendes:

  1. Es gibt viele Möglichkeiten, wie eine Sprache solche Ressourcen laden kann.

  2. Beim Entwurf des Datenzugriffs von Objekten sollten Sie nicht vergessen, dass man Ressourcen auch als Referenzen weitergeben kann.

  3. Beachten Sie, dass beim Laden einer Ressource die von der Engine verwaltete zwischengespeicherte Ressourceninstanz abgerufen wird. Um ein neues Objekt zu erhalten, muss man eine vorhandene Referenz duplizieren oder eine von Grund auf mit new () instanziieren.

Nodes haben ebenfalls einen alternativen Zugangspunkt: den Szenenbaum.

extends Node

# Slow.
func dynamic_lookup_with_dynamic_nodepath():
    print(get_node("Child"))

# Faster. GDScript only.
func dynamic_lookup_with_cached_nodepath():
    print($Child)

# Fastest. Doesn't break if node moves later.
# Note that `@onready` annotation is GDScript-only.
# Other languages must do...
#     var child
#     func _ready():
#         child = get_node("Child")
@onready var child = $Child
func lookup_and_cache_for_future_access():
    print(child)

# Fastest. Doesn't break if node is moved in the Scene tree dock.
# Node must be selected in the inspector as it's an exported property.
@export var child: Node
func lookup_and_cache_for_future_access():
    print(child)

# Delegate reference assignment to an external source.
# Con: need to perform a validation check.
# Pro: node makes no requirements of its external structure.
#      'prop' can come from anywhere.
var prop
func call_me_after_prop_is_initialized_by_parent():
    # Validate prop in one of three ways.

    # Fail with no notification.
    if not prop:
        return

    # Fail with an error message.
    if not prop:
        printerr("'prop' wasn't initialized")
        return

    # Fail and terminate.
    # NOTE: Scripts run from a release export template don't run `assert`s.
    assert(prop, "'prop' wasn't initialized")

# Use an autoload.
# Dangerous for typical nodes, but useful for true singleton nodes
# that manage their own data and don't interfere with other objects.
func reference_a_global_autoloaded_variable():
    print(globals)
    print(globals.prop)
    print(globals.my_getter())

Zugriff auf Daten oder Logik aus einem Objekt

Die Skript-API von Godot ist "duck-typed". Dies bedeutet, dass Godot, wenn ein Skript eine Operation ausführt, nicht anhand des Typs sicherstellt, ob es die Operation unterstützt. Stattdessen wird überprüft, ob das Objekt die einzelne Methode implementiert.

Beispielsweise hat die Klasse CanvasItem eine Eigenschaft visible. Alle Eigenschaften, die für die Skript-API sichtbar gemacht werden, sind tatsächlich ein Setter- und Getter-Paar, das an einen Namen gebunden ist. Wenn man versuchen würde, auf CanvasItem.visible zuzugreifen, würde Godot die folgenden Überprüfungen der Reihe nach durchführen:

  • Wenn an das Objekt ein Skript angehängt ist, wird versucht, die Property über das Skript zu setzen. Dies eröffnet Skripten die Möglichkeit, eine für ein Basisobjekt definierte Eigenschaft zu überschreiben, indem die Setter-Methode für die Eigenschaft überschrieben wird.

  • Wenn das Skript nicht über die Eigenschaft verfügt, führt es in der ClassDB eine HashMap-Suche nach der "sichtbaren" Eigenschaft für die CanvasItem-Klasse und alle ihre geerbten Typen durch. Wenn es gefunden wird, wird der damit verbundene Setter oder Getter aufgerufen. Weitere Informationen zu HashMaps finden Sie in der Dokumentation Dateneinstellungen.

  • Wenn es nicht gefunden wird, wird explizit überprüft, ob der Benutzer auf die Propertys "script" oder "meta" zugreifen möchte.

  • Wenn nicht, wird im CanvasItem und seinen geerbten Typen nach einer _set / _get-Implementierung (abhängig von der Art des Zugriffs) gesucht. Diese Methoden können Logik ausführen, die den Eindruck erweckt, dass das Objekt eine Property hat. Dies ist auch bei der Methode _get_property_list der Fall.

    • Beachten Sie, dass dies auch bei nicht-legalen Symbolnamen geschieht, z.B. bei Namen, die mit einer Ziffer beginnen oder die einen Schrägstrich enthalten.

Infolgedessen kann dieses "duck-typed"-System eine Eigenschaft entweder im Skript, in der Klasse des Objekts oder in einer beliebigen Klasse, die das Objekt erbt, finden, jedoch nur für Dinge, die Object erweitern.

Godot bietet eine Vielzahl von Optionen für die Durchführung von Laufzeitprüfungen für diese Zugriffe:

  • Zugriff auf Propertys über "Duck-Typing". Dabei handelt es sich um Property-Checks (wie oben beschrieben). Wenn die Operation vom Objekt nicht unterstützt wird, wird die Ausführung angehalten.

    # All Objects have duck-typed get, set, and call wrapper methods.
    get_parent().set("visible", false)
    
    # Using a symbol accessor, rather than a string in the method call,
    # will implicitly call the `set` method which, in turn, calls the
    # setter method bound to the property through the property lookup
    # sequence.
    get_parent().visible = false
    
    # Note that if one defines a _set and _get that describe a property's
    # existence, but the property isn't recognized in any _get_property_list
    # method, then the set() and get() methods will work, but the symbol
    # access will claim it can't find the property.
    
  • Eine Methodenprüfung. Im Fall von CanvasItem.visible kann man wie bei jeder anderen Methode auf die Methoden set_visible und is_visible zugreifen.

    var child = get_child(0)
    
    # Dynamic lookup.
    child.call("set_visible", false)
    
    # Symbol-based dynamic lookup.
    # GDScript aliases this into a 'call' method behind the scenes.
    child.set_visible(false)
    
    # Dynamic lookup, checks for method existence first.
    if child.has_method("set_visible"):
        child.set_visible(false)
    
    # Cast check, followed by dynamic lookup.
    # Useful when you make multiple "safe" calls knowing that the class
    # implements them all. No need for repeated checks.
    # Tricky if one executes a cast check for a user-defined type as it
    # forces more dependencies.
    if child is CanvasItem:
        child.set_visible(false)
        child.show_on_top = true
    
    # If one does not wish to fail these checks without notifying users,
    # one can use an assert instead. These will trigger runtime errors
    # immediately if not true.
    assert(child.has_method("set_visible"))
    assert(child.is_in_group("offer"))
    assert(child is CanvasItem)
    
    # Can also use object labels to imply an interface, i.e. assume it
    # implements certain methods.
    # There are two types, both of which only exist for Nodes: Names and
    # Groups.
    
    # Assuming...
    # A "Quest" object exists and 1) that it can "complete" or "fail" and
    # that it will have text available before and after each state...
    
    # 1. Use a name.
    var quest = $Quest
    print(quest.text)
    quest.complete() # or quest.fail()
    print(quest.text) # implied new text content
    
    # 2. Use a group.
    for a_child in get_children():
        if a_child.is_in_group("quest"):
            print(quest.text)
            quest.complete() # or quest.fail()
            print(quest.text) # implied new text content
    
    # Note that these interfaces are project-specific conventions the team
    # defines (which means documentation! But maybe worth it?).
    # Any script that conforms to the documented "interface" of the name or
    # group can fill in for it.
    
  • Auslagerung des Zugriffs auf ein Callable. Dies kann in Fällen nützlich sein, in denen man ein Höchstmaß an Freiheit von Abhängigkeiten benötigt. In diesem Fall ist man auf einen externen Kontext angewiesen, um die Methode einzurichten.

# child.gd
extends Node
var fn = null

func my_method():
    if fn:
        fn.call()

# parent.gd
extends Node

@onready var child = $Child

func _ready():
    child.fn = print_me
    child.my_method()

func print_me():
    print(name)

Diese Strategien tragen zu Godots flexiblem Design bei. Die Benutzer verfügen so über eine Vielzahl von Tools , um ihre spezifischen Anforderungen zu erfüllen.