Capítulo 16 — Nosso primeiro mini Game Loop no console
Série: Do zero ao jogo comercial com C++ e SFML
Pela primeira vez, nosso programa vai deixar de executar uma sequência curta e começar a se comportar como uma aplicação de jogo em execução contínua
Até agora construímos várias peças fundamentais.
Aprendemos a armazenar estado:
int playerHealth = 100;
int playerCoins = 0;
Representamos valores que não deveriam mudar:
const int DAMAGE_PER_FRAME = 5;
Criamos decisões:
if (playerHealth <= 0)
{
// ...
}
Representamos estados nomeados:
enum class GameState
{
Menu,
Playing,
GameOver
};
E aprendemos a repetir ações:
while (condition)
{
// ...
}
e:
for (int index = 0; index < 5; index++)
{
// ...
}
Cada conceito foi estudado separadamente.
Agora chegou o momento de começar a combiná-los.
Ainda não teremos:
janela
sprites
teclado
mouse
áudio
SFML
renderização
Mas construiremos algo muito importante:
uma primeira aproximação do ciclo contínuo que mantém um jogo executando.
Nosso primeiro mini Game Loop no console.
1. O problema do nosso programa atual
Considere:
#include <iostream>
int main()
{
int playerHealth = 100;
playerHealth -= 10;
std::cout << "Health: " << playerHealth << std::endl;
return 0;
}
O fluxo é:
iniciar
↓
criar estado
↓
alterar Health
↓
mostrar Health
↓
encerrar
Tudo acontece uma vez.
Um jogo real precisa de algo muito diferente.
Enquanto estiver executando, ele precisa continuar realizando trabalho.
Conceitualmente:
iniciar jogo
↓
processar
↓
atualizar
↓
mostrar estado
↓
verificar se continua
↓
repetir
↓
repetir
↓
repetir
Precisamos de um ciclo.
2. O coração de uma aplicação de jogo
Podemos imaginar algo muito simplificado:
ENQUANTO o jogo estiver rodando
processar
atualizar
mostrar
verificar
Em C++:
while (isRunning)
{
// Process
// Update
// Show
// Check
}
Isso ainda não é um Game Loop profissional.
Mas já contém a ideia central:
o jogo continua executando enquanto uma condição de continuidade permanecer verdadeira.
3. Precisamos representar se o jogo continua rodando
Podemos utilizar algo que já conhecemos:
bool
Então:
bool isRunning = true;
Agora:
while (isRunning)
{
}
significa:
enquanto
isRunningfor verdadeiro, continue executando o loop.
Se em algum momento fizermos:
isRunning = false;
na próxima verificação da condição o loop termina.
4. Nossa primeira versão mínima
Vamos começar com:
#include <iostream>
int main()
{
bool isRunning = true;
int frame = 0;
while (isRunning)
{
frame++;
std::cout << "Frame: " << frame << std::endl;
if (frame >= 5)
{
isRunning = false;
}
}
std::cout << "Game loop finished." << std::endl;
return 0;
}
Resultado:
Frame: 1
Frame: 2
Frame: 3
Frame: 4
Frame: 5
Game loop finished.
Pela primeira vez temos um programa que permanece dentro de um ciclo até uma condição determinar seu encerramento.
5. Por que chamamos a variável de frame?
Neste exemplo, frame é apenas um contador de iterações.
Estamos utilizando a palavra para começar a aproximar nosso vocabulário do desenvolvimento de jogos.
Mas atenção:
essas iterações ainda não correspondem a frames gráficos reais.
Não temos:
renderização
monitor
GPU
sincronização
deltaTime
FPS
Então, neste capítulo:
int frame
é apenas um contador didático das passagens pelo loop.
6. O ciclo completo da primeira versão
Começamos:
isRunning = true
frame = 0
A condição:
while (isRunning)
é verdadeira.
Entramos.
Executamos:
frame++;
Agora:
frame = 1
Mostramos:
Frame: 1
Testamos:
frame >= 5
Resultado:
false
Voltamos para:
while (isRunning)
Como:
isRunning = true
continuamos.
7. O momento do encerramento
Na quinta iteração:
frame = 5
Então:
if (frame >= 5)
produz:
true
Executamos:
isRunning = false;
Importante:
o programa não desaparece imediatamente naquele ponto.
Ele termina o restante da iteração atual.
Depois volta para:
while (isRunning)
Agora:
isRunning = false
e o loop termina.
8. Uma condição de continuidade
Essa ideia aparece em praticamente toda aplicação interativa:
devo continuar?
Em nosso exemplo:
bool isRunning
representa exatamente isso.
Podemos pensar:
true
→ continuar
false
→ encerrar
Ainda estamos definindo artificialmente quando o jogo termina.
Mais tarde essa condição poderá depender de eventos reais, como fechar uma janela.
9. Vamos adicionar um estado do jogo
No capítulo de enum class, aprendemos a representar estados nomeados.
Podemos reutilizar isso:
enum class GameState
{
Menu,
Playing,
GameOver
};
Agora:
GameState currentState = GameState::Menu;
Nosso programa possui:
estado da aplicação + condição de execução
São conceitos diferentes.
10. GameState e isRunning não representam a mesma coisa
Podemos ter:
GameState currentState = GameState::Playing;
bool isRunning = true;
O primeiro responde:
Em qual estado o jogo está?
O segundo:
A aplicação deve continuar executando?
Por exemplo:
GameState::GameOver
não precisa significar necessariamente que a aplicação deve fechar imediatamente.
Um jogo poderia mostrar uma tela de Game Over e continuar rodando.
Nosso exemplo será mais simples, mas essa distinção conceitual importa.
11. Uma primeira transição de estado
Podemos começar em:
GameState currentState = GameState::Menu;
Depois, durante o primeiro ciclo, simular:
currentState = GameState::Playing;
Não existe entrada real do jogador ainda.
Estamos simulando uma transição apenas para observar o funcionamento do loop.
12. Montando nosso estado inicial
Vamos utilizar:
bool isRunning = true;
int frame = 0;
int playerHealth = 100;
int playerCoins = 0;
GameState currentState = GameState::Menu;
Temos:
isRunning → continuidade da aplicação
frame → iteração atual
playerHealth → estado do jogador
playerCoins → estado econômico simples
currentState → estado geral do jogo
Nosso pequeno programa começa a possuir diversas informações evoluindo ao longo do tempo.
13. Uma primeira organização mental do Game Loop
Vamos adotar quatro etapas didáticas:
1. PROCESS
2. UPDATE
3. SHOW STATE
4. CHECK
Ainda não são módulos.
Não são classes.
Não são funções.
São apenas responsabilidades conceituais dentro do loop.
14. Etapa 1 — Process
Em um jogo real, Process poderá envolver:
teclado
mouse
controle
eventos
comandos
rede
Nós ainda não temos nada disso.
Então simularemos uma entrada.
Por exemplo, quando estivermos no menu:
if (currentState == GameState::Menu)
{
std::cout << "Starting game." << std::endl;
currentState = GameState::Playing;
}
É uma entrada simulada.
O objetivo é observar uma mudança de estado.
15. Etapa 2 — Update
Se o jogo estiver:
GameState::Playing
vamos alterar o estado.
Por exemplo:
playerCoins += 1;
playerHealth -= 5;
Isso simula uma atualização extremamente simples.
A cada iteração:
Coins aumenta
Health diminui
Não estamos criando regras finais de gameplay.
São transformações didáticas para tornar o ciclo visível.
16. Etapa 3 — Show State
Precisamos observar o que está acontecendo.
Podemos mostrar:
std::cout << "Frame: " << frame << std::endl;
std::cout << "Health: " << playerHealth << std::endl;
std::cout << "Coins: " << playerCoins << std::endl;
Isso funciona como nossa forma provisória de visualização.
No futuro essa responsabilidade deixará de ser texto no console e passará por sistemas gráficos.
17. Etapa 4 — Check
Finalmente verificamos se alguma condição exige mudança.
Por exemplo:
if (playerHealth <= 0)
{
playerHealth = 0;
currentState = GameState::GameOver;
}
Depois:
if (currentState == GameState::GameOver)
{
isRunning = false;
}
Assim:
Health chega a zero
↓
GameState vira GameOver
↓
isRunning vira false
↓
loop termina
18. Nosso primeiro mini Game Loop completo
Agora podemos reunir tudo:
#include <iostream>
enum class GameState
{
Menu,
Playing,
GameOver
};
int main()
{
bool isRunning = true;
int frame = 0;
int playerHealth = 100;
int playerCoins = 0;
GameState currentState = GameState::Menu;
while (isRunning)
{
frame++;
std::cout << std::endl;
std::cout << "=== FRAME " << frame << " ===" << std::endl;
// 1. Process
if (currentState == GameState::Menu)
{
std::cout << "Starting game." << std::endl;
currentState = GameState::Playing;
}
// 2. Update
if (currentState == GameState::Playing)
{
playerCoins += 1;
playerHealth -= 5;
}
// 3. Show State
std::cout << "Health: " << playerHealth << std::endl;
std::cout << "Coins: " << playerCoins << std::endl;
// 4. Check
if (playerHealth <= 0)
{
playerHealth = 0;
currentState = GameState::GameOver;
}
if (currentState == GameState::GameOver)
{
std::cout << "Player defeated." << std::endl;
isRunning = false;
}
}
std::cout << std::endl;
std::cout << "Game loop finished." << std::endl;
return 0;
}
Agora temos uma pequena aplicação executando continuamente até uma regra encerrá-la.
19. Existe um detalhe na primeira iteração
Começamos:
currentState = GameState::Menu;
No Process:
if (currentState == GameState::Menu)
{
currentState = GameState::Playing;
}
Logo depois, na mesma iteração, chegamos a:
if (currentState == GameState::Playing)
Agora a condição já é verdadeira.
Portanto a atualização ocorre no primeiro ciclo.
Esse comportamento é consequência da ordem das instruções.
20. Ordem dentro do loop importa
Temos:
Process
↓
Update
↓
Show
↓
Check
Se mudássemos essa ordem, poderíamos mudar o comportamento.
Por exemplo:
Check
↓
Update
não é necessariamente equivalente a:
Update
↓
Check
Imagine Health:
5
e Update:
-5
Se verificarmos antes:
5 <= 0 → false
Depois Health chega a 0.
A detecção só ocorreria em outro momento.
Ordem de responsabilidades importa.
21. Nosso loop ainda tem um pequeno problema visual
No código anterior, fazemos:
playerHealth -= 5;
Depois mostramos:
Health
Só então verificamos:
playerHealth <= 0
Na última iteração, o valor pode já estar:
0
o que funciona.
Mas se o dano fosse:
playerHealth -= 30;
poderíamos mostrar temporariamente:
Health: -20
antes de corrigir para zero.
Isso revela que talvez a validação deva acontecer antes da apresentação do estado final daquela iteração.
22. Refinando a ordem
Uma sequência melhor para nosso exemplo pode ser:
PROCESS
↓
UPDATE
↓
CHECK
↓
SHOW STATE
Agora:
simulamos entrada;
atualizamos;
garantimos consistência;
mostramos o estado validado.
Isso evita mostrar Health negativo.
Nosso mini loop pode evoluir.
23. Refatorando a ordem conceitual
Dentro do while:
// 1. Process
// 2. Update
// 3. Check
// 4. Show State
Observe:
ainda não estamos criando funções.
Estamos apenas tornando as responsabilidades mais explícitas.
24. Um limite de segurança para o exercício
Nosso loop já possui uma condição natural de saída:
Health chega a zero
Com:
playerHealth -= 5;
ele terminará.
Mas durante aprendizado é útil evitar que um erro acidental produza uma execução indefinida.
Podemos utilizar temporariamente:
const int MAX_FRAMES = 100;
e:
if (frame >= MAX_FRAMES)
{
isRunning = false;
}
Isso funciona como uma proteção didática.
Não é uma arquitetura de produção.
25. Por que uma proteção pode ajudar?
Imagine que por engano removamos:
playerHealth -= 5;
Agora Health nunca chega a zero.
Então:
currentState
nunca chega a:
GameState::GameOver
e:
isRunning
pode permanecer verdadeiro indefinidamente.
Durante um exercício, MAX_FRAMES ajuda a impedir isso.
26. Isso não significa que jogos reais terminam após 100 frames
É importante separar:
proteção de exercício
de:
regra real do jogo
Um jogo real não terá:
if (frame >= 100)
{
closeGame();
}
apenas para evitar loop infinito.
Mais tarde nossa continuidade será controlada por eventos e pelo lifecycle da aplicação.
27. Código final refinado
Vamos utilizar:
#include <iostream>
enum class GameState
{
Menu,
Playing,
GameOver
};
int main()
{
const int DAMAGE_PER_FRAME = 5;
const int COINS_PER_FRAME = 1;
const int MAX_FRAMES = 100;
bool isRunning = true;
int frame = 0;
int playerHealth = 100;
int playerCoins = 0;
GameState currentState = GameState::Menu;
std::cout << "=== MINI GAME LOOP START ===" << std::endl;
while (isRunning)
{
frame++;
// 1. Process
if (currentState == GameState::Menu)
{
std::cout << "Starting game." << std::endl;
currentState = GameState::Playing;
}
// 2. Update
if (currentState == GameState::Playing)
{
playerHealth -= DAMAGE_PER_FRAME;
playerCoins += COINS_PER_FRAME;
}
// 3. Check
if (playerHealth <= 0)
{
playerHealth = 0;
currentState = GameState::GameOver;
}
if (frame >= MAX_FRAMES)
{
isRunning = false;
}
// 4. Show State
std::cout << std::endl;
std::cout << "Frame: " << frame << std::endl;
std::cout << "Health: " << playerHealth << std::endl;
std::cout << "Coins: " << playerCoins << std::endl;
if (currentState == GameState::Playing)
{
std::cout << "State: Playing" << std::endl;
}
if (currentState == GameState::GameOver)
{
std::cout << "State: GameOver" << std::endl;
std::cout << "Player defeated." << std::endl;
isRunning = false;
}
}
std::cout << std::endl;
std::cout << "=== MINI GAME LOOP FINISHED ===" << std::endl;
std::cout << "Frames: " << frame << std::endl;
std::cout << "Final Health: " << playerHealth << std::endl;
std::cout << "Final Coins: " << playerCoins << std::endl;
return 0;
}
Agora temos nossa primeira implementação que realmente merece ser chamada de:
mini Game Loop didático.
28. Qual será o resultado?
Começamos:
Health = 100
Coins = 0
A cada frame:
Health -= 5
Coins += 1
Então:
Frame 1
Health 95
Coins 1
Frame 2
Health 90
Coins 2
Frame 3
Health 85
Coins 3
E assim por diante.
Depois de vinte atualizações:
Health = 0
Coins = 20
Então:
GameState::GameOver
e finalmente:
isRunning = false
29. Podemos prever o número de iterações
Temos:
Initial Health = 100
Damage per Frame = 5
Então:
100 / 5 = 20
Esperamos aproximadamente vinte atualizações até Health chegar a zero.
Isso nos dá um resultado esperado que podemos utilizar na validação.
30. Resultado final esperado
Ao término:
Frames: 20
Final Health: 0
Final Coins: 20
Por quê?
Porque a cada uma das vinte iterações:
Health perde 5
Coins ganha 1
Temos:
20 × 5 = 100 de dano
20 × 1 = 20 moedas
Nosso mini loop produz um estado previsível.
31. Isso é importante para testes
Podemos definir:
Entrada inicial
Health = 100
Coins = 0
Damage = 5
Coins per Frame = 1
Resultado esperado:
Frames = 20
Health = 0
Coins = 20
State = GameOver
Agora executamos e comparamos.
Esse pensamento será essencial quando começarmos a automatizar testes.
32. Ainda não temos um verdadeiro conceito de tempo
Observe:
playerHealth -= DAMAGE_PER_FRAME;
Estamos dizendo:
por iteração
e não:
por segundo
Nossa execução depende da velocidade com que o computador percorre o loop.
Não existe:
deltaTime
elapsedTime
clock
frame time
fixed timestep
ainda.
Portanto:
frame neste capítulo não é uma unidade de tempo confiável.
Essa distinção será fundamental quando chegarmos ao verdadeiro Game Loop gráfico.
33. Não coloque regras reais dependentes da velocidade do loop
Imagine futuramente:
playerPosition += 1.0f;
a cada iteração.
Em uma máquina mais rápida, isso poderia acontecer mais vezes por segundo.
Em outra, menos.
O movimento ficaria dependente da velocidade de execução.
Esse problema é uma das razões pelas quais sistemas de tempo são fundamentais em games.
Mas ainda não precisamos resolver isso.
Primeiro estamos aprendendo a estrutura.
34. O mini Game Loop tem quatro responsabilidades conceituais
Nossa versão atual mostra:
PROCESS
↓
UPDATE
↓
CHECK
↓
SHOW
Mais tarde essas ideias serão refinadas.
Talvez vejamos estruturas como:
Process Events
Handle Input
Update
Render
ou divisões ainda mais sofisticadas.
Mas o princípio já começou a aparecer:
um loop central coordena diferentes responsabilidades do jogo.
35. Por enquanto são apenas comentários
Temos:
// 1. Process
// 2. Update
// 3. Check
// 4. Show State
Isso não cria arquitetura.
São marcadores visuais.
Mais tarde provavelmente veremos algo parecido com:
ProcessInput();
Update();
Render();
Mas ainda não aprendemos funções.
Não vamos utilizá-las antes de explicar o problema que elas resolvem.
36. E o problema está começando a aparecer
Observe nosso main().
Ele agora possui:
constantes
variáveis
estado
while
processamento
atualização
validação
exibição
encerramento
Tudo dentro de um único método.
O código ainda é compreensível.
Mas já começa a crescer.
Esse crescimento é importante.
Ele está preparando naturalmente nosso próximo assunto.
37. Não vamos resolver isso antecipadamente
Seria tentador escrever imediatamente:
Process();
Update();
Check();
Render();
Mas até agora ainda não estudamos:
declaração de funções
definição
assinatura
parâmetros
retorno
chamada
Se fizéssemos isso agora, estaríamos apenas escondendo o problema atrás de uma sintaxe não explicada.
Primeiro precisamos sentir a necessidade.
Agora estamos começando a senti-la.
38. Erro proposital — nunca encerrar o loop
Remova temporariamente:
playerHealth -= DAMAGE_PER_FRAME;
e também remova ou aumente bastante a proteção de MAX_FRAMES.
O que acontece?
playerHealth continua:
100
Então:
playerHealth <= 0
nunca será verdadeiro.
GameState::GameOver não será atingido.
Logo:
isRunning
permanece verdadeiro.
Podemos criar uma repetição indefinida.
Interrompa a execução e investigue.
39. Método de investigação
Pergunte:
Qual condição mantém o while executando?
isRunning
Depois:
Onde isRunning deveria mudar?
E:
Qual condição leva até essa mudança?
Por exemplo:
Health <= 0
Depois:
Health está sendo atualizado?
Essa cadeia de perguntas é mais útil do que alterar código aleatoriamente.
40. Erro proposital — encerrar cedo demais
Agora faça:
bool isRunning = false;
antes do loop.
Temos:
while (isRunning)
que equivale a:
while (false)
Resultado:
0 iterações
O programa pula diretamente para:
MINI GAME LOOP FINISHED
Isso confirma novamente:
whiletesta antes de executar.
41. Erro proposital — dano muito alto
Altere:
const int DAMAGE_PER_FRAME = 60;
Temos:
Frame 1
Health = 40
Frame 2
Health = -20
Mas nosso Check faz:
if (playerHealth <= 0)
{
playerHealth = 0;
currentState = GameState::GameOver;
}
Portanto mostramos:
Health: 0
e não:
Health: -20
Nossa validação protege a representação final.
42. Testando diferentes cenários
Use:
Cenário A
const int DAMAGE_PER_FRAME = 10;
Esperado:
10 frames
Cenário B
const int DAMAGE_PER_FRAME = 25;
Esperado:
4 frames
Cenário C
const int DAMAGE_PER_FRAME = 100;
Esperado:
1 frame
Verifique se os resultados correspondem à regra.
43. Um cenário interessante: dano que não divide exatamente a vida
Use:
const int DAMAGE_PER_FRAME = 30;
Temos:
100
↓
70
↓
40
↓
10
↓
-20
Depois corrigimos para:
0
Logo esperamos:
4 frames
Não:
3.333...
Estamos contando iterações discretas do loop.
44. Tabela de cenários
Podemos organizar:
Health inicial
Dano por frame
Frames esperados
Health final
1005200100101001002540100304010010010
Agora já temos uma pequena especificação de comportamento.
45. Observe como vários conhecimentos estão trabalhando juntos
Temos constantes:
const int DAMAGE_PER_FRAME = 5;
Variáveis:
int playerHealth = 100;
Boolean:
bool isRunning = true;
Enumeração:
GameState currentState = GameState::Menu;
Loop:
while (isRunning)
Decisões:
if (...)
Operadores:
-=
+=
++
<=
>=
==
Tudo aquilo que aprendemos está começando a formar um comportamento maior.
46. Esse é um marco importante da série
Nosso programa não é mais apenas:
algumas instruções isoladas
Agora temos:
estado
+
repetição
+
decisão
+
transformação
+
encerramento
Isso começa a se parecer com uma aplicação interativa, mesmo que ainda não exista interação real.
É nossa primeira base conceitual de Game Loop.
47. Mas ainda não é o Game Loop que teremos na Engine
Precisamos deixar isso muito claro.
Nosso loop atual não possui:
event polling
input real
window lifecycle
renderer
time step
delta time
fixed update
variable update
frame limiting
vertical synchronization
resource lifecycle
scene management
audio
physics
Não devemos pegar este código e tratá-lo como arquitetura final.
Ele existe para ensinar a ideia central:
continuar
↓
processar
↓
atualizar
↓
validar
↓
mostrar
↓
repetir
48. A Engine nascerá de problemas reais
Quando tivermos uma janela, surgirá um problema de lifecycle.
Quando tivermos input, surgirá processamento de eventos.
Quando tivermos movimento, surgirá o problema de tempo.
Quando tivermos múltiplas telas, surgirá o problema de cenas.
Quando tivermos recursos, surgirá o problema de ownership e gerenciamento.
Só então abstrações como:
Application
Game
Scene
Renderer
Input
Time
começarão a fazer sentido.
Hoje não precisamos delas.
49. Estrutura atual do projeto
Continuamos:
CppGameEngine/
└── main.cpp
Sim.
Mesmo com nosso primeiro mini Game Loop.
Ainda não vamos criar pastas apenas para antecipar arquitetura futura.
O código ainda cabe em um único arquivo.
Mas algo está mudando.
main.cpp já está começando a acumular responsabilidades.
Essa será nossa próxima dor real.
50. Validando o código final
Salve:
Ctrl + S
Faça Build:
Ctrl + Shift + B
Confirme:
Build succeeded
Execute:
Ctrl + F5
Verifique especialmente:
Starting game.
Depois as atualizações sucessivas.
E no final:
State: GameOver
Player defeated.
seguido por:
MINI GAME LOOP FINISHED
51. Checklist de compreensão
Antes de avançar, tente responder sem consultar o artigo:
O que estamos chamando de mini Game Loop?
Qual papel
isRunningpossui?Por que utilizamos
while?Qual diferença existe entre
GameStateeisRunning?O que significa Process?
O que significa Update?
Por que precisamos validar o estado depois de atualizá-lo?
Por que a ordem das etapas importa?
Por que nosso
framenão representa ainda um frame gráfico real?Por que não devemos relacionar gameplay real à velocidade deste loop?
Para que serve
MAX_FRAMESneste exercício?Por que essa proteção não representa uma regra real de produção?
Como um loop infinito poderia surgir?
Por que nosso
main.cppcomeçou a ficar mais difícil de organizar?Por que ainda não criamos funções?
Se consegue explicar essas respostas, estamos prontos para a próxima evolução.
52. Git
Depois da validação:
git status
Adicione:
git add CppGameEngine/main.cpp
Ajuste o caminho caso sua estrutura local seja diferente.
Commit sugerido:
git commit -m "feat: add first console mini game loop"
Esse commit marca um ponto importante do projeto:
nosso programa passou a executar atualizações repetidas enquanto uma condição de execução permanece ativa.
53. Atualizando o CHANGELOG
Em [Unreleased]:
### Added
- Added the first console-based mini Game Loop.
- Added continuous execution controlled by an `isRunning` state.
- Added basic `GameState` transitions between menu, playing, and game over.
- Added repeated player state updates for health and coins.
- Added loop termination based on gameplay state.
- Added a temporary maximum-frame safeguard for learning scenarios.
Depois:
git add CHANGELOG.md
git commit -m "docs: update changelog with first mini game loop"
Neste ponto ainda não precisamos alterar a arquitetura descrita no ROADMAP técnico.
54. O que aprendemos?
Antes deste capítulo sabíamos repetir:
while (condition)
{
}
Mas hoje utilizamos essa repetição para construir algo maior:
while (isRunning)
{
// Process
// Update
// Check
// Show State
}
Aprendemos que:
uma aplicação de jogo precisa de um mecanismo de execução contínua;
isRunningpode representar a condição de continuidade;estado do jogo e lifecycle da aplicação são conceitos distintos;
cada iteração pode transformar o estado;
a ordem Process → Update → Check → Show pode alterar o comportamento;
um loop precisa possuir uma condição real de término;
erros na atualização podem gerar loops infinitos;
frameainda é apenas um contador didático;não possuímos controle temporal real;
este mini loop não é nossa arquitetura final;
conceitos anteriores começaram a trabalhar juntos dentro de um único fluxo;
nosso
main.cppcomeçou a acumular responsabilidades.
Conclusão
Este capítulo representa uma mudança importante em nossa jornada.
Começamos a série com:
std::cout << "Game Engineering com C++ começou!" << std::endl;
Depois nosso programa aprendeu:
armazenar estado
↓
representar tipos
↓
proteger constantes
↓
realizar operações
↓
avaliar expressões
↓
tomar decisões
↓
escolher alternativas
↓
representar estados
↓
repetir ações
Agora finalmente juntamos tudo em:
INICIAR
↓
ENQUANTO ESTIVER RODANDO
│
├── PROCESSAR
├── ATUALIZAR
├── VALIDAR
└── MOSTRAR
│
└───────────────┐
│
▼
REPETIR
Nosso programa passou a ter um ciclo de execução.
Ainda é pequeno.
Ainda é console.
Ainda não existe SFML.
Mas conceitualmente demos um dos passos mais importantes da série.
Temos pela primeira vez algo que se aproxima da estrutura central de um jogo.
E exatamente por isso um novo problema ficou muito visível.
Nosso main() agora faz tudo:
cria variáveis
define estado
controla loop
processa
atualiza
valida
mostra
encerra
Se continuarmos adicionando comportamento dessa maneira, ele crescerá rapidamente.
Imagine adicionar:
player
enemy
inventory
farm
time
weather
NPC
input
collision
rendering
dentro do mesmo main().
Claramente isso não vai escalar.
Precisamos começar a separar comportamentos em unidades menores.
E é exatamente daí que nasce nosso próximo assunto.
Próximo capítulo
Capítulo 17 — Funções: nosso main.cpp começou a crescer
Série: Do zero ao jogo comercial com C++ e SFML
Partiremos do mini Game Loop que acabamos de construir.
Hoje temos algo conceitualmente parecido com:
while (isRunning)
{
// Process
// Update
// Check
// Show State
}
Mas todo o comportamento continua dentro de:
int main()
No próximo capítulo vamos observar o problema real:
main.cpp cresce
↓
responsabilidades se misturam
↓
código fica difícil de navegar
↓
precisamos separar comportamentos
Só então introduziremos funções.
Começaremos entendendo:
o que uma função realmente é;
por que ela precisa existir;
declaração e definição;
nome da função;
tipo de retorno;
parênteses;
corpo;
chamada;
fluxo de execução;
como o programa entra e retorna de uma função;
por que funções não existem apenas para “diminuir código”;
como separar responsabilidades sem criar abstrações prematuras;
como nosso mini Game Loop ficará mais legível;
e por que esse será nosso primeiro passo real para tirar responsabilidades de dentro do
main().
Vamos evoluir de algo como:
while (isRunning)
{
// várias instruções
}
para uma estrutura progressivamente mais clara, conceitualmente próxima de:
while (isRunning)
{
Process();
Update();
Check();
ShowState();
}
Mas desta vez não vamos apenas copiar essas chamadas.
Vamos compreender como uma função nasce, por que ela existe e o que realmente acontece quando ela é chamada.
Nosso Game Loop finalmente existe.
Agora nosso próximo problema é torná-lo organizado o suficiente para continuar crescendo.
Comentários
Ainda não há comentários. Sê o primeiro a comentar!