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...
Optimisation à l'aide de serveurs
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.
Bien sûr, il y a toujours des inconvénients :
Il y a un niveau de complexité supplémentaire.
Les performances sont inférieures à l'utilisation directe d'API simples.
Il n'est pas possible d'utiliser plusieurs threads pour les contrôler.
Il faut plus de mémoire.
Dans de nombreux cas, ce n'est pas vraiment un problème (Godot est très optimisé, et la plupart des opérations sont traitées avec des signaux, donc aucun sondage (polling) n'est nécessaire). Néanmoins, cela peut parfois l'être. Par exemple, le traitement de dizaines de milliers d'instances pour quelque chose qui doit être traité à chaque trame peut constituer un goulot d'étranglement.
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.
Pourtant, Godot est conçu pour contourner ce problème.
Voir aussi
Vous pouvez voir comment fonctionne l'utilisation de serveurs de bas niveau en action à l'aide du projet de démonstration Bullet Shower
Serveurs
Une des décisions les plus intéressantes pour Godot est le fait que le système de scènes est entièrement optionnel. Bien qu'il ne soit pas possible de compiler sans actuellement, il peut être complètement contourné.
Au cœur, Godot utilise le concept de Serveurs. Ce sont des API de très bas niveau pour contrôler le rendu, la physique, le son, etc. Le système de scène est construit sur eux et les utilise directement. Les serveurs les plus courants sont :
RenderingServer : gère tout ce qui concerne les graphismes.
PhysicsServer3D : gère tout ce qui concerne la physique 3D.
PhysicsServer2D : gère tout ce qui concerne la physique 2D.
AudioServer : gère tout ce qui concerne l'audio.
Explorez leurs API et vous vous rendrez compte que toutes les fonctions fournies sont des implémentations de bas niveau de tout ce que Godot vous permet de faire.
RIDs
La clé de l'utilisation des serveurs consiste à comprendre les objets Resource ID (RID). Ce sont des poignées opaques pour l'implémentation du serveur. Ils sont alloués et libérés manuellement. Presque toutes les fonctions des serveurs nécessitent des RID pour accéder à la ressource réelle.
La plupart des nœuds et ressources Godot contiennent ces RID à partir des serveurs en interne, et ils peuvent être obtenus avec différentes fonctions. En fait, tout ce qui hérite de Resource peut être directement converti en RID. Toutes les ressources ne contiennent pas de RID, cependant, dans de tels cas, le RID sera vide. Les ressources peuvent être transmises aux API de serveur en tant que RID.
Avertissement
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, or else both it and its RID will be erased.
Pour les nœuds, de nombreuses fonctions sont disponibles :
Pour CanvasItem, la méthode CanvasItem.get_canvas_item() retourne l'élément RID du canvas dans le serveur.
Pour CanvasLayer, la méthode CanvasLayer.get_canvas() renverra le RID du canvas dans le serveur.
Pour Viewport, la méthode Viewport.get_viewport_rid() renverra le RID du viewport dans le serveur.
Pour la 3D, la ressource World3D (que l'on peut obtenir dans les nœuds Viewport et Node3D) contient des fonctions permettant d'obtenir le RenderingServer Scenario, et le PhysicsServer Space. Cela permet de créer des objets 3D directement avec l'API du serveur et de les utiliser.
Pour la 2D, la ressource World2D (disponible dans les nœuds Viewport et CanvasItem) contient des fonctions pour obtenir le RenderingServer Canvas, et le Physics2DServer Space. Cela permet de créer des objets 2D directement avec l'API du serveur et de les utiliser.
The VisualInstance3D class, allows getting the scenario instance and instance base via the VisualInstance3D.get_instance() and VisualInstance3D.get_base() respectively.
Essayez d'explorer les nœuds et les ressources qui vous sont familiers et de trouver les fonctions pour obtenir les RIDs serveur.
Il n'est pas conseillé de contrôler les RID d'objets auxquels un nœud est déjà associé. Au lieu de cela, les fonctions du serveur devraient toujours être utilisées pour en créer et en contrôler de nouveaux et pour interagir avec ceux qui existent déjà.
Création d’un sprite
This is an example of how to create a sprite from code and move it using the low-level CanvasItem API.
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)
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, keep this reference.
_texture = ResourceLoader.Load<Texture2D>("res://MyTexture.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);
}
}
L'API Canvas Item du serveur vous permet d'y ajouter des primitives de dessin. Une fois ajoutés, elles ne peuvent pas être modifiées. L'élément doit être effacé et les primitives ré-ajoutées (ce n'est pas le cas pour définir la transformation, qui peut être effectuée autant de fois que souhaité).
Les primitifs sont éliminés de cette façon :
RenderingServer.canvas_item_clear(ci_rid)
RenderingServer.CanvasItemClear(ciRid);
Instanciation d'un Mesh dans l'espace 3D
Les API 3D sont différentes des API 2D, il faut donc utiliser l'API d'instanciation.
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)
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, keep the reference.
_mesh = ResourceLoader.Load<Mesh>("res://MyMesh.obj");
RenderingServer.InstanceSetBase(instance, _mesh.GetRid());
// Move the mesh around.
Transform3D xform = new Transform3D(Basis.Identity, new Vector3(20, 100, 0));
RenderingServer.InstanceSetTransform(instance, xform);
}
}
Créer un RigidBody en 2D et déplacer un sprite avec lui
Cela crée un RigidBody2D en utilisant l'API PhysicsServer2D, et déplace un CanvasItem lorsque le corps bouge.
# 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)
using Godot;
public partial class MyNode2D : Node2D
{
private Rid _canvasItem;
private void BodyMoved(PhysicsDirectBodyState2D state, int index)
{
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);
}
}
La version 3D devrait être très similaire, car les serveurs de physique 2D et 3D sont identiques (en utilisant respectivement RigidBody et PhysicsServer3D).
Obtention des données depuis les serveurs
Essayez de ne jamais demander d'informations au RenderingServer
, PhysicsServer2D
ou au PhysicsServer3D
en appelant des fonction sauf si vous savez ce que vous faites. Ces serveurs fonctionnent souvent de manière asynchrone pour des raisons de performance et le fait d'appeler une fonction qui renvoie une valeur les bloque et les force à traiter tout ce qui est en attente jusqu'à ce que la fonction soit finalement appelée. Cela réduira considérablement les performances si vous les appelez à chaque trame (et la raison ne sera pas évidente).
Pour cette raison, la plupart des API de ces serveurs sont conçues de telle sorte qu'il est impossible de demander des informations en retour, jusqu'à ce que ses données actuelles puissent être sauvegardées.