Work in progress

The content of this page was not yet updated for Godot 4.2 and may be outdated. If you know how to improve this page or you can confirm that it's up to date, feel free to open a pull request.

Optimierung mit Servern

Engines wie Godot bieten dank ihrer High-Level-Konstrukte und -Funktionen eine größere Benutzerfreundlichkeit. Die meisten von ihnen werden über das Scene System angesprochen und verwendet. Die Verwendung von Nodes und Ressourcen vereinfacht die Projektorganisation und das Asset-Management in komplexen Spielen.

Natürlich gibt es immer auch Nachteile:

  • Es gibt eine zusätzliche Komplexitätsebene.

  • Die Performance ist geringer als bei der direkten Verwendung einfacher APIs.

  • Es ist nicht möglich, mehrere Threads zu verwenden, um sie zu kontrollieren.

  • Mehr Speicher wird benötigt.

In vielen Fällen stellt dies kein wirkliches Problem dar (Godot ist sehr optimiert und die meisten Prozesse werden an Signale weitergegeben, sodass keine Polling erforderlich ist). Dennoch kann es manchmal zu einem Bottleneck führen, etwa, wenn zehntausende Instanzen in einem Frame verarbeitet werden müssen.

Diese Art von Situation lässt Programmierer bedauern, dass sie eine Spiele-Engine verwenden, und sie wünschen sich, sie könnten zu einer handgefertigten Low-Level-Implementierung von Spielecode zurückkehren.

Dennoch ist Godot darauf ausgelegt, dieses Problem zu umgehen.

Siehe auch

Wie die Verwendung von Low-Level-Servern in der Praxis funktioniert, können Sie anhand des Bullet Shower-Demoprojekts sehen

Server

Eine der interessantesten Designentscheidungen für Godot ist die Tatsache, dass das gesamte Szenensystem optional ist. Während es derzeit nicht möglich ist, es herauszukompilieren, kann es vollständig umgangen werden.

Im Kern verwendet Godot das Konzept der Server. Es handelt sich um Low-Level-APIs zur Steuerung von Rendering, Physik, Sound usw. Das Szenensystem ist darauf aufgebaut und verwendet sie direkt. Die gängigsten Server sind:

Wenn Sie nur die APIs untersuchen, werden Sie feststellen, dass alle bereitgestellten Funktionen Low-Level-Implementierungen von allem sind, was Godot Ihnen ermöglicht.

RIDs

Der Schlüssel zur Verwendung von Servern liegt im Verständnis von Resource-ID-Objekten (RID). Diese stellen undurchsichtige Referenzen auf die jeweilige Server-Implementierung dar. Sie werden manuell alloziert und freigegeben. Fast jede Funktion in den Servern erfordert RIDs, um auf die tatsächliche Ressource zugreifen zu können.

Den meisten Godot-Nodes und -Ressourcen wird diese RIDs aus den Servern intern zugewiesen und sie können mit verschiedenen Funktionen abgerufen werden. Tatsächlich kann alles, was von Resource erbt, direkt in eine RID umgewandelt werden. Nicht alle Ressourcen enthalten jedoch eine RID; in solchen Fällen ist sie leer. Die Ressource kann dann per RID an Server-APIs übergeben werden.

Warnung

Ressourcen werden per Reference Counting verwaltet (siehe RefCounted), und Referenzen auf die RID einer Ressource werden nicht gezählt, wenn ermittelt wird, ob die Ressource noch in Gebrauch ist. Stellen Sie sicher, dass Sie eine Referenz auf die Ressource außerhalb des Servers aufbewahren, da sonst sowohl die Ressource als auch ihre RID gelöscht werden.

Für Nodes stehen viele Funktionen zur Verfügung:

  • Für CanvasItem gibt die Methode CanvasItem.get_canvas_item() die RID des Canvas-Elements auf dem Server zurück.

  • Für CanvasLayer gibt die Methode CanvasLayer.get_canvas() die Canvas-RID auf dem Server zurück.

  • Bei Viewports gibt die Methode Viewport.get_viewport_rid() die RID des Viewports auf dem Server zurück.

  • Für 3D enthält die Ressource World3D (erhältlich in den Nodes Viewport und Node3D) Funktionen, um das RenderingServer Scenario und den PhysicsServer Space zu erhalten. Dies ermöglicht es, 3D-Objekte direkt mit der Server-API zu erstellen und sie zu verwenden.

  • Für 2D enthält die Ressource World2D (erhältlich in den Nodes Viewport und CanvasItem) Funktionen zum Abrufen des RenderingServer Canvas und des Physics2DServer Space. Dies ermöglicht es, 2D-Objekte direkt mit der Server-API zu erstellen und sie zu verwenden.

  • Die Klasse VisualInstance3D ermöglicht es, das Szenario instance und instance base über die VisualInstance3D.get_instance() bzw. VisualInstance3D.get_base() zu erhalten.

Versuchen Sie, die Ihnen bekannten Nodes und Ressourcen zu erkunden und die Funktionen zu finden, um die Server RIDs zu erhalten.

Es wird nicht empfohlen, RIDs von Objekten zu steuern, denen bereits ein Node zugeordnet ist. Stattdessen sollten immer Serverfunktionen verwendet werden, um neue RIDs zu erstellen und zu steuern und mit den vorhandenen RIDs zu interagieren.

Ein Sprite erstellen

Dies ist ein Beispiel dafür, wie man ein Sprite über Code erstellt und es mit der Low-Level-API CanvasItem verschiebt.

extends Node2D


# RenderingServer expects references to be kept around.
var texture


func _ready():
    # Create a canvas item, child of this node.
    var ci_rid = RenderingServer.canvas_item_create()
    # Make this node the parent.
    RenderingServer.canvas_item_set_parent(ci_rid, get_canvas_item())
    # Draw a texture on it.
    # Remember, keep this reference.
    texture = load("res://my_texture.png")
    # Add it, centered.
    RenderingServer.canvas_item_add_texture_rect(ci_rid, Rect2(texture.get_size() / 2, texture.get_size()), texture)
    # Add the item, rotated 45 degrees and translated.
    var xform = Transform2D().rotated(deg_to_rad(45)).translated(Vector2(20, 30))
    RenderingServer.canvas_item_set_transform(ci_rid, xform)

Die Canvas Item-API des Servers ermöglicht das Hinzufügen von Zeichenprimitiven. Einmal hinzugefügt, können sie nicht mehr geändert werden. Das Element muss gelöscht und die Primitive erneut hinzugefügt werden (dies gilt nicht für die Einstellung der Transformation, die beliebig oft vorgenommen werden kann).

Primitive werden folgendermaßen gelöscht:

RenderingServer.canvas_item_clear(ci_rid)

Instanziieren eines Meshs in den 3D-Raum

Die 3D-APIs unterscheiden sich von den 2D-APIs, daher muss die Instanziierungs-API verwendet werden.

extends Node3D


# RenderingServer expects references to be kept around.
var mesh


func _ready():
    # Create a visual instance (for 3D).
    var instance = RenderingServer.instance_create()
    # Set the scenario from the world, this ensures it
    # appears with the same objects as the scene.
    var scenario = get_world_3d().scenario
    RenderingServer.instance_set_scenario(instance, scenario)
    # Add a mesh to it.
    # Remember, keep the reference.
    mesh = load("res://mymesh.obj")
    RenderingServer.instance_set_base(instance, mesh)
    # Move the mesh around.
    var xform = Transform3D(Basis(), Vector3(20, 100, 0))
    RenderingServer.instance_set_transform(instance, xform)

Erstellen eines 2D-RigidBody und Verschieben eines Sprites damit

Dies erzeugt einen RigidBody2D unter Verwendung der PhysicsServer2D API und bewegt ein CanvasItem, wenn sich der Body bewegt.

# Physics2DServer expects references to be kept around.
var body
var shape


func _body_moved(state, index):
    # Created your own canvas item, use it here.
    RenderingServer.canvas_item_set_transform(canvas_item, state.transform)


func _ready():
    # Create the body.
    body = Physics2DServer.body_create()
    Physics2DServer.body_set_mode(body, Physics2DServer.BODY_MODE_RIGID)
    # Add a shape.
    shape = Physics2DServer.rectangle_shape_create()
    # Set rectangle extents.
    Physics2DServer.shape_set_data(shape, Vector2(10, 10))
    # Make sure to keep the shape reference!
    Physics2DServer.body_add_shape(body, shape)
    # Set space, so it collides in the same space as current scene.
    Physics2DServer.body_set_space(body, get_world_2d().space)
    # Move initial position.
    Physics2DServer.body_set_state(body, Physics2DServer.BODY_STATE_TRANSFORM, Transform2D(0, Vector2(10, 20)))
    # Add the transform callback, when body moves
    # The last parameter is optional, can be used as index
    # if you have many bodies and a single callback.
    Physics2DServer.body_set_force_integration_callback(body, self, "_body_moved", 0)

Die 3D-Version sollte sehr ähnlich sein, da 2D- und 3D-Physikserver identisch sind (mit RigidBody3D bzw. PhysicsServer3D).

Abrufen der Daten von den Servern

Versuchen Sie, niemals Informationen von RenderingServer, PhysicsServer2D oder PhysicsServer3D durch den Aufruf von Funktionen anzufordern, es sei denn, Sie wissen, was Sie tun. Diese Server laufen aus Performance-Gründen oft asynchron und der Aufruf einer Funktion, die einen Wert zurückgibt, wird sie abwürgen und sie zwingen, alles zu verarbeiten, was ansteht, bis die Funktion tatsächlich aufgerufen wird. Dies wird die Performance stark beeinträchtigen, wenn Sie sie bei jedem Frame aufrufen (und es wird nicht offensichtlich sein, warum).

Aus diesem Grund sind die meisten APIs in solchen Servern so konzipiert, dass es nicht einmal möglich ist, Informationen zurückzufordern, solange es sich nicht um tatsächliche Daten handelt, die gespeichert werden können.