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.

Otimização usando Servers

Engines como o Godot fornecem maior facilidade de uso graças aos seus recursos e construções de alto nível. A maioria deles é acessada e usada por meio do sistema de cena. O uso de nós e recursos simplifica a organização do projeto e o gerenciamento de assets em jogos complexos.

Existem várias desvantagens nisso:

  • Existe uma camada extra de complexidade.

  • O desempenho é menor do que ao usar APIs simples diretamente.

  • Não é possível usar múltiplas threads para controlá-los.

  • Mais memória é necessária.

Na maioria dos casos, isso não é realmente um problema. O Godot é bem otimizado e a maioria das operações é realizada com sinais, o que significa que nenhuma varredura (polling) é necessária. Ainda assim, às vezes, queremos extrair um melhor desempenho do hardware quando outras vias de otimização já foram esgotadas. Por exemplo, lidar com dezenas de milhares de instâncias para algo que precisa ser processado a cada quadro pode ser um gargalo.

Esse tipo de situação faz os programadores se arrependerem de estar usando uma engine de jogo e desejarem poder voltar a uma implementação de código de jogo de mais baixo nível e feita à mão.

Ainda assim, o Godot foi projetado para contornar esse problema.

Ver também

Você pode ver como o uso de servidores de baixo nível funciona em ação usando o projeto de demonstração Bullet Shower.

Servidores

Uma das decisões de design mais interessantes do Godot é o fato de que todo o sistema de cenas é opcional. Embora não seja possível removê-lo na compilação, ele pode ser completamente ignorado.

No núcleo, o Godot usa o conceito de Servers. Eles são APIs de baixo nível para controlar renderização, física, som, etc. O sistema de cenas é construído sobre eles e os usa diretamente. Os servers mais comuns são:

Explore suas APIs e você perceberá que todas as funções fornecidas são implementações de baixo nível de tudo o que o Godot permite que você faça usando nós.

RIDs

A chave para usar os servers é entender os objetos de ID de Recurso (RID). Eles são referências opacas para a implementação do servidor. Eles são alocados e liberados manualmente. Quase todas as funções nos servers exigem RIDs para acessar o recurso real.

A maioria dos nós e recursos do Godot contém esses RIDs dos servers internamente, e eles podem ser obtidos com diferentes funções. Na verdade, qualquer coisa que herde de Resource pode ser convertida diretamente para um RID. Nem todos os recursos contêm um RID, no entanto: nesses casos, o RID estará vazio. O recurso pode então ser passado para as APIs do servidor como um RID.

Aviso

Os recursos têm contagem de referências (veja RefCounted), e as referências ao RID de um recurso não são contadas ao determinar se o recurso ainda está em uso. Certifique-se de manter uma referência ao recurso fora do servidor. Caso contrário, tanto o recurso quanto seu RID serão apagados.

Para nós, existem muitas funções disponíveis:

Tente explorar os nós e recursos com os quais você está familiarizado e encontre as funções para obter os RIDs do servidor.

Não é aconselhável controlar RIDs a partir de objetos que já possuem um nó associado. Em vez disso, as funções do servidor devem sempre ser usadas para criar e controlar novos objetos e interagir com os já existentes.

Criando um sprite

Este é um exemplo de como criar um sprite a partir do código e movê-lo usando a API de baixo nível do CanvasItem.

Nota

Ao criar canvas items usando o RenderingServer, você deve resetar a interpolação de física no primeiro quadro usando RenderingServer.canvas_item_reset_physics_interpolation(). Isso garante a sincronização adequada entre os sistemas de renderização e física.

Se isso não for feito, o canvas item pode parecer teletransportar-se para a tela quando a cena é carregada, em vez de aparecer diretamente no local pretendido.

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)

A API de Canvas Item no servidor permite que você adicione primitivas de desenho a ele. Uma vez adicionadas, elas não podem ser modificadas. O Item precisa ser limpo e as primitivas adicionadas novamente. Esse não é o caso para definir a transformação (transform), o que pode ser feito quantas vezes desejar.

As primitivas são limpas desta forma:

RenderingServer.canvas_item_clear(ci_rid)

Instanciando uma Mesh no espaço 3D

As APIs 3D são diferentes das 2D, portanto, a API de instanciação deve ser usada.

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)

Criando um RigidBody 2D e movendo um sprite com ele

Isso cria um RigidBody2D usando a API do PhysicsServer2D e move um CanvasItem quando o corpo se move.

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

A versão 3D deve ser muito semelhante, já que os servidores de física 2D e 3D são idênticos (usando RigidBody3D e PhysicsServer3D, respectivamente).

Obtendo dados dos servidores

Tente nunca solicitar qualquer informação do RenderingServer, PhysicsServer2D ou PhysicsServer3D chamando funções, a menos que saiba o que está fazendo. Esses servidores geralmente rodam de forma assíncrona por desempenho, e chamar qualquer função que retorne um valor irá paralisá-los (stall) e forçá-los a processar tudo o que estiver pendente até que a função seja realmente chamada. Isso reduzirá drasticamente o desempenho se você chamá-los a cada quadro (e não será óbvio o porquê).

Por causa disso, a maioria das APIs nesses servidores é projetada de forma que nem seja possível solicitar informações de volta, a menos que sejam dados reais que possam ser salvos.