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...
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:
RenderingServer: Cuida de tudo relacionado a gráficos.
PhysicsServer3D: Cuida de tudo relacionado à física 3D.
PhysicsServer2D: Cuida de tudo relacionado à física 2D.
AudioServer: Cuida de tudo relacionado ao áudio.
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:
Para CanvasItem, o método CanvasItem.get_canvas_item() retornará o RID do canvas item no servidor.
Para CanvasLayer, o método CanvasLayer.get_canvas() retornará o RID do canvas no servidor.
Para Viewport, o método Viewport.get_viewport_rid() retornará o RID do viewport no servidor.
Para 2D, o recurso World2D (obtível nos nós Viewport e CanvasItem) contém funções para obter o RenderingServer Canvas e o PhysicsServer2D Space. Isso permite criar objetos 2D diretamente com a API do servidor e usá-los.
Para 3D, o recurso World3D (obtível nos nós Viewport e Node3D) contém funções para obter o RenderingServer Scenario e o PhysicsServer Space. Isso permite criar objetos 3D diretamente com a API do servidor e usá-los.
A classe VisualInstance3D permite obter a instância do cenário e a base da instância por meio de VisualInstance3D.get_instance() e VisualInstance3D.get_base(), respectivamente.
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)
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);
}
}
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)
RenderingServer.CanvasItemClear(ciRid);
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)
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);
}
}
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.
# ...
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.
// ...
}
}
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.