Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
Optimierung mit Servern
Engines like Godot provide increased ease of use thanks to their high-level constructs and features. Most of them are accessed and used via the scene system. Using nodes and resources simplifies project organization and asset management in complex games.
There are several drawbacks to this:
Es gibt eine zusätzliche Komplexitätsebene.
Die Performance ist geringer als bei der direkten Verwendung einfacher APIs.
It is not possible to use multiple threads to control them.
Mehr Speicher wird benötigt.
In most cases, this is not really a problem. Godot is well-optimized, and most operations are handled with signals, which means no polling is required. Still, sometimes, we want to extract better performance from the hardware when other avenues of optimization have been exhausted. For example, dealing with tens of thousands of instances for something that needs to be processed every frame can be a bottleneck.
This type of situation makes programmers regret they are using a game engine and wish they could go back to a more handcrafted, low-level implementation of game code.
Dennoch ist Godot darauf ausgelegt, dieses Problem zu umgehen.
Siehe auch
You can see how using low-level servers works in action using the Bullet Shower demo project.
Server
One of the most interesting design decisions for Godot is the fact that the whole scene system is optional. While it is not possible to compile it out, it can be completely bypassed.
At the core, Godot uses the concept of Servers. They are low-level APIs to control rendering, physics, sound, etc. The scene system is built on top of them and uses them directly. The most common servers are:
RenderingServer: Handles everything related to graphics.
PhysicsServer3D: Handles everything related to 3D physics.
PhysicsServer2D: Handles everything related to 2D physics.
AudioServer: Handles everything related to audio.
Explore their APIs, and you will realize that all the functions provided are low-level implementations of everything Godot allows you to do using nodes.
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
Resources are reference-counted (see RefCounted), and references to a resource's RID are not counted when determining whether the resource is still in use. Make sure to keep a reference to the resource outside the server. Otherwise, both the resource and its RID will be erased.
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.
For 2D, the World2D resource (obtainable in the Viewport and CanvasItem nodes) contains functions to get the RenderingServer Canvas, and the PhysicsServer2D Space. This allows creating 2D objects directly with the server API and using them.
For 3D, the World3D resource (obtainable in the Viewport and Node3D nodes) contains functions to get the RenderingServer Scenario, and the PhysicsServer Space. This allows creating 3D objects directly with the server API and using them.
The VisualInstance3D class, allows getting the scenario instance and instance base via the VisualInstance3D.get_instance() and VisualInstance3D.get_base() respectively.
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
This is an example of how to create a sprite from code and move it using the low-level CanvasItem API.
Bemerkung
When creating canvas items using the RenderingServer, you should reset physics interpolation on the first frame using RenderingServer.canvas_item_reset_physics_interpolation(). This ensures proper synchronization between the rendering and physics systems.
If this is not done, the canvas item may appear to teleport in when the scene is loaded, rather than appearing directly at its intended location.
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 to 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)
# Reset physics interpolation for this item.
RenderingServer.canvas_item_reset_physics_interpolation(ci_rid)
public partial class MyNode2D : Node2D
{
// RenderingServer expects references to be kept around.
private Texture2D _texture;
public override void _Ready()
{
// Create a canvas item, child of this node.
Rid ciRid = RenderingServer.CanvasItemCreate();
// Make this node the parent.
RenderingServer.CanvasItemSetParent(ciRid, GetCanvasItem());
// Draw a texture on it.
// Remember to keep this reference.
_texture = ResourceLoader.Load<Texture2D>("res://my_texture.png");
// Add it, centered.
RenderingServer.CanvasItemAddTextureRect(ciRid, new Rect2(-_texture.GetSize() / 2, _texture.GetSize()), _texture.GetRid());
// Add the item, rotated 45 degrees and translated.
Transform2D xform = Transform2D.Identity.Rotated(Mathf.DegToRad(45)).Translated(new Vector2(20, 30));
RenderingServer.CanvasItemSetTransform(ciRid, xform);
// Reset physics interpolation for this item.
RenderingServer.CanvasItemResetPhysicsInterpolation(ciRid);
}
}
The Canvas Item API in the server allows you to add draw primitives to it. Once added, they can't be modified. The Item needs to be cleared and the primitives re-added. This is not the case for setting the transform, which can be done as many times as desired.
Primitive werden folgendermaßen gelöscht:
RenderingServer.canvas_item_clear(ci_rid)
RenderingServer.CanvasItemClear(ciRid);
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 to keep this reference.
mesh = load("res://my_mesh.obj")
RenderingServer.instance_set_base(instance, mesh)
# Move the mesh around.
var xform = Transform3D(Basis(), Vector3(2, 3, 0))
RenderingServer.instance_set_transform(instance, xform)
public partial class MyNode3D : Node3D
{
// RenderingServer expects references to be kept around.
private Mesh _mesh;
public override void _Ready()
{
// Create a visual instance (for 3D).
Rid instance = RenderingServer.InstanceCreate();
// Set the scenario from the world. This ensures it
// appears with the same objects as the scene.
Rid scenario = GetWorld3D().Scenario;
RenderingServer.InstanceSetScenario(instance, scenario);
// Add a mesh to it.
// Remember to keep this reference.
_mesh = ResourceLoader.Load<Mesh>("res://my_mesh.obj");
RenderingServer.InstanceSetBase(instance, _mesh.GetRid());
// Move the mesh around.
Transform3D xform = new Transform3D(Basis.Identity, new Vector3(2, 3, 0));
RenderingServer.InstanceSetTransform(instance, xform);
}
}
Erstellen eines 2D-RigidBody und Verschieben eines Sprites damit
This creates a RigidBody2D using the PhysicsServer2D API, and moves a CanvasItem when the body moves.
# PhysicsServer2D expects references to be kept around.
var body
var shape
func _body_moved(state, index):
# Created your own canvas item; use it here.
# `ci_rid` from the sprite example above needs to be moved to a
# member variable (instead of within `_ready()`) so it can be referenced here.
RenderingServer.canvas_item_set_transform(ci_rid, state.transform)
func _ready():
# Create the body.
body = PhysicsServer2D.body_create()
PhysicsServer2D.body_set_mode(body, PhysicsServer2D.BODY_MODE_RIGID)
# Add a shape.
shape = PhysicsServer2D.rectangle_shape_create()
# Set rectangle extents.
PhysicsServer2D.shape_set_data(shape, Vector2(10, 10))
# Make sure to keep the shape reference!
PhysicsServer2D.body_add_shape(body, shape)
# Set space, so it collides in the same space as current scene.
PhysicsServer2D.body_set_space(body, get_world_2d().space)
# Move initial position.
PhysicsServer2D.body_set_state(body, PhysicsServer2D.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.
PhysicsServer2D.body_set_force_integration_callback(body, self, "_body_moved", 0)
# Also create a sprite using RenderingServer here.
# See the section above on creating a sprite.
# ...
using Godot;
public partial class MyNode2D : Node2D
{
private Rid _canvasItem;
private void BodyMoved(PhysicsDirectBodyState2D state, int index)
{
// Created your own canvas item; use it here.
// `ciRid` from the sprite example above needs to be moved to a
// member variable (instead of within `_Ready()`) so it can be referenced here.
RenderingServer.CanvasItemSetTransform(_canvasItem, state.Transform);
}
public override void _Ready()
{
// Create the body.
var body = PhysicsServer2D.BodyCreate();
PhysicsServer2D.BodySetMode(body, PhysicsServer2D.BodyMode.Rigid);
// Add a shape.
var shape = PhysicsServer2D.RectangleShapeCreate();
// Set rectangle extents.
PhysicsServer2D.ShapeSetData(shape, new Vector2(10, 10));
// Make sure to keep the shape reference!
PhysicsServer2D.BodyAddShape(body, shape);
// Set space, so it collides in the same space as current scene.
PhysicsServer2D.BodySetSpace(body, GetWorld2D().Space);
// Move initial position.
PhysicsServer2D.BodySetState(body, PhysicsServer2D.BodyState.Transform, new Transform2D(0, new 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.
PhysicsServer2D.BodySetForceIntegrationCallback(body, new Callable(this, MethodName.BodyMoved), 0);
// Also create a sprite using RenderingServer here.
// See the section above on creating a sprite.
// ...
}
}
The 3D version should be very similar, as the 2D and 3D physics servers are identical (using RigidBody3D and PhysicsServer3D respectively).
Abrufen der Daten von den Servern
Try to never request any information from RenderingServer, PhysicsServer2D, or PhysicsServer3D by calling functions unless you know what you are doing. These servers will often run asynchronously for performance and calling any function that returns a value will stall them and force them to process anything pending until the function is actually called. This will severely decrease performance if you call them every frame (and it won't be obvious why).
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.