Input
O sistema de input da Turian desacopla os dispositivos físicos da lógica do jogo através de ações nomeadas. Você define as ações em um asset .inputactions — mapeamentos de teclado, mouse e controle (gamepad) todos apontam para o mesmo nome de ação — e seus scripts apenas perguntam "a ação jump foi pressionada?", nunca "a tecla Espaço está pressionada?".
Início rápido
1. Criar um asset de Input Actions
No Asset Browser, clique com o botão direito → Create → Input Actions. Isso gera um arquivo .inputactions padrão (formato ZON) na pasta selecionada.
Dê um duplo clique no arquivo para abrir o Input Actions Editor.
2. Definir suas ações
O editor mostra uma lista de ações. Cada ação possui:
- Name (Nome) — a string que seu script usará (ex:
jump,move,look). - Kind (Tipo) —
button(ligado/desligado),axis(1D −1…+1) ouvector(2D, x/y). - Roles (Papéis) —
Positive/Negativepara eixos (axis);Right/Left/Up/Downpara vetores (vector);Buttonspara botões (button). Cada papel suporta até 4 mapeamentos.
Para cada mapeamento, escolha um device (dispositivo) e um code (código):
| Dispositivo (Device) | Exemplos de código (Code) |
|---|---|
key |
a, space, left_shift, left_ctrl |
mouse |
right, left, middle |
gamepad_button |
south, east, left_shoulder, left_trigger |
gamepad_axis |
left_x, left_y, right_x, right_y, left_trigger, right_trigger |
Para gamepad_axis, a caixa de seleção + seleciona a direção positiva desse eixo (ex: analógico para a direita, gatilho pressionado). Desmarque para a direção negativa.
Clique em Save para salvar o arquivo ZON e recompilá-lo (re-cook) no projeto.
3. Empacotar o asset
O carregador do jogo descobre cada asset .inputactions no seu projeto automaticamente — sem necessidade de código. O asset é aplicado ao engine.Input compartilhado na inicialização.
Lendo ações em um script
Os scripts recebem o contexto engine.Frame através do método update. frame.input é um *const engine.Input:
const engine = @import("engine");
pub const PlayerController = struct {
pub const is_component = true;
speed: f32 = 5.0,
pub fn update(self: *@This(), frame: engine.Frame) void {
const input = frame.input;
const dt = frame.time.delta;
const t = frame.transform;
// Ação de botão — digital ligado/desligado (on/off)
if (input.wasPressed("jump")) {
// apenas borda de subida (rising edge)
}
// Ação de eixo — valor contínuo 1D
const throttle = input.axis("throttle"); // -1..+1
// Ação vetorial — 2D (ex: WASD / analógico esquerdo)
const move = input.vector("move"); // .x = direita, .y = frente
t.position.x += move.x * self.speed * dt;
t.position.z += move.y * self.speed * dt;
}
};
API de consulta de ações
| Método | Retorna | Significado |
|---|---|---|
isPressed(name) |
bool |
valor da ação ≥ limiar (pressionado) |
wasPressed(name) |
bool |
subiu acima do limiar neste frame |
axis(name) |
f32 |
valor 1D, −1…+1, com zona morta (deadzone) |
vector(name) |
engine.Vector2 |
valor 2D, limitado ao círculo unitário |
Todos os quatro funcionam da mesma forma, independentemente do mapeamento ser uma tecla, um botão do mouse ou analógico do controle — o tipo de ação apenas determina qual consulta faz sentido semântico.
Consultas brutas de dispositivos
Para interfaces gráficas (UI) ou código de debug que precisam de estados específicos de dispositivos, o frame.input também expõe consultas brutas (raw):
// Teclado
if (input.isKeyDown(.space)) { ... }
if (input.wasKeyPressed(.a)) { ... }
// Mouse
const delta = input.mouseDelta(); // pixels .x / .y neste frame
const wheel = input.wheelDelta(); // passos da roda do mouse neste frame
if (input.isMouseDown(.right)) { ... }
// Controle (Gamepad)
const lx = input.gamepadAxis(.left_x); // -1..+1
if (input.isGamepadButtonDown(.south)) { ... }
Exemplo: câmera livre (freefly camera)
O projeto de exemplo inclui o arquivo FreeflyCamera.zig, que controla uma câmera sem colisão (no-clip) tanto por teclado+mouse quanto por controle usando os mesmos nomes de ação:
move — WASD / analógico esquerdo
look_axis — analógico direito (analógico, independente de framerate)
ascend — Space / botão A / gatilho direito
descend — LShift / botão B / gatilho esquerdo
boost — LCtrl / botão superior esquerdo (left shoulder)
look — segurar botão direito do mouse para olhar com o delta do mouse
O script lê apenas ações — ele nunca menciona um código de tecla físico. Remapeie tudo em player-controls.inputactions sem alterar o FreeflyCamera.zig.
Reconfiguração em tempo de execução (rebinding)
O engine expõe duas funções para menus do tipo "pressione uma tecla para remapear" no jogo:
// Em uma atualização do menu de configurações:
if (frame.input.captureBinding()) |binding| {
// Usuário pressionou algo — aplicar
_ = frame.input.rebind("jump", .pos, 0, binding);
}
captureBinding() retorna a primeira tecla recém-pressionada, botão do mouse, botão do controle ou eixo do controle que cruzou o limiar de pressionamento. rebind substitui ou anexa um mapeamento pelo nome da ação, papel (.pos/.neg/.up/.down) e índice.
Persistência: salvar mapeamentos alterados entre sessões requer o sistema de configurações (issue #13, ainda não implementado). Acompanhe o progresso na issue #42.
Referência do engine.Frame
Cada hook do ciclo de vida que recebe um segundo parâmetro recebe o engine.Frame:
pub fn update(self: *@This(), frame: engine.Frame) void
pub fn start(self: *@This(), frame: engine.Frame) void
// awake, enable, disable, destroy também aceitam frame
| Campo | Tipo | Significado |
|---|---|---|
frame.time |
engine.Time |
delta, tempo decorrido, contador de frames |
frame.input |
*const engine.Input |
consultas de ações + estado bruto do dispositivo |
frame.transform |
*engine.Transform |
posição/rotação/escala deste objeto |
frame.objects |
[]engine.SceneNode |
todos os objetos na cena |
frame.services |
*engine.Services |
registro de serviços cadastrados pelo usuário |
Use frame.service(MyService) para uma busca tipada de ?*MyService em frame.services.
A assinatura legada
update(self, time: engine.Time)ainda é suportada para compatibilidade retroativa.