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...
Usando InputEvent
O que é isso?
O gerenciamento de entrada normalmente é complexo, não importa o SO ou a plataforma. Para facilitar isto um pouco, um tipo especial embutido é fornecido, InputEvent. Este tipo de dado pode ser configurado para conter vários tipos de eventos de entrada. Eventos de entrada viajam pela engine e podem ser recebidos em vários locais, dependendo da finalidade.
Eis um exemplo rápido, que fecha seu jogo se a tecla Escape for pressionada:
func _unhandled_input(event):
if event is InputEventKey:
if event.pressed and event.keycode == KEY_ESCAPE:
get_tree().quit()
public override void _UnhandledInput(InputEvent @event)
{
if (@event is InputEventKey eventKey)
{
if (eventKey.Pressed && eventKey.Keycode == Key.Escape)
{
GetTree().Quit();
}
}
}
No entanto, é mais limpo e flexível usar o recurso fornecido InputMap, que o permite definir ações de entrada e atribuir-lhes diferentes teclas. Desta forma você pode definir várias teclas para a mesma ação (por exemplo, a tecla Esc do teclado e o botão Start em um controle). Você então pode alterar mais facilmente este mapeamento nas configurações do projeto sem atualizar seu código, e até fazer um recurso por cima para permitir seu jogo alterar o mapeamento de teclas em tempo de execução!
Você pode configurar seu InputMap em Projeto > Configurações do Projeto > Mapa de Entrada e usar essas ações como esta:
func _process(delta):
if Input.is_action_pressed("ui_right"):
# Move right.
public override void _Process(double delta)
{
if (Input.IsActionPressed("ui_right"))
{
// Move right.
}
}
Como funciona?
Todo evento de entrada origina-se do usuário/jogador (embora seja possível gerar um InputEvent e enviá-lo de volta à engine, o que é útil para gestos). O DisplayServer de cada plataforma lerá os eventos do sistema operacional e, em seguida, os repassará para a Window raiz.
A Viewport da janela faz bastante coisa com a entrada recebida, em ordem:
Se o Viewport estiver incorporando Janelas, o Viewport tenta interpretar o evento em sua capacidade como um Gerenciador de Janelas (por exemplo, para redimensionar ou mover Janelas).
Em seguida, se uma Janela incorporada estiver focada, o evento é enviado para essa Janela e processado no Viewport da Janela e depois tratado como resolvido. Se nenhuma Janela incorporada estiver focada, o evento é enviado para os nós do viewport atual na seguinte ordem.
Antes de tudo, a função padrão Node._input() será chamada em qualquer nó que a redefina (e não tenha desativado o processamento de entrada com Node.set_process_input()). Se qualquer função consumir o evento, ela poderá chamar Viewport.set_input_as_handled(), e o evento não se espalhará mais. Isso garante que você possa filtrar todos os eventos de interesse, mesmo antes da GUI. Para entrada de jogabilidade, Node._unhandled_input() geralmente é uma escolha melhor, porque permite que a GUI intercepte os eventos.
Em segundo lugar, ela tentará enviar a entrada para a GUI e ver se algum controle pode recebê-la. Se sim, o Control será chamado por meio da função virtual Control._gui_input() e o sinal "gui_input" será emitido (esta função pode ser reimplementada por script ao herdar dela). Se o controle quiser "consumir" o evento, ele chamará Control.accept_event() e o evento não se espalhará mais. Use la propriedade Control.mouse_filter para controlar se um Control é notificado de eventos de mouse por meio do callback Control._gui_input(), e se esses eventos são propagados adiante.
Se até o momento ninguém tiver consumido o evento, o callback Node._shortcut_input() será chamado, caso tenha sido sobrescrito (e não desativado com Node.set_process_shortcut_input()). Isso ocorre apenas para InputEventKey, InputEventShortcut e InputEventJoypadButton. Se alguma função consumir o evento, ela pode chamar Viewport.set_input_as_handled(), e o evento não se propagará mais. O callback de entrada de atalho é ideal para lidar com eventos destinados a atalhos.
Se até o momento ninguém tiver consumido o evento, o callback Node._unhandled_key_input() será chamado, caso tenha sido sobrescrito (e não desativado com Node.set_process_unhandled_key_input()). Isso ocorre apenas se o evento for um InputEventKey. Se alguma função consumir o evento, ela pode chamar Viewport.set_input_as_handled(), e o evento não se propagará mais. O callback de entrada de teclado não tratada é ideal para eventos de teclado.
Se até agora ninguém consumiu o evento, o callback Node._unhandled_input() será chamado se for redefinido (e não estiver desativado com Node.set_process_unhandled_input()). Se qualquer função consumir o evento, ela poderá chamar Viewport.set_input_as_handled(), e o evento não se espalhará mais. O callback de entrada não tratada é ideal para eventos de jogabilidade em tela cheia, para que não sejam recebidos quando uma GUI estiver ativa.
Se ninguém quis o evento até agora, e a Seleção de Objetos estiver ativada, o evento será usado para a seleção de objetos. Para a viewport raiz, isso também pode ser ativado em Configurações do Projeto. No caso de uma cena 3D, se uma Camera3D estiver atribuída à Viewport, um raio para o mundo de física (na direção do raio a partir do clique) será lançado. Se este raio atingir um objeto, ele chamará a função CollisionObject3D._input_event() no objeto de física relevante. No caso de uma cena 2D, conceitualmente o mesmo acontece com CollisionObject2D._input_event().
Ao enviar eventos para seus nós filhos e descendentes, a viewport o fará - conforme ilustrado no gráfico a seguir - em uma ordem inversa de busca em profundidade, começando pelo nó na parte inferior da árvore de cena e terminando no nó raiz. Windows e Subviewports estão excluídas desse processo.
Nota
Esta ordem não se aplica a Control._gui_input(), que usa um método diferente baseado no local do evento ou no Control focado. Os eventos de mouse da GUI também sobem pela árvore de cenas, sujeitos às restrições de Control.mouse_filter descritas acima. No entanto, como esses eventos visam Controls específicos, apenas os ancestrais diretos do nó Control alvo recebem o evento. Os eventos de teclado e joypad da GUI não sobem pela árvore de cenas e só podem ser manipulados pelo Control que os recebeu. Caso contrário, eles serão propagados como eventos que não são da GUI através de Node._unhandled_input().
Como os Viewports não enviam eventos para outros SubViewports, um dos seguintes métodos deve ser usado:
Use um SubViewportContainer, que envia automaticamente eventos para seus SubViewports filhos após Node._input() ou Control._gui_input().
Implemente a propagação de eventos com base nos requisitos individuais.
De acordo com o design baseado em nós do Godot, isto permite que nós filhos especializados tratem e consumam eventos específicos, enquanto seus ancestrais e, em última instância, a raíz da cena, podem fornecer um comportamento mais generalizado, se necessário.
Anatomia de um InputEvent
InputEvent é apenas um tipo embutido base, não representa nada e contém apenas algumas informações básicas, como o ID do evento (que é aumentado para cada evento), índice do dispositivo, etc.
Existem vários tipos especializados de InputEvent, descritos na tabela abaixo:
Evento |
Descrição |
Evento de Entrada vazio. |
|
Contém um código de tecla e um valor Unicode, bem como modificadores. |
|
Contém informações de clique, como botão, modificadores, etc. |
|
Contém informações de movimento, como posições relativas e absolutas e velocidade. |
|
Contém informações de eixo analógico do Joystick/Joypad. |
|
Contém informações de botão do Joystick/Joypad. |
|
Contém informações de pressionamento/liberação multitoque. (disponível apenas em dispositivos móveis) |
|
Contém informações de arrasto multitoque. (disponível apenas em dispositivos móveis) |
|
Contém uma posição, um fator, bem como modificadores. |
|
Contém uma posição, um delta, bem como modificadores. |
|
Contém informações relacionadas a MIDI. |
|
Contém um atalho. |
|
Contém uma ação genérica. Esses eventos são frequentemente gerados pelo programador como feedback. (mais sobre isso abaixo) |
Ações de entrada
Ações de entrada são um agrupamento de zero ou mais InputEvents em um título comumente compreendido (por exemplo, a ação padrão "ui_left" agrupa tanto a entrada analógica/digital esquerda do joypad quanto a tecla de seta para a esquerda do teclado). Elas não são obrigatórias para representar um InputEvent, mas são úteis porque abstraem várias entradas ao programar a lógica do jogo.
Isso permite que:
O mesmo código para trabalhar em diferentes dispositivos com diferentes entradas (por exemplo, teclado no PC, Joypad no console).
A entrada seja reconfigurada em tempo de execução.
Ações sejam acionadas programaticamente em tempo de execução.
As ações podem ser criadas no menu Configurações do Projeto na aba Mapa de Entrada e receber eventos de entrada atribuídos.
Qualquer evento possui os métodos InputEvent.is_action(), InputEvent.is_pressed() e InputEvent.is_echo().
Alternativamente, pode ser desejado fornecer ao jogo uma ação do código do jogo (um bom exemplo disso é a detecção de gestos). O singleton Input tem um método para isso: Input.parse_input_event(). Você normalmente usaria assim:
var ev = InputEventAction.new()
# Set as ui_left, pressed.
ev.action = "ui_left"
ev.pressed = true
# Feedback.
Input.parse_input_event(ev)
var ev = new InputEventAction();
// Set as ui_left, pressed.
ev.Action = "ui_left";
ev.Pressed = true;
// Feedback.
Input.ParseInputEvent(ev);
Ver também
Veja Criando ações de entrada para um tutorial sobre como adicionar ações de entrada nas configurações do projeto.
InputMap
Customizar e remapear entradas a partir do código é frequentemente desejado. Se todo o seu fluxo de trabalho depende de ações, o singleton InputMap é ideal para reatribuir ou criar ações diferentes em tempo de execução. Este singleton não é salvo (deve ser modificado manualmente) e seu estado é executado a partir das configurações do projeto (project.godot). Portanto, qualquer sistema dinâmico deste tipo precisa armazenar as configurações da maneira que o programador achar melhor.