Vibração do Controle na Godot 4 (Rumble e Force Feedback)

Aprenda a fazer a vibração do controle na Godot 4 reagir a dano, tiro e explosão, controlar intensidade e duração e vibrar tambem no celular.
A vibração do controle na Godot 4 é um daqueles detalhes que separa um protótipo de um jogo que parece vivo. Quando o jogador toma dano e sente um baque na mão, ou quando um tiro sai e o controle dá um pulso curto, o cérebro conecta a ação à reação de um jeito que só o áudio e a imagem não conseguem. A boa notícia é que a Godot 4 já traz tudo que você precisa na classe Input, sem plugin nem addon. A má notícia (e é bom saber disso desde o começo) é que rumble depende de hardware e driver, então nem todo controle vai responder. Neste post você vai montar uma função utilitária de vibração, disparar ela em eventos de jogo, criar um padrão de pulso e ainda vibrar o celular.
O que a Godot 4 oferece para rumble
Toda a API de vibração vive na classe Input, que é global e você acessa de qualquer script. As funções principais são poucas e diretas:
Input.start_joy_vibration(device: int, weak_magnitude: float, strong_magnitude: float, duration: float = 0)começa a vibração. Odeviceé o índice do controle (quase sempre0para o primeiro),weak_magnitudeestrong_magnitudevão de0.0a1.0edurationé em segundos. Sedurationfor0, a vibração continua até você mandar parar.Input.stop_joy_vibration(device: int)para a vibração daquele controle na hora.Input.get_joy_vibration_strength(device: int) -> Vector2devolve a intensidade atual (x = fraco, y = forte).Input.get_joy_vibration_duration(device: int) -> floatdevolve quanto tempo de vibração ainda resta.Input.get_connected_joypads() -> Arraylista os controles conectados, útil para checar se existe alguém para vibrar antes de tentar.
O detalhe que muita gente ignora é a diferença entre os dois motores. O weak_magnitude controla o motor pequeno, de alta frequência, que produz uma vibração fina e nervosa, ótima para tiros rápidos e feedback de menu. O strong_magnitude controla o motor grande, de baixa frequência, que dá um baque pesado, perfeito para explosões e dano. Brincar com a mistura dos dois é o que faz cada evento ter uma textura própria.
Uma função utilitária para vibrar
Espalhar chamadas de start_joy_vibration pelo código inteiro é receita para dor de cabeça. Melhor centralizar tudo em uma função, de preferência num autoload (singleton). Assim você tem um único lugar para checar se há controle conectado, respeitar a configuração do jogador e ajustar o comportamento no futuro.
Crie um script feedback.gd, registre ele como autoload com o nome Feedback e escreva:
extends Node
## Ligado por padrao; o jogador pode desligar nas opcoes.
var vibracao_ligada: bool = true
func vibrar(fraco: float, forte: float, dur: float) -> void:
if not vibracao_ligada:
return
# Sem controle conectado, nao ha o que vibrar.
if Input.get_connected_joypads().is_empty():
return
Input.start_joy_vibration(0, fraco, forte, dur)
func parar_vibracao() -> void:
Input.stop_joy_vibration(0)
Repare em três decisões. Primeiro, se a vibração estiver desligada nas opções, a função retorna sem fazer nada. Segundo, se não houver nenhum controle conectado (get_connected_joypads() vazio), também retornamos, evitando chamadas inúteis. Terceiro, os parâmetros ficam explícitos e tipados, então em qualquer lugar do jogo você lê Feedback.vibrar(0.2, 0.8, 0.3) e entende na hora que é um baque forte e curto.
Se o seu jogo tem controle de teclado e joystick, vale entender como a Godot trata cada dispositivo. Cobri isso em detalhe no guia de suporte a controle/joystick na Godot, que ajuda a mapear botões e eixos antes de pensar em rumble.
Vibrar ao tomar dano
Agora que existe Feedback.vibrar, disparar em eventos vira uma linha. No script do jogador, no ponto onde a vida diminui, chame a vibração com uma intensidade que combine com o baque de levar dano. Dano pede o motor forte, então mande strong_magnitude alto e weak_magnitude baixo:
extends CharacterBody2D
var vida: int = 100
func tomar_dano(quantidade: int) -> void:
vida -= quantidade
# Baque pesado e curto: motor forte alto, fraco baixo.
Feedback.vibrar(0.1, 0.9, 0.25)
if vida <= 0:
morrer()
func morrer() -> void:
Feedback.vibrar(0.0, 1.0, 0.6)
# ... resto da logica de morte
Para um tiro, inverta a lógica. Um tiro de pistola é rápido e agudo, então use o motor fraco com duração bem curta:
func atirar() -> void:
# ... instancia o projetil
Feedback.vibrar(0.6, 0.1, 0.08)
E uma explosão perto do jogador merece os dois motores no talo por um instante um pouco mais longo:
func explosao_proxima() -> void:
Feedback.vibrar(1.0, 1.0, 0.4)
Note como três eventos completamente diferentes usam a mesma função, só variando os números. Essa é a vantagem de centralizar: você afina o game feel em um lugar só, testando valores até cada evento ficar com a sensação certa.
Padrão de pulso com timer
Uma única vibração contínua serve para a maioria dos casos, mas às vezes você quer um padrão, tipo três pulsos rápidos para sinalizar vida crítica ou um alarme. Como start_joy_vibration com duração já para sozinha, dá para encadear pulsos com await e um timer da árvore de cena:
func pulsar(vezes: int, fraco: float, forte: float, dur_pulso: float, intervalo: float) -> void:
for i: int in range(vezes):
vibrar(fraco, forte, dur_pulso)
await get_tree().create_timer(dur_pulso + intervalo).timeout
Aqui a cada volta do laço a gente dispara um pulso e espera o tempo do pulso mais um intervalo de descanso antes do próximo. Chamar Feedback.pulsar(3, 0.0, 0.7, 0.12, 0.08) produz três batidas fortes e espaçadas. Como a função tem await, quem chamar também pode esperar por ela se quiser sincronizar com uma animação ou som.
Um cuidado: se o jogador desligar a vibração no meio de um pulso, o laço continua rodando mas vibrar simplesmente não faz nada, porque a checagem de vibracao_ligada está lá dentro. Silêncio garantido, sem quebrar o fluxo.
Vibrar o celular
Se você exporta para Android, o feedback tátil não precisa ficar de fora. A função é diferente porque o celular não tem dois motores separados como um controle:
func vibrar_celular(ms: int = 300) -> void:
if not vibracao_ligada:
return
Input.vibrate_handheld(ms)
O Input.vibrate_handheld(duration_ms: int = 500) recebe a duração em milissegundos. Vale um aviso honesto de plataforma: no Android a duração é respeitada, mas no iOS a vibração dispara ignorando o valor que você passou, então não conte com controle fino de tempo no iPhone. Em jogos de celular a vibração costuma andar junto com controles na tela, e se esse é o seu caso o guia de controles touch no celular mostra como montar um joystick virtual que combina bem com esse feedback.
Consultar o estado da vibração
Às vezes você quer saber se o controle já está vibrando antes de disparar outra coisa, para não sobrepor efeitos de um jeito estranho. As funções de consulta ajudam:
func esta_vibrando() -> bool:
var forca: Vector2 = Input.get_joy_vibration_strength(0)
return forca.length() > 0.0
func tempo_restante() -> float:
return Input.get_joy_vibration_duration(0)
Com isso você pode, por exemplo, deixar uma explosão sobrescrever um tiro em andamento, mas evitar que dois tiros seguidos criem um chiado contínuo indesejado. É micro-ajuste, mas é exatamente esse tipo de polimento que compõe o game feel e juice de um jogo que agrada na mão.
A opção de desligar (e por que ela importa)
Vibração não agrada todo mundo. Tem quem ache cansativo, tem quem jogue em contexto onde barulho de motor incomoda e tem gente com sensibilidade tátil. Por isso a opção de desligar não é enfeite, é acessibilidade básica. Como já deixamos a variável vibracao_ligada no autoload, o menu de opções só precisa alterá-la:
func _on_checkbox_vibracao_toggled(ativado: bool) -> void:
Feedback.vibracao_ligada = ativado
if not ativado:
Feedback.parar_vibracao()
Quando o jogador desmarca a caixa, além de bloquear novas vibrações, chamamos parar_vibracao para cortar qualquer efeito que já esteja rolando. E não esqueça de salvar essa preferência no arquivo de configuração do jogo, junto com volume e resolução, para que a escolha persista entre sessões.
Limites que você precisa aceitar
Antes de fechar, vale repetir o aviso do começo com todas as letras, porque ele evita horas de depuração inútil. Rumble depende do driver e da camada SDL que a Godot usa por baixo. Nem todo controle tem motores de vibração, controles genéricos baratos às vezes não expõem rumble corretamente, o suporte no export para web (navegador) é limitado e inconsistente entre navegadores, e mesmo com o código impecável a Godot não garante o efeito em todo hardware. Então, se não vibrar, o primeiro passo é testar em um controle que você sabe que funciona (um Xbox ou DualShock no cabo é uma aposta segura) antes de suspeitar do seu código.
Conclusão e próximo passo
Você já tem o essencial: uma função vibrar(fraco, forte, dur) centralizada, disparos em dano, tiro e explosão, um padrão de pulso com await e timer, vibração no celular e uma opção de desligar que respeita o jogador. O próximo passo é sentar com um controle na mão e afinar os números de cada evento, porque game feel se descobre testando, não no papel. Ajuste até cada baque parecer inevitável. Se quiser continuar aprofundando essa camada de resposta sensorial do jogo, siga para o material sobre game feel e juice e transforme os efeitos que você montou aqui em algo realmente memorável no CursoGame.Dev.
Perguntas frequentes
Como fazer o controle vibrar na Godot 4?
Use Input.start_joy_vibration(device, weak_magnitude, strong_magnitude, duration). O device costuma ser 0 para o primeiro controle, weak e strong vao de 0.0 a 1.0 e duration e em segundos. Passe 0 na duracao para vibrar ate chamar stop_joy_vibration.
Qual a diferenca entre weak_magnitude e strong_magnitude?
weak_magnitude controla o motor pequeno de alta frequencia (uma vibracao mais fina e nervosa) e strong_magnitude controla o motor grande de baixa frequencia (um baque pesado). Misturar os dois da texturas diferentes para cada evento do jogo.
Da para vibrar o celular na Godot 4?
Sim, com Input.vibrate_handheld(duration_ms). No Android a duracao em milissegundos e respeitada. No iOS a chamada dispara a vibracao mas ignora a duracao que voce passou.
Por que meu controle nao vibra mesmo com o codigo certo?
Rumble depende de driver, do SDL e do hardware do controle. Nem todo joystick tem motores, o suporte no navegador (export web) e limitado e a Godot nao garante o efeito em todo hardware. Teste em um controle que voce sabe que vibra.
Como deixar o jogador desligar a vibracao?
Guarde uma variavel de configuracao como vibracao_ligada e cheque ela dentro da sua funcao utilitaria de vibrar. Se estiver desligada, apenas retorne sem chamar start_joy_vibration. Salve essa opcao no arquivo de config do jogo.


