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...
Ottimizzazione tramite i server
Motori come Godot offrono una maggiore facilità d'uso grazie alle loro strutture e funzionalità di alto livello. La maggior parte di esse è accessibile e usabile tramite il sistema di scene. L'utilizzo di nodi e risorse semplifica l'organizzazione dei progetto e la gestione delle risorse in giochi complessi.
There are several drawbacks to this:
C'è un ulteriore livello di complessità.
Le prestazioni sono inferiori rispetto all'utilizzo diretto di API semplici.
Non è possibile utilizzare più thread per controllarli.
Serve più memoria.
Nella maggior parte dei casi, questo non è un vero problema. Godot è ben ottimizzato e la maggior parte delle operazioni è gestita tramite segnali, il che significa che non c'è bisogno di polling. Tuttavia, a volte, si desidera estrarre prestazioni migliori dall'hardware quando altre vie di ottimizzazione sono state esaurite. Ad esempio, gestire decine di migliaia di istanze per qualcosa che bisogna elaborare a ogni frame può rappresentare un collo di bottiglia.
Questo tipo di situazione fanno pentire ai programmatori di utilizzare un motore di gioco e desiderare tornare a un'implementazione più artigianale e di basso livello del codice di gioco.
Tuttavia, Godot è progettato per aggirare questo problema.
Vedi anche
Puoi vedere come funziona in azione l'utilizzo di server di basso livello attraverso il progetto demo Bullet Shower.
Server
Una delle scelte di progettazione più interessanti di Godot è il fatto che l'intero sistema di scene è opzionale. Pur non essendo possibile disabilitarlo in fase di compilazione, può essere completamente aggirato.
In sostanza, Godot si basa sul concetto di Server. Essi sono API di basso livello per controllare il rendering, la fisica, l'audio, ecc. Il sistema di scene è costruito sopra di essi e li utilizza direttamente. I server più comuni sono:
RenderingServer: gestisce tutto ciò che riguarda la grafica.
PhysicsServer3D: gestisce tutto ciò che riguarda la fisica 3D.
PhysicsServer2D: gestisce tutto ciò che riguarda la fisica 2D.
AudioServer: gestisce tutto ciò che riguarda l'audio.
Esplorando le loro API, ti renderai conto che tutte le funzioni fornite sono implementazioni di basso livello di tutto ciò che Godot ti permette di fare usando i nodi.
RID
La cosa importante per utilizzare i server è comprendere gli oggetti Resource ID (RID). Questi sono identificatori opachi per l'implementazione dei server. Vengono allocati e liberati manualmente. Quasi tutte le funzioni dei server richiedono i RID per accedere alla risorsa effettiva.
La maggior parte dei nodi e delle risorse di Godot contengono questi RID provenienti dai server internamente, ed è possibile ottenerli tramite diverse funzioni. Infatti, qualsiasi cosa che eredita da Resource si può convertire direttamente in un RID. Non tutte le risorse, tuttavia, contengono un RID: in questi casi, il RID sarà vuoto. La risorsa si può quindi passare alle API dei server sotto forma di un RID.
Avvertimento
Le risorse sono conteggiate per riferimento (vedi RefCounted), e i riferimenti al RID di una risorsa non sono conteggiati quando si determina se la risorsa è ancora in uso. Assicurati di mantenere un riferimento alla risorsa al di fuori del server. Se no, sia la risorsa sia il suo RID saranno eliminati.
Per i nodi, sono disponibili numerose funzioni:
Per CanvasItem, il metodo CanvasItem.get_canvas_item() restituirà l'RID dell'elemento canvas presente nel server.
Per CanvasLayer, il metodo CanvasLayer.get_canvas() restituirà il RID del canvas nel server.
Per Viewport, il metodo Viewport.get_viewport_rid() restituirà il RID della viewport nel server.
Per il 2D, la risorsa World2D (ottenibile nei nodi Viewport e CanvasItem) contiene funzioni per ottenere il Canvas del RenderingServer e lo Spazio del PhysicsServer2D. Ciò consente di creare oggetti 2D direttamente con l'API del server e di utilizzarli.
Per il 3D, la risorsa World3D (ottenibile nei nodi Viewport e Node3D) contiene funzioni per ottenere lo Scenario del RenderingServer e lo Spazio del PhysicsServer. Ciò consente di creare oggetti 3D direttamente con l'API del server e di utilizzarli.
La classe VisualInstance3D consente di ottenere l'istanza dello scenario e la base dell'istanza tramite i metodi VisualInstance3D.get_instance() e VisualInstance3D.get_base() rispettivamente.
Prova a esplorare i nodi e le risorse che conosci e trova le funzioni per ottenere gli RID del server.
Si sconsiglia di controllare gli RID da oggetti che hanno già un nodo associato. È preferibile sempre utilizzare le funzioni del server per creare e controllare nuovi RID e per interagire con quelli esistenti.
Creare uno sprite
Questo è un esempio di come creare uno sprite tramite codice e spostarlo tramite l'API di basso livello di CanvasItem.
Nota
Quando si creano elementi canvas utilizzando il RenderingServer, si dovrebbe ripristinare l'interpolazione della fisica sul primo frame tramite RenderingServer.canvas_item_reset_physics_interpolation(). Ciò garantisce la corretta sincronizzazione tra i sistemi di rendering e di fisica.
Se non si fa, l'elemento canvas potrebbe sembrare teletrasportarsi quando viene caricata la scena, anziché apparire direttamente nella posizione prevista.
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);
}
}
L'API di CanvasItem nel server consente di aggiungerci primitive di disegno. Una volta aggiunte, non si possono modificare. L'elemento deve essere svuotato e le primitive riaggiunte. Questo non accade impostando la trasformazione, cosa che si può fare tutte le volte che si desidera.
Le primitive si eliminano in questo modo:
RenderingServer.canvas_item_clear(ci_rid)
RenderingServer.CanvasItemClear(ciRid);
Istanziare una Mesh nello spazio 3D
Le API 3D sono diverse da quelle 2D, quindi è necessario utilizzare l'API di istanziazione.
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);
}
}
Creare un RigidBody 2D e spostare uno sprite con esso
Questo crea un RigidBody2D tramite l'API del PhysicsServer2D e sposta un CanvasItem quando il corpo si muove.
# 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.
// ...
}
}
La versione 3D dovrebbe essere molto simile, poiché i server di fisica 2D e 3D sono identici (utilizzando rispettivamente RigidBody3D e PhysicsServer3D).
Ottenere dati dai server
Tenta di non richiedere mai informazioni dal RenderingServer, PhysicsServer2D o PhysicsServer3D chiamando funzioni, a meno che tu non sappia cosa stai facendo. Questi server spesso girano in modo asincrono per motivi di prestazioni e chiamare qualsiasi funzione che restituisca un valore li bloccherà, costringendoli a elaborare tutto ciò che è in sospeso fino a quando la funzione non verrà effettivamente chiamata. Questo ridurrà drasticamente le prestazioni se vengono chiamati a ogni frame (e non sarà subito evidente il perché).
Per questo motivo, la maggior parte delle API in tali server sono progettate in modo tale da non consentire nemmeno di richiedere informazioni, finché non sono effettivamente dati che si possono salvare.