Capítulo 3 — Fundamentos do Game Loop: Update e Draw

Introdução: o coração invisível de todo jogo

Todo jogo, por mais sim­ples ou com­plexo que pareça, fun­ciona com uma lóg­i­ca fun­da­men­tal: ele atu­al­iza o esta­do do mun­do e depois desen­ha esse mun­do na tela. Esse proces­so acon­tece repeti­da­mente, várias vezes por segun­do, crian­do a sen­sação de movi­men­to, con­t­role e vida.

No MonoGame, esse mecan­is­mo aparece de for­ma clara em dois méto­dos cen­trais:

Update(GameTime gameTime)
Draw(GameTime gameTime)

A própria doc­u­men­tação ofi­cial do MonoGame expli­ca que a classe Game é o pon­to de entra­da da maio­r­ia dos jogos e exe­cu­ta um game loop que chama Update(GameTime) e Draw(GameTime).

Em ter­mos sim­ples:

Update cal­cu­la. Draw mostra.

Ou seja:

  • Update decide o que acon­tece;
  • Draw exibe o resul­ta­do na tela.

Se o jogador aper­tou uma tecla, isso é lido no Update.
Se o per­son­agem mudou de posição, isso é cal­cu­la­do no Update.
Se hou­ve col­isão, isso é ver­i­fi­ca­do no Update.
Se a pon­tu­ação aumen­tou, isso acon­tece no Update.

Depois dis­so, o Draw pega o esta­do atu­al e desen­ha:

  • fun­do;
  • per­son­agem;
  • inimi­gos;
  • moedas;
  • tiros;
  • partícu­las;
  • HUD;
  • tex­to;
  • menus.

Esse capí­tu­lo é um dos mais impor­tantes de todo o cur­so, porque enten­der o game loop sig­nifi­ca enten­der a base de qual­quer jogo.


🎮 O que é o game loop?

O game loop é o ciclo prin­ci­pal de exe­cução de um jogo.

Enquan­to o jogo está aber­to, ele repete uma sequên­cia pare­ci­da com esta:

1. Ler entradas do jogador
2. Atualizar lógica do jogo
3. Verificar colisões
4. Atualizar posições
5. Atualizar animações
6. Desenhar tudo na tela
7. Repetir

No MonoGame, esse ciclo é rep­re­sen­ta­do prin­ci­pal­mente pelos méto­dos Update e Draw.

A doc­u­men­tação ofi­cial do tuto­r­i­al “The Game1 File” expli­ca que, sem­pre que o game loop com­ple­ta uma exe­cução e o jogo é desen­hado na tela, isso é chama­do de frame. O mes­mo mate­r­i­al tam­bém expli­ca que, se o MonoGame estiv­er rodan­do a 60 frames por segun­do, ele real­iza atu­al­iza­ção e ren­der­iza­ção de um frame em aprox­i­mada­mente 16 milis­se­gun­dos.

Isso sig­nifi­ca que seu jogo não é uma imagem para­da. Ele é uma sequên­cia muito ráp­i­da de atu­al­iza­ções e desen­hos.

É como um filme, mas inter­a­ti­vo.


🧠 Por que o game loop é tão importante?

Porque tudo no jogo depende dele.

Sem game loop, não existe:

  • movi­men­to;
  • col­isão;
  • ani­mação;
  • input;
  • inimi­go;
  • físi­ca;
  • câmera;
  • pon­tu­ação;
  • tem­po;
  • menu;
  • pausa;
  • tela de game over.

O jogo pre­cisa estar con­stan­te­mente per­gun­tan­do:

“O que mudou des­de o últi­mo frame?”

Essa per­gun­ta é respon­di­da no Update.

Depois ele per­gun­ta:

“Como o jogo deve apare­cer ago­ra?”

Essa respos­ta acon­tece no Draw.

Essa sep­a­ração parece sim­ples, mas é uma das maiores difer­enças entre um pro­je­to orga­ni­za­do e um pro­je­to con­fu­so.


🧩 A estrutura básica do Game1.cs

Quan­do você cria um pro­je­to MonoGame, nor­mal­mente recebe uma classe pare­ci­da com esta:

public class Game1 : Game
{
private GraphicsDeviceManager _graphics;
private SpriteBatch _spriteBatch;

public Game1()
{
_graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
IsMouseVisible = true;
}

protected override void Initialize()
{
base.Initialize();
}

protected override void LoadContent()
{
_spriteBatch = new SpriteBatch(GraphicsDevice);
}

protected override void Update(GameTime gameTime)
{
base.Update(gameTime);
}

protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);

base.Draw(gameTime);
}
}

A doc­u­men­tação ofi­cial expli­ca que a classe Game1 her­da de Game, e essa classe base fornece os méto­dos prin­ci­pais do jogo, como car­rega­men­to de con­teú­do, atu­al­iza­ção e desen­ho.

Para este capí­tu­lo, os dois méto­dos mais impor­tantes são:

Update(GameTime gameTime)
Draw(GameTime gameTime)

Eles são o cen­tro da lóg­i­ca inter­a­ti­va.


⚙️ O papel do Update

O méto­do Update é onde a lóg­i­ca do jogo acon­tece.

Ele é chama­do repeti­da­mente pelo MonoGame enquan­to o jogo está rodan­do.

Den­tro dele você colo­ca coisas como:

✅ leitu­ra do tecla­do;
✅ leitu­ra do mouse;
✅ leitu­ra do con­t­role;
✅ movi­men­to do per­son­agem;
✅ col­isões;
✅ inteligên­cia arti­fi­cial sim­ples;
✅ con­ta­dores de tem­po;
✅ pon­tu­ação;
✅ vida;
✅ tro­ca de fase;
✅ pause;
✅ game over;
✅ atu­al­iza­ção de ani­mações.

Exem­p­lo sim­ples:

protected override void Update(GameTime gameTime)
{
if (Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();

base.Update(gameTime);
}

A doc­u­men­tação ofi­cial de intro­dução ao MonoGame mostra jus­ta­mente esse tipo de uso: no méto­do Update, é comum ver­i­ficar o esta­do do tecla­do ou do con­t­role e sair do jogo se Escape ou o botão cor­re­spon­dente estiv­er pres­sion­a­do.

Em out­ras palavras:

Update é onde o jogo pen­sa.


🎨 O papel do Draw

O méto­do Draw é onde o jogo desen­ha o que deve apare­cer na tela.

Ele tam­bém é chama­do repeti­da­mente enquan­to o jogo está rodan­do.

Den­tro dele você colo­ca:

✅ limpeza da tela;
✅ desen­ho do fun­do;
✅ desen­ho do per­son­agem;
✅ desen­ho dos inimi­gos;
✅ desen­ho dos obje­tos;
✅ desen­ho de tex­tos;
✅ desen­ho do HUD;
✅ desen­ho de menus;
✅ efeitos visuais.

Exem­p­lo bási­co:

protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);

base.Draw(gameTime);
}

Esse códi­go limpa a tela com a cor CornflowerBlue.

A doc­u­men­tação ofi­cial reforça que a respon­s­abil­i­dade do Draw é ren­derizar o esta­do do jogo cal­cu­la­do no Update, e que ele não deve con­ter lóg­i­ca com­plexa de jogo.

Essa frase é essen­cial.

Draw não deve decidir o jogo. Draw deve mostrar o jogo.


🧠 A regra de ouro: Update pensa, Draw mostra

Essa é a regra mais impor­tante deste capí­tu­lo:

Update pen­sa. Draw mostra.

Se você gravar ape­nas uma ideia deste capí­tu­lo, grave essa.

O que deve ir no Update

Movimento
Input
Colisão
IA
Tempo
Regras
Pontuação
Vida
Estados do jogo
Animações

O que deve ir no Draw

Fundo
Sprites
Personagem
Inimigos
Moedas
Projéteis
Textos
HUD
Menus
Efeitos visuais

O que não deve ir no Draw

Evite colo­car no Draw:

  • cál­cu­lo de col­isão;
  • mudança de posição;
  • leitu­ra de tecla­do;
  • alter­ação de vida;
  • cri­ação de inimi­gos;
  • regras de vitória;
  • regras de der­ro­ta;
  • lóg­i­ca de pon­tu­ação.

Por quê?

Porque o Draw existe para desen­har. Mis­tu­rar lóg­i­ca com ren­der­iza­ção deixa o pro­je­to difí­cil de man­ter.


🕒 O que é GameTime?

Tan­to Update quan­to Draw recebem um parâmetro chama­do GameTime.

protected override void Update(GameTime gameTime)
protected override void Draw(GameTime gameTime)

O GameTime fornece infor­mações de tem­po para o jogo.

A doc­u­men­tação ofi­cial expli­ca que o parâmetro GameTime ofer­ece um retra­to dos val­ores de tem­po do jogo, incluin­do quan­to tem­po o frame ante­ri­or lev­ou para exe­cu­tar.

Isso é muito impor­tante porque jogos depen­dem de tem­po.

Você pre­cisa saber:

  • quan­to tem­po pas­sou des­de o últi­mo frame;
  • há quan­to tem­po o jogo está rodan­do;
  • se o movi­men­to deve ser ajus­ta­do;
  • como man­ter ani­mações suaves;
  • como con­tro­lar timers.

O GameTime per­mite que o jogo não depen­da ape­nas da veloci­dade da máquina.


🏃 Movimento sem delta time: o erro clássico

Imag­ine este códi­go:

playerPosition.X += 5;

Parece sim­ples. A cada Update, o per­son­agem anda 5 pix­els para a dire­i­ta.

Mas há um prob­le­ma.

Se o jogo roda mais rápi­do em um com­puta­dor potente, o per­son­agem pode andar mais rápi­do. Se roda mais lento em out­ro com­puta­dor, pode andar mais deva­gar.

Isso acon­tece porque o movi­men­to depende da quan­ti­dade de chamadas ao Update.

Por isso, em jogos, é comum usar o tem­po decor­ri­do entre frames.


⏱️ Movimento com delta time

O delta time é o tem­po pas­sa­do des­de a últi­ma atu­al­iza­ção.

No MonoGame, você pode obter isso assim:

float deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds;

Depois usa esse val­or no movi­men­to:

playerPosition.X += playerSpeed * deltaTime;

Exem­p­lo com­ple­to:

private Vector2 _playerPosition;
private float _playerSpeed = 200f;

protected override void Initialize()
{
_playerPosition = new Vector2(100, 100);
base.Initialize();
}

protected override void Update(GameTime gameTime)
{
float deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds;

KeyboardState keyboard = Keyboard.GetState();

if (keyboard.IsKeyDown(Keys.Right))
_playerPosition.X += _playerSpeed * deltaTime;

if (keyboard.IsKeyDown(Keys.Left))
_playerPosition.X -= _playerSpeed * deltaTime;

base.Update(gameTime);
}

Aqui, playerSpeed sig­nifi­ca pix­els por segun­do.

Se a veloci­dade for 200f, o per­son­agem ten­ta andar 200 pix­els por segun­do, não 5 pix­els por frame.

Isso deixa o movi­men­to mais estáv­el.


🧮 Por que 60 FPS importa?

FPS sig­nifi­ca frames per sec­ond, ou quadros por segun­do.

Se o jogo roda a 60 FPS, ele ten­ta desen­har 60 quadros por segun­do. A doc­u­men­tação ofi­cial usa esse exem­p­lo para explicar que, a 60 frames por segun­do, atu­al­iza­ção e ren­der­iza­ção pre­cisam acon­te­cer em aprox­i­mada­mente 16 ms por frame.

Isso impor­ta porque o tem­po por frame é lim­i­ta­do.

Se sua lóg­i­ca for pesa­da demais, o jogo pode perder desem­pen­ho.

Por isso, evite faz­er oper­ações pesadas den­tro de Update e Draw.

Exem­p­los de coisas perigosas:

  • car­regar tex­tu­ra den­tro do Draw;
  • cri­ar muitos obje­tos por frame;
  • faz­er leitu­ra pesa­da de arqui­vo no Update;
  • proces­sar mapas inteiros sem neces­si­dade;
  • cal­cu­lar col­isões de for­ma des­or­ga­ni­za­da;
  • desen­har mil­hares de sprites sem con­t­role.

🎮 Exemplo prático: personagem se movendo na tela

Vamos mon­tar um exem­p­lo sim­ples de movi­men­to.

Primeiro, cam­pos da classe:

private Texture2D _playerTexture;
private Vector2 _playerPosition;
private float _playerSpeed = 200f;

No LoadContent:

protected override void LoadContent()
{
_spriteBatch = new SpriteBatch(GraphicsDevice);
_playerTexture = Content.Load<Texture2D>("sprites/player");
}

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);
}

No Draw:

protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);

_spriteBatch.Begin();
_spriteBatch.Draw(_playerTexture, _playerPosition, Color.White);
_spriteBatch.End();

base.Draw(gameTime);
}

A doc­u­men­tação ofi­cial sobre desen­ho de sprites expli­ca o fluxo bási­co: adi­cionar tex­tu­ra ao pro­je­to de con­teú­do, car­regar a tex­tu­ra em LoadContent e usar SpriteBatch para desen­har.

Esse é o primeiro momen­to em que você sente o jogo “nascer”: uma imagem aparece na tela e responde ao tecla­do.


🖼️ O que é SpriteBatch?

SpriteBatch é uma das class­es mais usadas em jogos 2D com MonoGame.

Ela per­mite desen­har tex­turas, sprites e tex­tos na tela.

O fluxo bási­co é:

_spriteBatch.Begin();

_spriteBatch.Draw(...);

_spriteBatch.End();

Ou seja:

  1. abre um lote de desen­ho;
  2. desen­ha sprites;
  3. fecha o lote.

Exem­p­lo:

_spriteBatch.Begin();
_spriteBatch.Draw(_playerTexture, _playerPosition, Color.White);
_spriteBatch.End();

Pense no SpriteBatch como um orga­ni­zador de desen­hos 2D. Em vez de man­dar cada sprite de for­ma des­or­de­na­da para a pla­ca grá­fi­ca, você agru­pa os desen­hos em um lote.


🧱 A ordem de desenho importa

No Draw, a ordem em que você desen­ha define o que aparece por cima.

Exem­p­lo:

_spriteBatch.Begin();

_spriteBatch.Draw(_backgroundTexture, Vector2.Zero, Color.White);
_spriteBatch.Draw(_playerTexture, _playerPosition, Color.White);
_spriteBatch.Draw(_enemyTexture, _enemyPosition, Color.White);

_spriteBatch.End();

Aqui:

  1. o fun­do é desen­hado primeiro;
  2. o jogador vem por cima;
  3. o inimi­go vem depois.

Se você desen­har o fun­do por últi­mo, ele pode cobrir todo o resto.

Exem­p­lo erra­do:

_spriteBatch.Begin();

_spriteBatch.Draw(_playerTexture, _playerPosition, Color.White);
_spriteBatch.Draw(_backgroundTexture, Vector2.Zero, Color.White);

_spriteBatch.End();

Nesse caso, o fun­do pode escon­der o jogador.

Regra práti­ca:

Desen­he de trás para frente.


🧠 Separando lógica e desenho em classes

No começo, colo­car tudo no Game1.cs é nor­mal.

Mas logo o códi­go cresce.

Uma boa evolução é cri­ar uma classe Player.

Exem­p­lo:

public class Player
{
private Texture2D _texture;
private Vector2 _position;
private float _speed = 200f;

public Player(Texture2D texture, Vector2 position)
{
_texture = texture;
_position = position;
}

public void Update(GameTime gameTime)
{
float deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
KeyboardState keyboard = Keyboard.GetState();

if (keyboard.IsKeyDown(Keys.Right))
_position.X += _speed * deltaTime;

if (keyboard.IsKeyDown(Keys.Left))
_position.X -= _speed * deltaTime;

if (keyboard.IsKeyDown(Keys.Up))
_position.Y -= _speed * deltaTime;

if (keyboard.IsKeyDown(Keys.Down))
_position.Y += _speed * deltaTime;
}

public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(_texture, _position, Color.White);
}
}

No Game1.cs, ficaria assim:

private Player _player;

protected override void LoadContent()
{
_spriteBatch = new SpriteBatch(GraphicsDevice);

Texture2D playerTexture = Content.Load<Texture2D>("sprites/player");
_player = new Player(playerTexture, new Vector2(100, 100));
}

protected override void Update(GameTime gameTime)
{
if (Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();

_player.Update(gameTime);

base.Update(gameTime);
}

protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);

_spriteBatch.Begin();
_player.Draw(_spriteBatch);
_spriteBatch.End();

base.Draw(gameTime);
}

Ago­ra o Game1.cs não pre­cisa saber todos os detal­h­es inter­nos do jogador.

Ele ape­nas chama:

_player.Update(gameTime);
_player.Draw(_spriteBatch);

Isso deixa o códi­go mais limpo.


🧭 O que deve acontecer primeiro: Update ou Draw?

A lóg­i­ca nat­ur­al é:

Update → Draw
Update → Draw
Update → Draw

Primeiro o jogo atu­al­iza o esta­do. Depois desen­ha o esta­do atu­al­iza­do.

Exem­p­lo:

  1. jogador aper­ta seta dire­i­ta;
  2. Update detec­ta a tecla;
  3. posição do jogador muda;
  4. Draw desen­ha o jogador na nova posição.

Se você ten­tar alter­ar posição no Draw, o pro­je­to fica con­fu­so.

O Draw deve ser como uma câmera: ele reg­is­tra visual­mente o esta­do atu­al do jogo.


🎯 Trabalhando com estados do jogo

Mes­mo jogos sim­ples têm esta­dos.

Exem­p­los:

Menu
Jogando
Pausado
GameOver

Você pode rep­re­sen­tar isso com um enum:

public enum GameState
{
Menu,
Playing,
Paused,
GameOver
}

No Game1.cs:

private GameState _currentState = GameState.Menu;

No Update:

protected override void Update(GameTime gameTime)
{
KeyboardState keyboard = Keyboard.GetState();

switch (_currentState)
{
case GameState.Menu:
if (keyboard.IsKeyDown(Keys.Enter))
_currentState = GameState.Playing;
break;

case GameState.Playing:
_player.Update(gameTime);

if (keyboard.IsKeyDown(Keys.P))
_currentState = GameState.Paused;
break;

case GameState.Paused:
if (keyboard.IsKeyDown(Keys.R))
_currentState = GameState.Playing;
break;

case GameState.GameOver:
if (keyboard.IsKeyDown(Keys.Enter))
RestartGame();
break;
}

base.Update(gameTime);
}

No Draw:

protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);

_spriteBatch.Begin();

switch (_currentState)
{
case GameState.Menu:
DrawMenu();
break;

case GameState.Playing:
DrawGameplay();
break;

case GameState.Paused:
DrawGameplay();
DrawPauseOverlay();
break;

case GameState.GameOver:
DrawGameOver();
break;
}

_spriteBatch.End();

base.Draw(gameTime);
}

Isso mostra como Update e Draw tra­bal­ham jun­tos, mas com respon­s­abil­i­dades difer­entes.


⏸️ Pausar o jogo corretamente

Um erro comum é pausar ape­nas o desen­ho.

Mas pausar sig­nifi­ca parar de atu­alizar a lóg­i­ca prin­ci­pal.

Quan­do o jogo está pau­sa­do, nor­mal­mente você não quer:

  • inimi­gos se moven­do;
  • tem­po cor­ren­do;
  • pro­jéteis avançan­do;
  • col­isões acon­te­cen­do;
  • jogador per­den­do vida.

Então, no Update, você con­tro­la:

if (_currentState == GameState.Playing)
{
_player.Update(gameTime);
UpdateEnemies(gameTime);
UpdateCollisions();
}

Mas no Draw, você ain­da pode desen­har o jogo con­ge­la­do:

DrawGameplay();
DrawPauseOverlay();

Assim, a tela mostra a cena, mas a lóg­i­ca está para­da.


🧨 Erros comuns com Update e Draw

1. Colocar lógica dentro do Draw

Erro:

protected override void Draw(GameTime gameTime)
{
_playerPosition.X += 5;
}

Isso mis­tu­ra ren­der­iza­ção com lóg­i­ca.

O cor­re­to é mover no Update.


2. Carregar textura dentro do Draw

Erro:

protected override void Draw(GameTime gameTime)
{
Texture2D player = Content.Load<Texture2D>("sprites/player");
}

Isso é ruim porque pode ten­tar car­regar con­teú­do repeti­da­mente.

O cor­re­to é car­regar em LoadContent.


3. Criar objetos demais por frame

Erro comum:

protected override void Update(GameTime gameTime)
{
List<Enemy> enemies = new List<Enemy>();
}

Cri­ar obje­tos sem neces­si­dade a cada frame pode prej­u­dicar per­for­mance.


4. Depender de pixels por frame

Erro:

_position.X += 5;

Mel­hor:

_position.X += _speed * deltaTime;

5. Não separar responsabilidades

Se o Game1.cs vira um arqui­vo gigante, fica difí­cil evoluir.

Crie class­es con­forme o jogo cresce.


📊 Tabela prática: Update vs Draw

Ele­men­toUpdateDraw
Ler tecla­do✅ Sim❌ Não
Mover per­son­agem✅ Sim❌ Não
Ver­i­ficar col­isão✅ Sim❌ Não
Atu­alizar pon­tu­ação✅ Sim❌ Não
Desen­har per­son­agem❌ Não✅ Sim
Desen­har fun­do❌ Não✅ Sim
Desen­har tex­to❌ Não✅ Sim
Desen­har HUD❌ Não✅ Sim
Cri­ar regra de vitória✅ Sim❌ Não
Exibir tela de vitória❌ Não✅ Sim

🧪 Pequeno exercício prático

Crie um per­son­agem que se move pela tela e não sai dos lim­ites da janela.

Cam­pos:

private Texture2D _playerTexture;
private Vector2 _playerPosition;
private float _playerSpeed = 250f;

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;

_playerPosition.X = MathHelper.Clamp(
_playerPosition.X,
0,
_graphics.PreferredBackBufferWidth - _playerTexture.Width
);

_playerPosition.Y = MathHelper.Clamp(
_playerPosition.Y,
0,
_graphics.PreferredBackBufferHeight - _playerTexture.Height
);

base.Update(gameTime);
}

No Draw:

protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);

_spriteBatch.Begin();
_spriteBatch.Draw(_playerTexture, _playerPosition, Color.White);
_spriteBatch.End();

base.Draw(gameTime);
}

Esse exer­cí­cio ensi­na três fun­da­men­tos:

  1. input;
  2. movi­men­to;
  3. lim­ite de tela.

🧠 O papel de base.Update e base.Draw

Você verá muito isto:

base.Update(gameTime);

e isto:

base.Draw(gameTime);

Essas chamadas exe­cu­tam o com­por­ta­men­to da classe base Game.

Em pro­je­tos sim­ples, elas cos­tu­mam ficar no final dos méto­dos.

No começo, man­ten­ha essas chamadas. Con­forme você avançar, enten­derá mel­hor quan­do e por que reor­ga­nizá-las.


🧩 Componentes atualizáveis e desenháveis

O MonoGame tam­bém pos­sui con­ceitos como GameComponent e DrawableGameComponent.

A doc­u­men­tação ofi­cial expli­ca que DrawableGameComponent é um obje­to desen­háv­el que, quan­do adi­ciona­do à coleção Game.Components, tem seu méto­do Draw(GameTime) chama­do quan­do Game.Draw(GameTime) é chama­do.

Isso mostra que o próprio frame­work pos­sui mecan­is­mos para orga­ni­zar com­po­nentes que atu­al­izam e desen­ham.

Você não pre­cisa usar isso no iní­cio, mas é útil saber que existe.

Em pro­je­tos maiores, você pode orga­ni­zar sis­temas com com­po­nentes, cenas ou enti­dades próprias.


🖊️ E texto na tela?

Além de sprites, você tam­bém pode desen­har tex­to.

A doc­u­men­tação ofi­cial expli­ca que o MonoGame ofer­ece uma for­ma de desen­har tex­to usan­do fontes reg­istradas no com­puta­dor de desen­volvi­men­to, con­ver­tendo a fonte em tex­tu­ra para ren­der­iza­ção no jogo.

Em um jogo real, tex­tos são usa­dos para:

  • pon­tu­ação;
  • vida;
  • tem­po;
  • menus;
  • men­sagens;
  • diál­o­gos;
  • instruções;
  • game over.

Mes­mo o tex­to segue a mes­ma sep­a­ração:

  • o val­or da pon­tu­ação muda no Update;
  • a pon­tu­ação aparece na tela no Draw.

Exem­p­lo con­ceitu­al:

// Update
_score += 10;

// Draw
_spriteBatch.DrawString(_font, $"Score: {_score}", new Vector2(20, 20), Color.White);

🚀 Como pensar como desenvolvedor de jogos

Um ini­ciante cos­tu­ma pen­sar assim:

“Como eu desen­ho meu per­son­agem?”

Um desen­volve­dor começa a pen­sar assim:

“Qual é o esta­do atu­al do meu jogo e como ele deve ser atu­al­iza­do antes de ser desen­hado?”

Essa mudança men­tal é poderosa.

Um jogo não é ape­nas imagem. Um jogo é esta­do.

Exem­p­los de esta­do:

posição do jogador
velocidade
vida
pontuação
fase atual
tempo restante
inimigos vivos
moedas coletadas
estado do menu
estado de pausa

O Update altera esse esta­do.
O Draw mostra esse esta­do.

Essa é a base.


📦 Organização recomendada após este capítulo

Depois de enten­der Update e Draw, comece a sep­a­rar arquiv­os.

Estru­tu­ra sug­eri­da:

MeuJogo/
├── Game1.cs
├── Program.cs
├── Entities/
│ └── Player.cs
├── Core/
│ └── InputManager.cs
├── Scenes/
│ └── GameScene.cs
└── Content/
└── sprites/
└── player.png

Você ain­da não pre­cisa cri­ar arquite­tu­ra com­plexa, mas já pode começar a tirar respon­s­abil­i­dade do Game1.cs.


✅ Checklist do Capítulo 3

Ao final deste capí­tu­lo, você deve enten­der:

✅ o que é game loop;
✅ por que jogos atu­al­izam e desen­ham várias vezes por segun­do;
✅ o papel do Update;
✅ o papel do Draw;
✅ o que é GameTime;
✅ por que usar delta time;
✅ como mover um per­son­agem;
✅ como desen­har uma tex­tu­ra;
✅ por que não colo­car lóg­i­ca no Draw;
✅ por que não car­regar con­teú­do por frame;
✅ como sep­a­rar a classe Player;
✅ como pen­sar em esta­dos de jogo;
✅ por que Update pensa e Draw mostra.


🏁 Conclusão: quem entende o game loop entende a alma do jogo

O game loop é a base invisív­el de qual­quer jogo.

No MonoGame, ele aparece de for­ma clara e dire­ta por meio dos méto­dos Update e Draw. Essa sim­pli­ci­dade é uma das maiores forças do frame­work: ele não esconde de você o fun­ciona­men­to essen­cial do jogo.

Quan­do você entende que o Update atu­al­iza o mun­do e o Draw desen­ha o mun­do, tudo começa a faz­er mais sen­ti­do.

O per­son­agem se move porque sua posição mudou no Update.
O jogador vê esse movi­men­to porque o Draw desen­hou o per­son­agem na nova posição.
A pon­tu­ação aumen­ta no Update.
A pon­tu­ação aparece na tela no Draw.
O jogo pausa no Update.
A tela de pause aparece no Draw.

Esse padrão vai acom­pan­har você em todo o desen­volvi­men­to com MonoGame.

Nos próx­i­mos capí­tu­los, você vai usar essa base para car­regar sprites, tra­bal­har com tex­turas, orga­ni­zar assets e começar a trans­for­mar uma janela vazia em um jogo visual­mente real.

Update é a mente do jogo. Draw é o ros­to do jogo. O game loop é o coração que man­tém tudo vivo.


❓ FAQ — Game Loop, Update e Draw no MonoGame

1. O que é game loop no MonoGame?

É o ciclo prin­ci­pal do jogo, respon­sáv­el por atu­alizar a lóg­i­ca e desen­har o resul­ta­do na tela. A classe Game do MonoGame exe­cu­ta esse loop chaman­do Update(GameTime) e Draw(GameTime).

2. Para que serve o Update?

O Update serve para atu­alizar a lóg­i­ca do jogo: input, movi­men­to, col­isão, esta­dos, pon­tu­ação, inimi­gos e regras.

3. Para que serve o Draw?

O Draw serve para ren­derizar o esta­do atu­al do jogo na tela: sprites, tex­tos, fun­do, HUD, menus e efeitos visuais.

4. Posso colocar lógica dentro do Draw?

Não é recomen­da­do. A doc­u­men­tação ofi­cial reforça que o Draw deve ren­derizar o esta­do cal­cu­la­do no Update, não con­ter lóg­i­ca com­plexa de jogo.

5. O que é GameTime?

É um obje­to que fornece infor­mações de tem­po do jogo, incluin­do o tem­po que o frame ante­ri­or lev­ou para exe­cu­tar.

6. O que é delta time?

É o tem­po decor­ri­do des­de o últi­mo frame. Ele aju­da a cri­ar movi­men­to mais con­sis­tente entre máquinas difer­entes.

7. O que é FPS?

FPS sig­nifi­ca frames por segun­do. Se um jogo roda a 60 FPS, ele ten­ta atu­alizar e desen­har 60 quadros por segun­do.

8. Onde devo carregar imagens?

Em ger­al, no LoadContent, não no Draw.

9. Onde devo desenhar sprites?

Den­tro do Draw, usan­do SpriteBatch.

10. Qual frase resume este capítulo?

Update pen­sa. Draw mostra.

Capí­tu­lo 1 — O que é MonoGame e por que usar para cri­ar jogos

Capí­tu­lo 2 — Preparan­do o Ambi­ente de Desen­volvi­men­to

Capí­tu­lo 3 — Fun­da­men­tos do Game Loop: Update e Draw

Capí­tu­lo 4 — Sprites, Tex­turas e Con­tent Pipeline

Capí­tu­lo 5 — Entra­da do Jogador: Tecla­do, Mouse e Con­t­role

Capí­tu­lo 6 — Col­isão, Físi­ca Sim­ples e Movi­men­tação

Capí­tu­lo 7 — Cenas, Menus e Arquite­tu­ra do Jogo

Capí­tu­lo 8 — Áudio, Partícu­las, Ani­mações e Poli­men­to

Capí­tu­lo 9 — Shaders, Câmera, Mapas e Per­for­mance

Capí­tu­lo 10 — Pub­li­cação, Mon­e­ti­za­ção e Pro­je­to Final no MonoGame

Posts Similares

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *