Manipulando Input: Teclado e Mouse

Aprenda sobre manipulando input: teclado e mouse

30 min
Aula 1 de 5

Manipulando Input: Teclado e Mouse

Olá, futuro desenvolvedor de games! 👋 Nesta aula prática, vamos mergulhar em um dos aspectos mais fundamentais de qualquer jogo interativo: a manipulação de inputs do jogador. Seja para mover um personagem, atirar ou interagir com o ambiente, entender como Godot processa os comandos do teclado e do mouse é essencial.

Vamos aprender a capturar e responder a eventos de teclado e mouse usando GDScript, focando nas melhores práticas para criar controles responsivos e flexíveis. Preparado para dar vida aos seus jogos? ✨

1. Entendendo o Fluxo de Input no Godot 🎮

Godot oferece duas abordagens principais para lidar com input:

a) _input(event): O Tratador de Eventos

Esta função é chamada sempre que um evento de input ocorre. É ideal para inputs que acontecem em um momento específico, como um clique de mouse, o pressionar de uma tecla, ou o movimento do mouse. O Godot envia um objeto InputEvent que você pode inspecionar para saber o tipo de input e seus detalhes.

  • Vantagem: Reage instantaneamente a eventos, processa apenas quando algo acontece.
  • Desvantagem: Não é ideal para inputs contínuos (como manter uma tecla pressionada para mover um personagem), pois ele só detecta o "pressionar" inicial e o "soltar". Para isso, usaremos o _process em conjunto com o Input singleton.

b) _process(delta): O Loop Contínuo

Como vimos na aula anterior, _process é chamado a cada frame. Podemos usá-lo em conjunto com o singleton Input (que é um objeto global disponível em qualquer lugar) para verificar o estado atual de uma tecla ou botão do mouse.

  • Vantagem: Ideal para inputs contínuos (ex: movimento do personagem enquanto a tecla está pressionada).
  • Desvantagem: É chamado a cada frame, mesmo que não haja input, o que pode ser menos eficiente para inputs pontuais.

Nesta aula, focaremos principalmente em _input para eventos pontuais e no Input singleton para estados contínuos, combinando-os com o Mapa de Ações (Input Map) para uma organização superior.

2. Configurando o Mapa de Ações (Input Map) 🗺️

Antes de mergulharmos no código, a melhor prática em Godot é definir ações de input em vez de verificar teclas ou botões do mouse diretamente. Isso torna seu jogo mais flexível, permitindo que os jogadores reconfigurem os controles e facilitando a portabilidade para diferentes plataformas (gamepads, touchscreens, etc.).

Passos para configurar o Input Map:

  1. Vá em Projeto -> Configurações do Projeto...
  2. Selecione a aba Mapa de Ações.
  3. No campo Ação, digite um nome para sua nova ação (ex: mover_para_frente, atirar, pular).
  4. Clique no botão Adicionar.
  5. Com a nova ação selecionada, clique no ícone + ao lado direito para adicionar um evento de input a ela.
  6. Pressione a tecla ou clique o botão do mouse que você deseja associar a essa ação.

Vamos criar algumas ações básicas para nossos exercícios:

  • move_left: Tecla A ou Seta Esquerda
  • move_right: Tecla D ou Seta Direita
  • jump: Tecla Espaço
  • shoot: Botão esquerdo do mouse (Mouse Button Index 1)

Configurações do Projeto - Mapa de Ações
Exemplo de configuração do Mapa de Ações no Godot.

3. Manipulando Input do Teclado ⌨️

Vamos criar um script simples para detectar o pressionar e soltar de teclas através do Input Map.

  1. Crie uma nova cena 2D (Node2D).
  2. Adicione um Label como filho para exibir as mensagens.
  3. Salve a cena como InputTester.tscn.
  4. Anexe um novo script chamado InputTester.gd ao nó Node2D.
# InputTester.gd
extends Node2D
 
@onready var message_label = $"Label"
 
func _ready():
	message_label.text = "Pressione A/D para mover, Espaço para pular, Clique Esquerdo para atirar."
 
func _input(event):
	# --- Verificando Ações de Teclado ---
	if event.is_action_pressed("move_left"):
		message_label.text = "Ação: Mover para Esquerda (Pressionado)"
		print("Ação: move_left Pressionada!")
	elif event.is_action_released("move_left"):
		message_label.text = "Ação: Mover para Esquerda (Solto)"
		print("Ação: move_left Solta!")
 
	if event.is_action_pressed("move_right"):
		message_label.text = "Ação: Mover para Direita (Pressionado)"
		print("Ação: move_right Pressionada!")
	elif event.is_action_released("move_right"):
		message_label.text = "Ação: Mover para Direita (Solto)"
		print("Ação: move_right Solta!")
 
	if event.is_action_pressed("jump"):
		message_label.text = "Ação: Pular (Pressionado)"
		print("Ação: jump Pressionada!")
	elif event.is_action_released("jump"):
		message_label.text = "Ação: Pular (Solto)"
		print("Ação: jump Solta!")
 
	# --- Verificando Ações de Mouse (apenas o clique esquerdo por enquanto) ---
	if event.is_action_pressed("shoot"):
		message_label.text = "Ação: Atirar (Pressionado)"
		print("Ação: shoot Pressionada!")
	elif event.is_action_released("shoot"):
		message_label.text = "Ação: Atirar (Solto)"
		print("Ação: shoot Solta!")
 

Explicação do Código:

  • @onready var message_label = $"Label": Obtém uma referência ao nó Label para atualizar seu texto.
  • _ready(): Define o texto inicial do Label.
  • _input(event): Esta função é o coração da detecção de eventos.
  • event.is_action_pressed("nome_da_acao"): Retorna true apenas no frame em que a ação foi pressionada. Útil para ações pontuais como pular ou atirar.
  • event.is_action_released("nome_da_acao"): Retorna true apenas no frame em que a ação foi solta.

Input Contínuo com _process e Input Singleton

Para inputs que precisam ser verificados continuamente (como mover um personagem enquanto a tecla está pressionada), usamos o singleton Input dentro de _process.

# Adicione este bloco ao seu InputTester.gd, ou crie um novo script para testar movimento.
extends Node2D
 
@onready var message_label = $"Label"
var player_speed = 100
 
func _process(delta):
	var direction = Vector2.ZERO
 
	# Verifica se a ação está sendo mantida pressionada
	if Input.is_action_pressed("move_left"):
		direction.x = -1
		message_label.text = "Movendo para Esquerda..."
	elif Input.is_action_pressed("move_right"):
		direction.x = 1
		message_label.text = "Movendo para Direita..."
	else:
		if message_label.text.begins_with("Movendo"): # Limpa a mensagem se não estiver movendo
			message_label.text = "Pressione A/D para mover, Espaço para pular, Clique Esquerdo para atirar."
 
	# Exemplo de movimento (assumindo que este script está em um Player com Position2D)
	# self.position += direction * player_speed * delta
 
	# Exemplo de ação "just_pressed" e "just_released" com o singleton Input
	if Input.is_action_just_pressed("jump"):
		print("Godot: Pulo iniciado!")
		# Adicione lógica de pulo aqui
	if Input.is_action_just_released("jump"):
		print("Godot: Pulo finalizado!")
		# Adicione lógica de finalização de pulo aqui
 

Explicação do Código Adicional:

  • Input.is_action_pressed("nome_da_acao"): Retorna true enquanto a ação estiver sendo mantida pressionada. Ideal para movimento contínuo.
  • Input.is_action_just_pressed("nome_da_acao"): Retorna true apenas no frame em que a ação foi pressionada. Similar ao event.is_action_pressed(), mas usado fora de _input.
  • Input.is_action_just_released("nome_da_acao"): Retorna true apenas no frame em que a ação foi solta. Similar ao event.is_action_released(), mas usado fora de _input.

4. Manipulando Input do Mouse 🖱️

O mouse pode gerar diferentes tipos de eventos: cliques, movimento e scroll. Vamos focar nos cliques e movimento.

a) InputEventMouseButton (Cliques)

Quando um botão do mouse é clicado ou solto, um InputEventMouseButton é gerado.

# Adicione ou modifique o _input no seu InputTester.gd
extends Node2D
 
@onready var message_label = $"Label"
 
func _input(event):
	# ... (código do teclado acima) ...
 
	# --- Verificando Ações de Mouse (com Input Map) ---
	if event.is_action_pressed("shoot"):
		message_label.text = "Ação: Atirar (Pressionado)"
		print("Ação: shoot Pressionada! Posição do mouse: ", event.position)
	elif event.is_action_released("shoot"):
		message_label.text = "Ação: Atirar (Solto)"
		print("Ação: shoot Solta!")
 
	# --- Verificando botões diretamente (menos recomendado, mas útil para debug) ---
	if event is InputEventMouseButton:
		if event.button_index == MOUSE_BUTTON_RIGHT and event.pressed:
			message_label.text = "Botão Direito do Mouse Pressionado!"
			print("Botão Direito do Mouse Pressionado em: ", event.position)
		elif event.button_index == MOUSE_BUTTON_RIGHT and not event.pressed:
			message_label.text = "Botão Direito do Mouse Solto!"
			print("Botão Direito do Mouse Solto em: ", event.position)
 

Propriedades importantes de InputEventMouseButton:

  • button_index: Qual botão do mouse foi pressionado (ex: MOUSE_BUTTON_LEFT, MOUSE_BUTTON_RIGHT, MOUSE_BUTTON_MIDDLE).
  • pressed: true se o botão foi pressionado, false se foi solto.
  • position: A posição global do mouse na tela no momento do evento.

b) InputEventMouseMotion (Movimento)

Quando o mouse se move, um InputEventMouseMotion é gerado.

# Adicione ou modifique o _input no seu InputTester.gd
extends Node2D
 
@onready var message_label = $"Label"
var last_mouse_pos = Vector2.ZERO
 
func _input(event):
	# ... (código de teclado e mouse button acima) ...
 
	if event is InputEventMouseMotion:
		message_label.text = "Mouse movendo... Posição: " + str(event.position) + \
							 "\nRelativo: " + str(event.relative) + \
							 "\nVelocidade: " + str(event.velocity)
		last_mouse_pos = event.position
		# print("Mouse em: ", event.position, " Relativo: ", event.relative, " Velocidade: ", event.velocity)
 

Propriedades importantes de InputEventMouseMotion:

  • position: A posição global atual do mouse na tela.
  • relative: A mudança na posição do mouse desde o último frame. Muito útil para câmeras ou rotação.
  • velocity: A velocidade do mouse em pixels por segundo.

5. Exercícios Práticos 🚀

Agora é a sua vez de colocar a mão na massa! Use os conceitos que aprendemos para implementar os seguintes desafios.

Exercício 1: Movimento Básico de um Personagem 🚶‍♂️

Crie uma cena com um CharacterBody2D (ou Node2D com um Sprite2D e CollisionShape2D para simular um personagem). Faça-o se mover horizontalmente e pular usando as ações do Input Map que você configurou (move_left, move_right, jump).

Tarefas:

  • Crie uma nova cena com um nó CharacterBody2D (renomeie para Player).
  • Adicione um Sprite2D (pode ser um Icon.svg ou um quadrado simples) e um CollisionShape2D como filhos do Player.
  • Anexe um script ao Player.
  • No _physics_process(delta) (ideal para movimento de personagens), use Input.is_action_pressed() para detectar move_left e move_right e atualizar a velocity.x do CharacterBody2D.
  • Use Input.is_action_just_pressed() para detectar jump e aplicar uma força vertical (velocity.y).
  • Aplique a gravidade e chame move_and_slide() para o movimento.
# Exemplo de Player.gd para o Exercício 1
extends CharacterBody2D
 
const SPEED = 150.0
const JUMP_VELOCITY = -300.0 # Valor negativo para ir para cima
const GRAVITY = 800.0 # Ajuste conforme a necessidade do seu jogo
 
func _physics_process(delta):
	# Aplica gravidade
	if not is_on_floor():
		velocity.y += GRAVITY * delta
 
	# Lida com o pulo
	if Input.is_action_just_pressed("jump") and is_on_floor():
		velocity.y = JUMP_VELOCITY
 
	# Lida com o movimento horizontal
	var direction = Input.get_vector("move_left", "move_right", "ui_up", "ui_down") # ui_up/down são ignorados aqui para movimento 2D horizontal
	if direction.x != 0:
		velocity.x = direction.x * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED) # Desacelera o personagem
 
	move_and_slide()
 

Exercício 2: Atirar na Direção do Mouse 🎯

Modifique a cena do InputTester ou crie uma nova. Ao clicar com o botão esquerdo do mouse (ação shoot), faça com que uma "bala" (pode ser outro Sprite2D simples) apareça na posição do jogador e se mova em direção à posição do mouse no momento do clique.

Tarefas:

  • Crie uma cena de "bala" (Bullet.tscn) com um Sprite2D e um script. A bala deve ter uma velocidade e se mover em sua _process(delta).
  • Na sua cena principal (ex: InputTester ou Player), adicione um Node2D chamado BulletSpawnPoint como filho do seu personagem.
  • No script do seu personagem, carregue a cena da bala usando preload() ou load().
  • No _input(event) ou _process(delta) (usando Input.is_action_just_pressed("shoot")), instancie a cena da bala.
  • Defina a posição inicial da bala para a posição do BulletSpawnPoint.
  • Calcule a direção do BulletSpawnPoint até a event.position do mouse (ou get_global_mouse_position() se usar _process).
  • Passe essa direção para a bala para que ela se mova corretamente.
  • Adicione a bala como filho da cena principal (ex: get_parent().add_child(bullet_instance) ou add_child(bullet_instance) se a bala for independente do player).
# Exemplo de Player.gd (continuando do Exercício 1)
# Adicione estas variáveis no topo
var BulletScene = preload("res://scenes/bullet.tscn") # Crie esta cena primeiro!
@onready var bullet_spawn_point = $BulletSpawnPoint # Crie um Node2D chamado BulletSpawnPoint no Player
 
# Adicione esta função ao Player.gd
func _input(event):
	if event.is_action_just_pressed("shoot"):
		var bullet = BulletScene.instantiate()
		get_parent().add_child(bullet) # Adiciona a bala à cena principal
 
		bullet.global_position = bullet_spawn_point.global_position
		var mouse_direction = (event.position - bullet_spawn_point.global_position).normalized()
		bullet.set_direction(mouse_direction) # Você precisará criar esta função na sua Bullet.gd
 
# Exemplo de Bullet.gd
extends CharacterBody2D
 
var speed = 300.0
var direction = Vector2.RIGHT # Direção padrão
 
func _ready():
	# Certifique-se de que o Sprite da bala esteja virado para a direita por padrão
	# Se não, ajuste a rotação aqui se necessário
	look_at(global_position + direction * 100) # Faz a bala "olhar" para a direção de movimento
 
func set_direction(dir: Vector2):
	direction = dir
 
func _physics_process(delta):
	velocity = direction * speed
	move_and_slide()
 
	# Opcional: Destruir a bala depois de um tempo ou fora da tela
	# if global_position.x < -100 or global_position.x > 1200 or \
	#    global_position.y < -100 or global_position.y > 700:
	# 	queue_free()
 

Exercício 3: Objeto Seguindo o Mouse 🐁

Crie uma cena simples com um Sprite2D. Faça com que este Sprite2D siga a posição do mouse em tempo real.

Tarefas:

  • Crie uma nova cena com um Node2D (renomeie para MouseFollower).
  • Adicione um Sprite2D como filho.
  • Anexe um script ao MouseFollower.
  • No _process(delta), atualize a global_position do MouseFollower para get_global_mouse_position().
# MouseFollower.gd
extends Node2D
 
func _process(delta):
	# Atualiza a posição do nó para seguir o mouse
	global_position = get_global_mouse_position()
 

Resumo ✨

Nesta aula, exploramos a manipulação de input no Godot, cobrindo:

  • A diferença entre _input(event) para eventos pontuais e _process(delta) com o singleton Input para estados contínuos.
  • A importância e configuração do Mapa de Ações (Input Map) para flexibilidade e organização.
  • Como detectar o pressionar e soltar de teclas usando is_action_pressed(), is_action_just_pressed(), is_action_released() e is_action_just_released().
  • Como lidar com cliques (InputEventMouseButton) e movimento do mouse (InputEventMouseMotion).
  • Praticamos criando um personagem que se move e pula, um sistema de tiro que mira no mouse e um objeto que segue o cursor.

Com essas ferramentas, você está pronto para criar interações complexas e intuitivas em seus jogos!

Próximos Passos ➡️

  • Experimente: Tente adicionar mais ações ao seu Input Map (ex: interagir, pausar).
  • Gamepads: Pesquise como o Godot lida com input de gamepads, o que é facilitado pelo uso de ações no Input Map.
  • UI Input: Explore como os controles de UI (botões, sliders) gerenciam seus próprios eventos de input.
  • Prioridade de Input: Entenda como o Godot decide qual nó recebe um evento de input primeiro (ordem da árvore de nós, set_process_input()).

Continue praticando e criando! O input é a ponte entre o jogador e o seu mundo de jogo. Boas criações! 🚀

© 2025 Escola All Dev. Todos os direitos reservados.

Manipulando Input: Teclado e Mouse - Fundamentos do Godot: Desenvolvimento de games para iniciantes | escola.all.dev.br