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.

Optimisation à l'aide de serveurs

Les moteurs comme Godot offrent une facilité d'utilisation accrue grâce à leurs constructions et leurs caractéristiques de haut niveau. La plupart d'entre elles sont accessibles et utilisées via le Système de scène. L'utilisation de nœuds et de ressources simplifie l'organisation des projets et la gestion des ressources dans les jeux complexes.

Bien sûr, il y a toujours des inconvénients :

  • There is an extra layer of complexity.

  • Performance is lower than when using simple APIs directly.

  • It is not possible to use multiple threads to control them.

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

Ce type de situation fait regretter aux programmeurs d'utiliser un moteur de jeu et leur fait souhaiter de revenir à une implémentation plus artisanale et de bas niveau du code du jeu.

Pourtant, Godot est conçu pour contourner ce problème.

Voir aussi

You can see how using low-level servers works in action using the Bullet Shower demo project

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 :

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 :

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)

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)

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)

Créer un RigidBody en 2D et déplacer un sprite avec lui

This creates a RigidBody2D using the PhysicsServer2D API, and moves a CanvasItem when the body moves.

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

The 3D version should be very similar, as 2D and 3D physics servers are identical (using RigidBody3D and PhysicsServer3D respectively).

Obtention des données depuis les serveurs

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

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.