
Introdução: sem input, não existe jogo de verdade
Um jogo só vira jogo quando o jogador consegue interagir.
Até aqui, nos capítulos anteriores, você entendeu o que é MonoGame, preparou o ambiente, estudou o game loop com Update e Draw, e aprendeu a desenhar sprites e texturas na tela.
Agora chegou o momento em que o jogo começa a responder ao jogador.
É aqui que entram:
🎮 teclado;
🖱️ mouse;
🕹️ controle/gamepad;
⌨️ comandos;
👆 cliques;
🎯 ações;
🚀 movimento;
⏸️ pausa;
✅ confirmação;
❌ cancelamento.
No MonoGame, a entrada do jogador é tratada principalmente pelo namespace Microsoft.Xna.Framework.Input, que reúne classes e estruturas para lidar com teclado, mouse, gamepad e outros tipos de entrada. A documentação oficial descreve esse namespace justamente como a área responsável por keyboard, mouse, gamepad e touch input handling.
A ideia central é simples:
O jogador aperta algo. O jogo lê esse estado no
Update. A lógica responde. ODrawmostra o resultado.
Esse capítulo é fundamental porque quase tudo em um jogo depende de input:
- mover personagem;
- atacar;
- pular;
- abrir menu;
- clicar em botão;
- pausar;
- selecionar opção;
- usar item;
- mirar;
- confirmar;
- cancelar;
- navegar em interface.
Se o game loop é o coração do jogo, o input é o sistema nervoso. É por ele que a vontade do jogador entra no mundo virtual.
🎮 O que é entrada do jogador?
Entrada do jogador, ou player input, é qualquer ação feita pelo usuário para controlar o jogo.
Exemplos:
| Dispositivo | Exemplos de entrada |
|---|---|
| Teclado | W, A, S, D, setas, espaço, Enter, Escape |
| Mouse | clique esquerdo, clique direito, posição, scroll |
| Controle | botões, analógicos, gatilhos, D‑Pad |
| Touch | toque, arrasto, gesto |
Neste capítulo, o foco será nos três dispositivos mais comuns para jogos desktop:
✅ teclado;
✅ mouse;
✅ controle.
A documentação oficial do MonoGame tem uma seção específica chamada “Working with Input”, criada para demonstrar como escrever código para gerenciar entrada em projetos MonoGame usando teclado, mouse, gamepad e touch.
🧠 A regra principal: input deve ser lido no Update
No capítulo sobre game loop, você aprendeu:
Update pensa. Draw mostra.
Essa regra continua aqui.
A entrada do jogador deve ser lida no método:
protected override void Update(GameTime gameTime)
{
// Ler teclado, mouse e controle aqui
}
Não coloque leitura de teclado, mouse ou controle dentro do Draw.
O Draw deve apenas renderizar o resultado visual.
Exemplo correto:
protected override void Update(GameTime gameTime)
{
KeyboardState keyboard = Keyboard.GetState();
if (keyboard.IsKeyDown(Keys.Escape))
Exit();
base.Update(gameTime);
}
Exemplo errado:
protected override void Draw(GameTime gameTime)
{
KeyboardState keyboard = Keyboard.GetState(); // Evite isso
}
A lógica do input pertence ao Update, porque input altera o estado do jogo. O Draw apenas mostra esse estado.
⌨️ Entrada pelo teclado no MonoGame
O teclado é a forma mais simples de começar.
No MonoGame, você usa:
Keyboard.GetState()
Esse método retorna um objeto do tipo:
KeyboardState
A documentação oficial descreve a classe Keyboard como responsável por obter teclas pressionadas do teclado, enquanto KeyboardState mantém o estado das teclas em determinado momento.
Exemplo básico:
KeyboardState keyboard = Keyboard.GetState();
if (keyboard.IsKeyDown(Keys.Space))
{
// Espaço está pressionado
}
A enumeração Keys define as teclas do teclado disponíveis para uso no MonoGame.
🕹️ Exemplo: mover personagem com teclado
Imagine que você tem uma posição do jogador:
private Vector2 _playerPosition;
private float _playerSpeed = 250f;
No Initialize:
protected override void Initialize()
{
_playerPosition = new Vector2(100, 100);
base.Initialize();
}
No Update:
protected override void Update(GameTime gameTime)
{
float deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
KeyboardState keyboard = Keyboard.GetState();
if (keyboard.IsKeyDown(Keys.Escape))
Exit();
if (keyboard.IsKeyDown(Keys.Right))
_playerPosition.X += _playerSpeed * deltaTime;
if (keyboard.IsKeyDown(Keys.Left))
_playerPosition.X -= _playerSpeed * deltaTime;
if (keyboard.IsKeyDown(Keys.Up))
_playerPosition.Y -= _playerSpeed * deltaTime;
if (keyboard.IsKeyDown(Keys.Down))
_playerPosition.Y += _playerSpeed * deltaTime;
base.Update(gameTime);
}
Esse código permite mover o personagem usando as setas.
A lógica é:
- seta direita aumenta
X; - seta esquerda diminui
X; - seta para cima diminui
Y; - seta para baixo aumenta
Y.
Em jogos 2D, normalmente o eixo Y cresce para baixo. Por isso, subir significa diminuir Y.
🧭 WASD ou setas?
Muitos jogos usam:
W = cima
A = esquerda
S = baixo
D = direita
Você pode permitir os dois formatos:
if (keyboard.IsKeyDown(Keys.Right) || keyboard.IsKeyDown(Keys.D))
_playerPosition.X += _playerSpeed * deltaTime;
if (keyboard.IsKeyDown(Keys.Left) || keyboard.IsKeyDown(Keys.A))
_playerPosition.X -= _playerSpeed * deltaTime;
if (keyboard.IsKeyDown(Keys.Up) || keyboard.IsKeyDown(Keys.W))
_playerPosition.Y -= _playerSpeed * deltaTime;
if (keyboard.IsKeyDown(Keys.Down) || keyboard.IsKeyDown(Keys.S))
_playerPosition.Y += _playerSpeed * deltaTime;
Isso melhora a experiência do jogador.
Alguns preferem setas. Outros preferem WASD. Em jogos de PC, oferecer ambos é uma boa prática.
⚠️ Tecla pressionada vs tecla recém-pressionada
Existe uma diferença importante entre:
IsKeyDown
e a ideia de “apertou agora”.
Quando você usa:
keyboard.IsKeyDown(Keys.Space)
isso será verdadeiro enquanto a tecla estiver segurada.
Se você usar isso para pular, pode acontecer de o personagem tentar pular várias vezes enquanto a tecla estiver pressionada.
Para resolver, você precisa comparar o estado atual do teclado com o estado anterior.
🧠 Estado atual e estado anterior
Crie dois campos:
private KeyboardState _currentKeyboard;
private KeyboardState _previousKeyboard;
No Update:
protected override void Update(GameTime gameTime)
{
_previousKeyboard = _currentKeyboard;
_currentKeyboard = Keyboard.GetState();
if (IsKeyPressed(Keys.Space))
{
// Executa apenas uma vez quando a tecla é pressionada
}
base.Update(gameTime);
}
private bool IsKeyPressed(Keys key)
{
return _currentKeyboard.IsKeyDown(key) &&
_previousKeyboard.IsKeyUp(key);
}
Agora IsKeyPressed(Keys.Space) só será verdadeiro no frame em que a tecla acabou de ser pressionada.
Esse conceito é chamado de state tracking between frames, ou rastreamento de estado entre frames. A documentação oficial do MonoGame possui um capítulo dedicado a gerenciamento de input, incluindo justamente teclado, mouse, gamepad e rastreamento de estado entre frames para criar um sistema reutilizável de entrada.
🧩 Quando usar IsKeyDown e quando usar IsKeyPressed?
Use IsKeyDown para ações contínuas:
✅ andar;
✅ correr;
✅ mirar;
✅ segurar escudo;
✅ carregar ataque;
✅ mover câmera.
Use IsKeyPressed para ações únicas:
✅ pular;
✅ abrir menu;
✅ pausar;
✅ confirmar;
✅ atirar uma vez;
✅ trocar item;
✅ iniciar diálogo.
Exemplo:
if (_currentKeyboard.IsKeyDown(Keys.D))
{
// Andar enquanto segura D
}
if (IsKeyPressed(Keys.Space))
{
// Pular uma vez
}
Essa diferença melhora muito a sensação de controle.
🖱️ Entrada pelo mouse no MonoGame
O mouse é essencial para vários tipos de jogos:
- estratégia;
- puzzle;
- point-and-click;
- construção;
- menus;
- inventários;
- interfaces;
- jogos de tiro com mira;
- editores de fase.
No MonoGame, você usa:
Mouse.GetState()
Esse método retorna:
MouseState
A documentação oficial descreve MouseState como uma estrutura que representa o estado do mouse, incluindo posição do cursor e informações de botões pressionados.
Exemplo:
MouseState mouse = Mouse.GetState();
int mouseX = mouse.X;
int mouseY = mouse.Y;
📍 Pegando a posição do mouse
A posição do mouse pode ser lida assim:
MouseState mouse = Mouse.GetState();
Vector2 mousePosition = new Vector2(mouse.X, mouse.Y);
Agora você pode usar essa posição para:
- mover uma mira;
- destacar um botão;
- clicar em item;
- selecionar personagem;
- posicionar objeto;
- arrastar interface.
Exemplo simples:
private Vector2 _mousePosition;
protected override void Update(GameTime gameTime)
{
MouseState mouse = Mouse.GetState();
_mousePosition = new Vector2(mouse.X, mouse.Y);
base.Update(gameTime);
}
🖱️ Detectando clique do mouse
Assim como no teclado, você pode verificar se um botão está pressionado.
MouseState mouse = Mouse.GetState();
if (mouse.LeftButton == ButtonState.Pressed)
{
// Botão esquerdo está pressionado
}
Mas isso detecta enquanto o botão está segurado.
Para detectar um clique único, você precisa comparar estado atual e anterior.
🧠 Clique único com mouse
Crie campos:
private MouseState _currentMouse;
private MouseState _previousMouse;
No Update:
protected override void Update(GameTime gameTime)
{
_previousMouse = _currentMouse;
_currentMouse = Mouse.GetState();
if (IsLeftClick())
{
// Clique esquerdo detectado uma vez
}
base.Update(gameTime);
}
private bool IsLeftClick()
{
return _currentMouse.LeftButton == ButtonState.Pressed &&
_previousMouse.LeftButton == ButtonState.Released;
}
Agora o clique acontece apenas uma vez por pressionamento.
Isso é essencial para menus e botões.
🎯 Exemplo: botão clicável com mouse
Imagine um botão representado por um retângulo:
private Rectangle _playButton;
No Initialize:
protected override void Initialize()
{
_playButton = new Rectangle(500, 300, 280, 80);
base.Initialize();
}
No Update:
protected override void Update(GameTime gameTime)
{
_previousMouse = _currentMouse;
_currentMouse = Mouse.GetState();
Point mousePoint = new Point(_currentMouse.X, _currentMouse.Y);
if (_playButton.Contains(mousePoint) && IsLeftClick())
{
StartGame();
}
base.Update(gameTime);
}
private bool IsLeftClick()
{
return _currentMouse.LeftButton == ButtonState.Pressed &&
_previousMouse.LeftButton == ButtonState.Released;
}
private void StartGame()
{
// Troca para a cena de gameplay
}
Esse é o princípio de menus clicáveis.
Você verifica:
- o mouse está sobre o botão?
- o jogador clicou?
- então executa a ação.
🧭 Hover: detectando mouse sobre objeto
Para criar efeito visual quando o mouse passa sobre um botão:
Point mousePoint = new Point(_currentMouse.X, _currentMouse.Y);
bool isHovering = _playButton.Contains(mousePoint);
No Draw, você pode mudar a cor:
Color buttonColor = isHovering ? Color.Yellow : Color.White;
Conceito:
Updatecalcula se está em hover;Drawmostra o botão diferente.
Lembre-se:
A lógica do hover fica no
Update. O visual do hover fica noDraw.
🕹️ Entrada por controle/gamepad no MonoGame
O controle é fundamental para jogos com foco em sofá, plataformas, ação, corrida, aventura e experiências mais próximas de console.
No MonoGame, você usa:
GamePad.GetState(PlayerIndex.One)
ou, dependendo da sobrecarga:
GamePad.GetState(0)
Isso retorna:
GamePadState
A documentação oficial descreve GamePadState como uma estrutura que representa informações específicas sobre o estado do controle, incluindo botões e analógicos. A classe GamePad permite obter o estado atual do controle e também possui sobrecargas relacionadas a dead zones dos analógicos.
Exemplo:
GamePadState gamePad = GamePad.GetState(PlayerIndex.One);
if (gamePad.IsConnected)
{
// Controle conectado
}
🎮 Detectando botões do controle
Exemplo:
GamePadState gamePad = GamePad.GetState(PlayerIndex.One);
if (gamePad.Buttons.A == ButtonState.Pressed)
{
// Botão A pressionado
}
if (gamePad.Buttons.B == ButtonState.Pressed)
{
// Botão B pressionado
}
Botões comuns:
A
B
X
Y
Start
Back
LeftShoulder
RightShoulder
Também existe o D‑Pad:
if (gamePad.DPad.Right == ButtonState.Pressed)
{
// Direita no D-Pad
}
🕹️ Movimento com analógico
Os analógicos são lidos como valores numéricos.
Exemplo:
Vector2 leftStick = gamePad.ThumbSticks.Left;
Normalmente:
leftStick.Xvaria de ‑1 a 1;leftStick.Yvaria de ‑1 a 1.
Exemplo:
_playerPosition.X += leftStick.X * _playerSpeed * deltaTime;
_playerPosition.Y -= leftStick.Y * _playerSpeed * deltaTime;
Repare no detalhe:
_playerPosition.Y -= leftStick.Y * _playerSpeed * deltaTime;
Em muitos sistemas de jogo 2D, o eixo Y da tela cresce para baixo. Por isso, dependendo do controle, pode ser necessário inverter o eixo Y.
⚠️ O que é dead zone?
Dead zone é uma área morta do analógico.
Analógicos físicos raramente ficam perfeitamente em zero. Mesmo sem tocar no controle, pode haver pequenos valores residuais. Isso pode fazer o personagem andar sozinho lentamente.
A documentação oficial da classe GamePad mostra sobrecargas de GetState que permitem informar o modo de dead zone usado nos analógicos.
Você pode lidar com isso manualmente:
Vector2 movement = gamePad.ThumbSticks.Left;
if (movement.Length() < 0.2f)
{
movement = Vector2.Zero;
}
Depois:
_playerPosition.X += movement.X * _playerSpeed * deltaTime;
_playerPosition.Y -= movement.Y * _playerSpeed * deltaTime;
Isso evita movimento involuntário.
🎮 Exemplo: teclado e controle juntos
Uma boa prática é permitir que o jogador use teclado ou controle.
Exemplo:
protected override void Update(GameTime gameTime)
{
float deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
KeyboardState keyboard = Keyboard.GetState();
GamePadState gamePad = GamePad.GetState(PlayerIndex.One);
Vector2 movement = Vector2.Zero;
if (keyboard.IsKeyDown(Keys.D) || keyboard.IsKeyDown(Keys.Right))
movement.X += 1;
if (keyboard.IsKeyDown(Keys.A) || keyboard.IsKeyDown(Keys.Left))
movement.X -= 1;
if (keyboard.IsKeyDown(Keys.W) || keyboard.IsKeyDown(Keys.Up))
movement.Y -= 1;
if (keyboard.IsKeyDown(Keys.S) || keyboard.IsKeyDown(Keys.Down))
movement.Y += 1;
if (gamePad.IsConnected)
{
Vector2 stick = gamePad.ThumbSticks.Left;
if (stick.Length() > 0.2f)
{
movement.X += stick.X;
movement.Y -= stick.Y;
}
}
if (movement != Vector2.Zero)
{
movement.Normalize();
_playerPosition += movement * _playerSpeed * deltaTime;
}
base.Update(gameTime);
}
Esse exemplo ensina várias coisas:
✅ teclado e controle podem coexistir;
✅ movimento diagonal precisa ser normalizado;
✅ analógico precisa de dead zone;
✅ input deve ser convertido em uma intenção de movimento.
🧭 Por que normalizar o movimento diagonal?
Sem normalização, o personagem pode andar mais rápido na diagonal.
Exemplo:
Direita = velocidade 1
Baixo = velocidade 1
Diagonal = combinação de duas direções
Isso pode resultar em velocidade maior.
Para corrigir:
if (movement != Vector2.Zero)
{
movement.Normalize();
}
Assim, o vetor mantém direção, mas sua magnitude é ajustada.
Esse pequeno detalhe deixa o jogo mais justo e profissional.
🧱 Criando um InputManager
Conforme o jogo cresce, não é ideal deixar toda a leitura de input dentro do Game1.cs.
O melhor é criar uma classe específica para entrada.
Exemplo:
public class InputManager
{
private KeyboardState _currentKeyboard;
private KeyboardState _previousKeyboard;
private MouseState _currentMouse;
private MouseState _previousMouse;
private GamePadState _currentGamePad;
private GamePadState _previousGamePad;
public void Update()
{
_previousKeyboard = _currentKeyboard;
_previousMouse = _currentMouse;
_previousGamePad = _currentGamePad;
_currentKeyboard = Keyboard.GetState();
_currentMouse = Mouse.GetState();
_currentGamePad = GamePad.GetState(PlayerIndex.One);
}
public bool IsKeyDown(Keys key)
{
return _currentKeyboard.IsKeyDown(key);
}
public bool IsKeyPressed(Keys key)
{
return _currentKeyboard.IsKeyDown(key) &&
_previousKeyboard.IsKeyUp(key);
}
public bool IsLeftMouseClicked()
{
return _currentMouse.LeftButton == ButtonState.Pressed &&
_previousMouse.LeftButton == ButtonState.Released;
}
public Point MousePosition
{
get { return new Point(_currentMouse.X, _currentMouse.Y); }
}
public bool IsGamePadButtonPressed(Buttons button)
{
return _currentGamePad.IsButtonDown(button) &&
_previousGamePad.IsButtonUp(button);
}
public Vector2 GetMovementVector()
{
Vector2 movement = Vector2.Zero;
if (IsKeyDown(Keys.D) || IsKeyDown(Keys.Right))
movement.X += 1;
if (IsKeyDown(Keys.A) || IsKeyDown(Keys.Left))
movement.X -= 1;
if (IsKeyDown(Keys.W) || IsKeyDown(Keys.Up))
movement.Y -= 1;
if (IsKeyDown(Keys.S) || IsKeyDown(Keys.Down))
movement.Y += 1;
if (_currentGamePad.IsConnected)
{
Vector2 stick = _currentGamePad.ThumbSticks.Left;
if (stick.Length() > 0.2f)
{
movement.X += stick.X;
movement.Y -= stick.Y;
}
}
if (movement != Vector2.Zero)
movement.Normalize();
return movement;
}
}
Agora, no Game1.cs:
private InputManager _input;
protected override void Initialize()
{
_input = new InputManager();
base.Initialize();
}
protected override void Update(GameTime gameTime)
{
_input.Update();
if (_input.IsKeyPressed(Keys.Escape))
Exit();
Vector2 movement = _input.GetMovementVector();
_playerPosition += movement * _playerSpeed *
(float)gameTime.ElapsedGameTime.TotalSeconds;
base.Update(gameTime);
}
Esse tipo de organização deixa o projeto muito mais limpo.
🎯 Mapeando ações em vez de teclas
Um erro comum é espalhar teclas pelo código inteiro.
Exemplo ruim:
if (keyboard.IsKeyDown(Keys.Space))
{
Jump();
}
Isso parece simples, mas se você quiser mudar o botão de pulo depois, terá que procurar em vários arquivos.
Uma abordagem melhor é pensar em ações:
MoveLeft
MoveRight
MoveUp
MoveDown
Jump
Attack
Pause
Confirm
Cancel
Então seu jogo não pergunta:
“A tecla espaço foi pressionada?”
Ele pergunta:
“A ação de pular foi acionada?”
Exemplo:
public bool IsJumpPressed()
{
return IsKeyPressed(Keys.Space) ||
IsGamePadButtonPressed(Buttons.A);
}
Agora o jogo usa:
if (_input.IsJumpPressed())
{
_player.Jump();
}
Isso é muito mais profissional.
🧠 Vantagens de mapear ações
Mapear ações traz várias vantagens:
✅ facilita trocar controles;
✅ permite suporte a teclado e controle;
✅ deixa o código mais legível;
✅ ajuda a criar tela de configuração;
✅ reduz repetição;
✅ melhora manutenção;
✅ separa regra de jogo de dispositivo físico.
Em vez de o jogador ser controlado por teclas, ele é controlado por intenções.
Isso é arquitetura de jogo mais madura.
🧩 Exemplo de InputManager com ações
public bool IsPausePressed()
{
return IsKeyPressed(Keys.Escape) ||
IsKeyPressed(Keys.P) ||
IsGamePadButtonPressed(Buttons.Start);
}
public bool IsConfirmPressed()
{
return IsKeyPressed(Keys.Enter) ||
IsKeyPressed(Keys.Space) ||
IsGamePadButtonPressed(Buttons.A);
}
public bool IsCancelPressed()
{
return IsKeyPressed(Keys.Escape) ||
IsGamePadButtonPressed(Buttons.B);
}
public bool IsAttackPressed()
{
return IsKeyPressed(Keys.J) ||
IsGamePadButtonPressed(Buttons.X);
}
Agora seu jogo pode fazer:
if (_input.IsPausePressed())
{
TogglePause();
}
if (_input.IsAttackPressed())
{
_player.Attack();
}
Esse padrão facilita muito a evolução.
🧱 Input em menus
Menus precisam de input diferente do gameplay.
Durante o gameplay, as teclas movem o personagem.
No menu, as teclas navegam entre opções.
Exemplo:
if (_input.IsKeyPressed(Keys.Down))
{
_selectedMenuIndex++;
}
if (_input.IsKeyPressed(Keys.Up))
{
_selectedMenuIndex--;
}
if (_input.IsConfirmPressed())
{
ExecuteMenuOption();
}
Controle:
if (_input.IsGamePadButtonPressed(Buttons.DPadDown))
{
_selectedMenuIndex++;
}
if (_input.IsGamePadButtonPressed(Buttons.DPadUp))
{
_selectedMenuIndex--;
}
Aqui, IsKeyPressed é melhor do que IsKeyDown, porque você não quer que o menu desça dez opções com um único toque segurado.
⏱️ Repetição controlada em menu
Em alguns jogos, se o jogador segura a tecla, o menu começa a repetir depois de um pequeno atraso.
Para iniciantes, o ideal é começar com clique único:
if (_input.IsKeyPressed(Keys.Down))
{
MoveSelectionDown();
}
Depois, em projetos mais avançados, você pode criar um sistema de repetição com timer:
private double _repeatTimer;
private double _repeatDelay = 0.25;
Mas no começo, mantenha simples.
🖱️ Input em botões de interface
Menus com mouse geralmente usam retângulos.
Classe simples:
public class Button
{
public Rectangle Bounds { get; set; }
public string Text { get; set; }
public bool IsHovered { get; private set; }
public Button(Rectangle bounds, string text)
{
Bounds = bounds;
Text = text;
}
public bool Update(InputManager input)
{
IsHovered = Bounds.Contains(input.MousePosition);
return IsHovered && input.IsLeftMouseClicked();
}
}
Uso:
if (_playButton.Update(_input))
{
StartGame();
}
Agora o botão sabe se o mouse está sobre ele e se foi clicado.
🎮 Input e cenas do jogo
Quando você começar a trabalhar com cenas, cada cena pode tratar input de forma diferente.
Exemplo:
MenuScene
GameScene
PauseScene
GameOverScene
Cada uma pode ter seu próprio método:
public void Update(GameTime gameTime, InputManager input)
{
// Input específico da cena
}
No menu:
if (input.IsConfirmPressed())
{
StartGame();
}
No gameplay:
Vector2 movement = input.GetMovementVector();
_player.Move(movement);
No pause:
if (input.IsPausePressed())
{
ResumeGame();
}
Esse modelo deixa o projeto muito mais organizado.
🚀 Input responsivo: o que faz um controle parecer bom?
Input não é apenas “funcionar”.
Input precisa parecer bom.
Um jogo com input ruim parece travado, atrasado ou injusto.
Boas práticas:
✅ leia input no início do Update;
✅ evite atrasos desnecessários;
✅ diferencie tecla segurada de tecla pressionada;
✅ normalize movimento diagonal;
✅ trate dead zone do analógico;
✅ permita teclado e controle quando fizer sentido;
✅ use ações em vez de teclas espalhadas;
✅ teste com diferentes dispositivos;
✅ faça menus responderem com clareza;
✅ dê feedback visual e sonoro.
Um bom input é quase invisível. O jogador só percebe quando está ruim.
🧪 Exercício prático: personagem com teclado e controle
Crie estes campos:
private InputManager _input;
private Vector2 _playerPosition;
private float _playerSpeed = 250f;
No Initialize:
protected override void Initialize()
{
_input = new InputManager();
_playerPosition = new Vector2(100, 100);
base.Initialize();
}
No Update:
protected override void Update(GameTime gameTime)
{
_input.Update();
if (_input.IsPausePressed())
{
// Pausar ou sair
}
float deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
Vector2 movement = _input.GetMovementVector();
_playerPosition += movement * _playerSpeed * deltaTime;
base.Update(gameTime);
}
No Draw, você desenha normalmente:
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
_spriteBatch.Begin();
_spriteBatch.Draw(_playerTexture, _playerPosition, Color.White);
_spriteBatch.End();
base.Draw(gameTime);
}
Esse exercício cria uma base sólida para praticamente qualquer jogo 2D.
⚠️ Erros comuns com input no MonoGame
1. Ler input no Draw
Evite. Input altera lógica, então pertence ao Update.
2. Usar IsKeyDown para tudo
Para ações únicas, use comparação entre estado atual e anterior.
3. Não tratar clique único
Sem controle de estado anterior, o clique pode disparar várias vezes.
4. Não normalizar movimento diagonal
O personagem pode ficar mais rápido na diagonal.
5. Ignorar dead zone do controle
O personagem pode se mover sozinho.
6. Espalhar teclas pelo código inteiro
Prefira mapear ações.
7. Criar um InputManager tarde demais
Quanto antes organizar input, melhor.
8. Não testar com controle real
Controle físico pode ter comportamento diferente do esperado.
9. Não dar feedback ao jogador
Botões precisam mudar visualmente quando selecionados ou clicados.
10. Misturar input de menu com input de gameplay
Cada estado do jogo deve interpretar input de forma adequada.
📊 Tabela prática: tipos de input
| Situação | Melhor abordagem |
|---|---|
| Movimento contínuo | IsKeyDown / analógico |
| Pulo | tecla recém-pressionada |
| Ataque único | botão recém-pressionado |
| Tiro automático | botão segurado com timer |
| Menu | tecla recém-pressionada |
| Botão clicável | mouse sobre retângulo + clique |
| Pausa | ação mapeada |
| Controle analógico | dead zone + normalização |
| Diagonal | normalizar vetor |
| Multidispositivo | InputManager |
🧠 Arquitetura recomendada para input
Uma estrutura profissional pode ficar assim:
MeuJogo/
├── Core/
│ ├── InputManager.cs
│ ├── GameState.cs
│ └── SceneManager.cs
├── Entities/
│ └── Player.cs
├── Scenes/
│ ├── MenuScene.cs
│ ├── GameScene.cs
│ └── PauseScene.cs
├── UI/
│ └── Button.cs
├── Game1.cs
└── Program.cs
O Game1.cs não precisa saber detalhes de cada tecla.
Ele apenas atualiza o input:
_input.Update();
E passa para as cenas:
_currentScene.Update(gameTime, _input);
Isso cria uma base muito mais escalável.
🧩 Input e acessibilidade
Mesmo em jogos pequenos, vale pensar em acessibilidade.
Boas ideias:
✅ permitir teclado e controle;
✅ aceitar WASD e setas;
✅ evitar depender de uma única tecla desconfortável;
✅ permitir remapeamento futuramente;
✅ dar feedback visual em menus;
✅ não exigir reflexos extremos sem necessidade;
✅ permitir pausa;
✅ criar comandos consistentes.
Acessibilidade não é apenas recurso avançado. É respeito ao jogador.
🎯 Input e sensação de jogo
Dois jogos podem ter a mesma mecânica, mas sensações completamente diferentes por causa do input.
Um personagem pode parecer:
- pesado;
- leve;
- escorregadio;
- preciso;
- travado;
- rápido;
- responsivo;
- lento.
Isso depende de fatores como:
- velocidade;
- aceleração;
- desaceleração;
- tempo de resposta;
- animação;
- som;
- colisão;
- feedback visual.
Neste capítulo, o foco é capturar input. Nos próximos, esse input será combinado com movimentação, colisão, física simples e animação para criar sensação de jogo real.
✅ Checklist do Capítulo 5
Ao final deste capítulo, você deve entender:
✅ o que é entrada do jogador;
✅ por que input deve ser lido no Update;
✅ como usar Keyboard.GetState;
✅ o que é KeyboardState;
✅ como usar Mouse.GetState;
✅ o que é MouseState;
✅ como ler posição do mouse;
✅ como detectar clique único;
✅ como usar GamePad.GetState;
✅ o que é GamePadState;
✅ como ler botões do controle;
✅ como ler analógico;
✅ o que é dead zone;
✅ por que normalizar movimento diagonal;
✅ como criar um InputManager;
✅ por que mapear ações em vez de teclas;
✅ como aplicar input em menus;
✅ como aplicar input em gameplay;
✅ quais erros evitar.
🏁 Conclusão: input é a ponte entre jogador e mundo do jogo
Entrada do jogador é muito mais do que detectar tecla apertada.
É a ponte entre a intenção humana e o mundo virtual.
Quando o jogador aperta uma tecla, clica com o mouse ou move um analógico, ele espera que o jogo responda de forma clara, rápida e justa. Se essa resposta falha, a experiência inteira perde força.
No MonoGame, o input é direto e poderoso. Você lê o estado do teclado com KeyboardState, do mouse com MouseState e do controle com GamePadState. A partir disso, cria ações, movimentos, menus, cliques, confirmações, ataques, pulos e interações.
Mas o salto profissional acontece quando você deixa de espalhar teclas pelo código e começa a pensar em ações:
mover, pular, atacar, pausar, confirmar, cancelar.
Essa mentalidade transforma input em arquitetura.
A partir daqui, seu jogo não apenas aparece na tela. Ele responde.
No próximo capítulo, essa resposta ganhará consequência física: colisão, movimentação, limites, gravidade simples e interação entre objetos.
Teclado, mouse e controle são os instrumentos. O InputManager é a partitura. O jogador é quem dá vida ao jogo.
❓ FAQ — Entrada do Jogador no MonoGame
1. Como ler teclado no MonoGame?
Use Keyboard.GetState() para obter um KeyboardState. Essa estrutura mantém o estado das teclas do teclado no momento da leitura.
2. Como saber se uma tecla está pressionada?
Use:
KeyboardState keyboard = Keyboard.GetState();
if (keyboard.IsKeyDown(Keys.Space))
{
// Espaço pressionado
}
3. Qual a diferença entre tecla pressionada e tecla recém-pressionada?
Tecla pressionada fica verdadeira enquanto o jogador segura a tecla. Tecla recém-pressionada exige comparar o estado atual com o estado anterior para disparar apenas uma vez.
4. Como ler mouse no MonoGame?
Use Mouse.GetState() para obter um MouseState, que representa posição do cursor e estado dos botões do mouse.
5. Como detectar clique único do mouse?
Compare o estado atual e anterior:
_currentMouse.LeftButton == ButtonState.Pressed &&
_previousMouse.LeftButton == ButtonState.Released
6. Como ler controle no MonoGame?
Use GamePad.GetState(PlayerIndex.One) para obter um GamePadState, que representa botões, analógicos e estado do controle.
7. O que é dead zone?
Dead zone é uma área morta do analógico usada para ignorar pequenos movimentos involuntários. A API de GamePad.GetState possui sobrecargas relacionadas a modos de dead zone.
8. Por que meu personagem anda mais rápido na diagonal?
Porque o vetor diagonal combina dois eixos. Para corrigir, normalize o vetor de movimento antes de aplicar velocidade.
9. Preciso criar um InputManager?
Não é obrigatório no começo, mas é altamente recomendado quando o projeto cresce. Ele centraliza teclado, mouse, controle e ações do jogador.
10. Qual frase resume este capítulo?
Input transforma intenção em ação.
Capítulo 1 — O que é MonoGame e por que usar para criar jogos
Capítulo 2 — Preparando o Ambiente de Desenvolvimento
Capítulo 3 — Fundamentos do Game Loop: Update e Draw
Capítulo 4 — Sprites, Texturas e Content Pipeline
Capítulo 5 — Entrada do Jogador: Teclado, Mouse e Controle
Capítulo 6 — Colisão, Física Simples e Movimentação
Capítulo 7 — Cenas, Menus e Arquitetura do Jogo
Capítulo 8 — Áudio, Partículas, Animações e Polimento
Capítulo 9 — Shaders, Câmera, Mapas e Performance
Capítulo 10 — Publicação, Monetização e Projeto Final no MonoGame