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.

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:

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)

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)

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)

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

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.