Aeca

Capítulo 10 — Decisões: ensinando nosso programa a escolher

Série: Do zero ao jogo comercial com C++ e SFML

Capítulo 10 — Decisões: ensinando nosso programa a escolher
aeca

Nosso código já calcula resultados. Agora ele precisa observar uma situação e decidir se deve agir.

Até agora, nosso programa executa suas instruções praticamente em sequência.

Algo parecido com:

  criar variáveis
↓
calcular valores
↓
alterar estado
↓
mostrar resultados
↓
encerrar

Nos últimos capítulos já conseguimos escrever:

  playerHealth -= damage;
playerCoins += reward;

Também construímos expressões como:

  int finalDamage = (baseDamage + bonusDamage) * criticalMultiplier;

Mas existe um problema cada vez mais evidente.

Imagine:

  playerHealth -= damage;

E se o dano for maior que a vida atual?

Poderíamos terminar com:

  Health: -40

Ou imagine:

  playerCoins -= itemPrice;

E se o jogador não possuir moedas suficientes?

Poderíamos produzir:

  Coins: -75

O programa sabe calcular.

Mas ainda não sabe perguntar:

Essa operação deveria acontecer?

Para isso, precisamos começar a tomar decisões.


1. O problema que já existe em nosso código

Considere:

  int playerHealth = 30;
int damage = 50;

playerHealth -= damage;

O cálculo será:

  30 - 50 = -20

Tecnicamente, C++ consegue realizar essa operação.

Mas talvez nossa regra seja:

  Health nunca deve ficar abaixo de zero.

Então precisamos de uma lógica semelhante a:

  calcular nova vida
↓
nova vida ficou abaixo de zero?
↓
SIM → usar zero
NÃO → usar o valor calculado

Estamos começando a falar sobre decisão.


2. O programa precisa avaliar uma condição

Uma decisão começa com uma pergunta.

Por exemplo:

  playerHealth é maior que zero?

Ou:

  playerCoins é suficiente para a compra?

Ou:

  damage ultrapassou a vida atual?

Essas perguntas podem produzir dois resultados fundamentais:

  true
false

Já conhecemos esse tipo:

  bool

Agora ele começa a mostrar por que é tão importante.


3. Uma comparação produz um valor booleano

Considere:

  playerHealth > 0

Se:

  playerHealth = 30

temos:

  30 > 0

Resultado:

  true

Agora:

  playerHealth = 0

Temos:

  0 > 0

Resultado:

  false

Portanto:

  playerHealth > 0

é uma expressão.

Só que, desta vez, o resultado dela é:

  bool

4. Do capítulo anterior para este

No Capítulo 09 vimos expressões como:

  baseDamage + bonusDamage

que podem produzir um número.

Agora teremos expressões como:

  playerHealth > 0

que produzem:

  true

ou:

  false

Podemos visualizar:

  playerHealth > 0
       ↓
    comparação
       ↓
     bool
       ↓
 true ou false

Essa será a base das nossas decisões.


5. Nosso primeiro operador de comparação: >

O operador:

  >

significa:

  maior que

Exemplo:

  int playerHealth = 30;

bool isAlive = playerHealth > 0;

A expressão:

  playerHealth > 0

é avaliada.

Como:

  30 > 0

é verdadeiro:

  isAlive

recebe:

  true

6. Vamos observar isso

  #include <iostream>

int main()
{
    int playerHealth = 30;
    bool isAlive = playerHealth > 0;

    std::cout << std::boolalpha;
    std::cout << "Health: " << playerHealth << std::endl;
    std::cout << "Is Alive: " << isAlive << std::endl;

    return 0;
}

Resultado:

  Health: 30
Is Alive: true

Agora altere:

  int playerHealth = 0;

Resultado:

  Health: 0
Is Alive: false

Nosso programa já consegue avaliar uma condição.

Mas ainda não está escolhendo executar ou não uma ação.


7. Conhecendo if

C++ possui uma estrutura de decisão chamada:

  if

Sua forma inicial é:

  if (condition)
{
    // código executado se a condição for verdadeira
}

Podemos ler:

se a condição for verdadeira, execute este bloco.

É exatamente o comportamento de que precisamos.


8. Nosso primeiro if

  #include <iostream>

int main()
{
    int playerHealth = 30;

    if (playerHealth > 0)
    {
        std::cout << "Player is alive." << std::endl;
    }

    return 0;
}

Temos:

  playerHealth > 0

Se essa expressão produzir:

  true

o bloco:

  {
    std::cout << "Player is alive." << std::endl;
}

será executado.


9. Visualizando o fluxo

Podemos imaginar:

  playerHealth > 0?
       │
       ├── true
       │     ↓
       │  mostrar mensagem
       │
       └── false
             ↓
          ignorar bloco

Esse detalhe é importante.

Quando a condição é falsa, o programa não para.

Ele simplesmente não executa aquele bloco e continua depois dele.


10. Testando os dois cenários

Primeiro:

  int playerHealth = 30;

Resultado:

  Player is alive.

Agora:

  int playerHealth = 0;

O bloco:

  if (playerHealth > 0)

não será executado.

Se não houver nenhuma outra saída no programa, você simplesmente não verá aquela mensagem.

Temos nossa primeira alteração real de fluxo.


11. O programa não executa mais tudo obrigatoriamente

Antes:

  instrução A
↓
instrução B
↓
instrução C

Agora:

  instrução A
↓
condição
├── true  → executa B
└── false → ignora B
↓
instrução C

Essa mudança parece pequena.

Mas ela é uma das bases de praticamente qualquer sistema interativo.


12. Aplicando isso ao dano

Vamos voltar ao problema:

  int playerHealth = 30;
int damage = 50;

Podemos primeiro calcular:

  int newHealth = playerHealth - damage;

Resultado:

  -20

Agora podemos perguntar:

  newHealth < 0

Se for verdadeiro, precisamos impedir que nossa vida final permaneça negativa.


13. O operador <

O operador:

  <

significa:

  menor que

Então:

  newHealth < 0

pergunta:

newHealth é menor que zero?

Se:

  newHealth = -20

temos:

  -20 < 0

Resultado:

  true

Agora podemos usar essa condição.


14. Protegendo a vida contra valores negativos

  #include <iostream>

int main()
{
    int playerHealth = 30;
    int damage = 50;

    int newHealth = playerHealth - damage;

    if (newHealth < 0)
    {
        newHealth = 0;
    }

    playerHealth = newHealth;

    std::cout << "Health: " << playerHealth << std::endl;

    return 0;
}

Resultado:

  Health: 0

Nosso código agora aplica uma regra:

se o cálculo produzir uma vida negativa, use zero.


15. A expressão vem antes da decisão

Observe a sequência:

  int newHealth = playerHealth - damage;

if (newHealth < 0)
{
    newHealth = 0;
}

Primeiro temos uma expressão aritmética:

  playerHealth - damage

Depois uma expressão de comparação:

  newHealth < 0

Essa comparação produz:

  true

ou:

  false

E o if utiliza esse resultado para decidir se executará o bloco.

Os capítulos estão começando a se conectar:

  variáveis
↓
tipos
↓
constantes
↓
operadores
↓
expressões
↓
condições
↓
decisões

16. Outros operadores de comparação

Precisaremos de mais algumas formas de comparação.

Para nossa etapa atual:

Operador Significado

> maior que

< menor que

>= maior ou igual

<= menor ou igual

== igual

!= diferente

Vamos entendê-los com pequenos exemplos.


17. Maior ou igual: >=

Imagine que um item custe:

  int itemPrice = 50;

e o jogador tenha:

  int playerCoins = 50;

Ele possui dinheiro suficiente?

A condição pode ser:

  playerCoins >= itemPrice

Temos:

  50 >= 50

Resultado:

  true

O jogador não precisa possuir mais que o preço.

Possuir exatamente o preço também é suficiente.


18. Menor ou igual: <=

Imagine:

  playerHealth <= 0

Podemos interpretar:

a vida chegou a zero ou ficou abaixo dele?

Com:

  playerHealth = 0

a condição é verdadeira.

Com:

  playerHealth = -10

também é verdadeira.

Mais tarde poderemos usar isso para representar estados como derrota.


19. Igualdade com ==

Aqui existe um cuidado importantíssimo.

Para comparar igualdade usamos:

  ==

Exemplo:

  playerLevel == 2

Isso pergunta:

playerLevel possui valor igual a 2?

Compare com:

  playerLevel = 2;

Esse código faz atribuição.

São operações completamente diferentes.


20. = não é ==

Temos:

  =

atribuição.

E:

  ==

comparação de igualdade.

Compare:

  playerLevel = 2;

com:

  playerLevel == 2

Primeiro:

  atribui 2

Segundo:

  pergunta se é igual a 2

Essa diferença é pequena visualmente e enorme semanticamente.


21. Erro clássico — confundir = com ==

Quando queríamos verificar:

  if (playerLevel == 2)
{
    std::cout << "Level reached." << std::endl;
}

um iniciante pode escrever por engano:

  if (playerLevel = 2)
{
    std::cout << "Level reached." << std::endl;
}

Não faça isso.

A segunda construção contém uma atribuição, não a comparação que queríamos representar.

Compiladores modernos podem emitir warnings dependendo da situação.

Nosso princípio:

warning merece investigação.

Não ignore avisos simplesmente porque o projeto compilou.


22. Diferente com !=

O operador:

  !=

significa:

  diferente de

Por exemplo:

  int divisor = 5;

if (divisor != 0)
{
    std::cout << "Division can be performed." << std::endl;
}

Estamos dizendo:

execute o bloco apenas se divisor for diferente de zero.

Esse é exatamente um dos problemas que encontramos no Capítulo 08.


23. Evitando uma divisão inteira por zero

Podemos escrever:

  #include <iostream>

int main()
{
    int totalExperience = 200;
    int divisor = 100;

    if (divisor != 0)
    {
        int result = totalExperience / divisor;
        std::cout << "Result: " << result << std::endl;
    }

    return 0;
}

Agora a divisão só acontece quando:

  divisor != 0

produz:

  true

Estamos começando a executar operações apenas quando suas condições são adequadas.


24. Uma condição já é uma expressão booleana

Considere:

  playerCoins >= itemPrice

O resultado é:

  true

ou:

  false

Portanto poderíamos fazer:

  bool hasEnoughCoins = playerCoins >= itemPrice;

E depois:

  if (hasEnoughCoins)
{
    std::cout << "Player can buy the item." << std::endl;
}

Isso torna explícito que o if trabalha com uma condição booleana.


25. Podemos usar a expressão diretamente

Também podemos escrever:

  if (playerCoins >= itemPrice)
{
    std::cout << "Player can buy the item." << std::endl;
}

As duas abordagens podem ser úteis.

Versão com variável:

  bool hasEnoughCoins = playerCoins >= itemPrice;

if (hasEnoughCoins)
{
}

Versão direta:

  if (playerCoins >= itemPrice)
{
}

A melhor escolha depende da clareza.


26. Nomes booleanos podem explicar regras

Compare:

  if (playerCoins >= itemPrice)
{
}

com:

  bool hasEnoughCoins = playerCoins >= itemPrice;

if (hasEnoughCoins)
{
}

A segunda versão dá um nome à pergunta:

  hasEnoughCoins

Isso pode ajudar quando a condição começa a representar um conceito relevante.

Mas não criaremos variáveis booleanas desnecessariamente para toda comparação simples.


27. Comprando um item

Vamos montar um pequeno exemplo:

  #include <iostream>

int main()
{
    int playerCoins = 80;
    int itemPrice = 50;

    if (playerCoins >= itemPrice)
    {
        playerCoins -= itemPrice;

        std::cout << "Item purchased." << std::endl;
    }

    std::cout << "Coins: " << playerCoins << std::endl;

    return 0;
}

Como:

  80 >= 50

é verdadeiro, a compra acontece.

Depois:

  80 - 50
=
30

Resultado:

  Item purchased.
Coins: 30

28. Agora teste com poucas moedas

Altere:

  int playerCoins = 30;

Temos:

  30 >= 50

Resultado:

  false

Portanto este bloco:

  {
    playerCoins -= itemPrice;

    std::cout << "Item purchased." << std::endl;
}

não é executado.

Resultado:

  Coins: 30

O dinheiro não ficou negativo.

Nosso programa tomou uma decisão.


29. Mas falta uma resposta para o outro caso

Perceba algo.

Quando o jogador possui moedas:

  Item purchased.

Quando não possui:

  ...

Nada acontece.

Talvez quiséssemos mostrar:

  Not enough coins.

Precisaríamos representar:

  se condição verdadeira
→ faça uma coisa

caso contrário
→ faça outra

Essa necessidade existe.

Mas ela será exatamente o assunto do Capítulo 11 — Quando existem alternativas.

Neste capítulo permaneceremos concentrados no if.


30. Uma decisão não precisa alterar estado

Podemos utilizar if apenas para reagir.

Por exemplo:

  int playerHealth = 0;

if (playerHealth <= 0)
{
    std::cout << "Player defeated." << std::endl;
}

Nenhuma variável é alterada dentro do bloco.

Ainda assim existe uma decisão:

  Health <= 0?
↓
true
↓
mostrar mensagem

31. Várias decisões podem existir

Considere:

  int playerHealth = 20;
int playerCoins = 80;

if (playerHealth > 0)
{
    std::cout << "Player is alive." << std::endl;
}

if (playerCoins >= 50)
{
    std::cout << "Player can buy the item." << std::endl;
}

São dois if independentes.

O programa avalia os dois.

Isso é diferente de uma estrutura em que apenas uma entre várias alternativas deve ser escolhida.

Novamente, chegaremos a isso depois.


32. Não confunda múltiplos if com alternativas exclusivas

Considere:

  if (playerHealth > 0)
{
    std::cout << "Player is alive." << std::endl;
}

if (playerCoins >= 50)
{
    std::cout << "Player has enough coins." << std::endl;
}

As duas condições podem ser verdadeiras.

Então os dois blocos podem executar.

Não estamos dizendo:

  execute um OU outro

Estamos dizendo:

  avalie a primeira condição

depois

avalie também a segunda

Essa distinção será importante nos próximos capítulos.


33. Condições não devem ser perguntas vagas

Isto é claro:

  playerHealth <= 0

Representa uma pergunta objetiva.

Já regras mal definidas como:

  o jogador está meio fraco?

precisam primeiro ser transformadas em critérios concretos.

Talvez:

  playerHealth <= LOW_HEALTH_THRESHOLD

Mas essa regra ainda precisaria ser definida.

Antes de escrever um if, precisamos saber exatamente:

qual condição representa a decisão?


34. Código é a tradução de uma regra

Considere a regra:

O jogador só pode comprar o item se possuir moedas suficientes.

Podemos decompor:

  dados necessários
↓
playerCoins
itemPrice

pergunta
↓
playerCoins >= itemPrice

ação se verdadeira
↓
playerCoins -= itemPrice

Em C++:

  if (playerCoins >= itemPrice)
{
    playerCoins -= itemPrice;
}

Esse processo é extremamente importante.

Não começamos pela sintaxe.

Começamos pela regra.


35. Outro exemplo: experiência necessária

Imagine:

  const int EXPERIENCE_REQUIRED = 100;

int playerExperience = 120;

Queremos apenas informar que o jogador atingiu a quantidade necessária.

  if (playerExperience >= EXPERIENCE_REQUIRED)
{
    std::cout << "Required experience reached." << std::endl;
}

Temos:

  120 >= 100
↓
true
↓
executar bloco

Ainda não implementaremos toda a progressão de nível.

Isso exigirá outras decisões e regras.


36. Evitando regras que ainda não definimos

Seria fácil escrever agora:

  playerLevel++;
playerExperience = 0;

dentro do if.

Mas isso levantaria várias perguntas:

  Experiência excedente é perdida?

O jogador pode subir vários níveis?

A experiência necessária muda?

Existe nível máximo?

O que acontece no nível máximo?

Não definimos essas regras.

Portanto não devemos inventá-las silenciosamente.

Neste capítulo nosso objetivo é aprender decisões, não construir o sistema completo de progressão.


37. Blocos e chaves

Observe:

  if (playerHealth <= 0)
{
    std::cout << "Player defeated." << std::endl;
}

As chaves:

  {
}

delimitam o bloco associado ao if.

Mesmo quando existe apenas uma instrução, manteremos as chaves.

Isso nos dá uma convenção consistente e reduz riscos quando o bloco crescer.


38. Não faremos isto

C++ permite situações como:

  if (playerHealth <= 0)
    std::cout << "Player defeated." << std::endl;

Mas em nosso projeto manteremos:

  if (playerHealth <= 0)
{
    std::cout << "Player defeated." << std::endl;
}

Clareza é mais importante que economizar duas linhas.


39. Indentação também comunica estrutura

Compare:

  if (playerHealth <= 0)
{
std::cout << "Player defeated." << std::endl;
}

com:

  if (playerHealth <= 0)
{
    std::cout << "Player defeated." << std::endl;
}

Os dois podem representar a mesma estrutura sintática.

Mas o segundo deixa visualmente claro que a instrução pertence ao bloco.

Código é lido muito mais vezes do que escrito.

Legibilidade importa.


40. Cuidado com o ponto e vírgula após if

Observe este código incorreto para nossa intenção:

  if (playerHealth <= 0);
{
    std::cout << "Player defeated." << std::endl;
}

Existe um:

  ;

imediatamente depois da condição.

Isso encerra uma instrução vazia associada ao if.

O bloco seguinte não funciona como imaginamos.

Esse tipo de erro pode ser particularmente perigoso porque o código pode compilar.


41. Erro proposital — if com ;

Teste:

  #include <iostream>

int main()
{
    int playerHealth = 100;

    if (playerHealth <= 0);
    {
        std::cout << "Player defeated." << std::endl;
    }

    return 0;
}

Mesmo com:

  Health = 100

você poderá observar a mensagem sendo exibida.

Pergunte:

  Por que o bloco executou?

Onde está o ponto e vírgula?

O que o if realmente controla?

Depois corrija:

  if (playerHealth <= 0)
{
    std::cout << "Player defeated." << std::endl;
}

42. Warnings começam a ficar ainda mais valiosos

Esse tipo de código pode gerar warnings dependendo do compilador e das configurações:

  if (playerHealth <= 0);

Não ignore warnings.

Nosso processo deve ser:

  Build succeeded

não significa automaticamente:

  código perfeito

Também observe:

  warnings
resultado
comportamento

Compilação é apenas uma etapa da validação.


43. Erro proposital — = em vez de ==

Teste:

  int playerLevel = 1;

if (playerLevel = 2)
{
    std::cout << "Level 2." << std::endl;
}

Depois compare com:

  if (playerLevel == 2)
{
    std::cout << "Level 2." << std::endl;
}

Não se limite a corrigir.

Explique:

  =  → atribuição

== → comparação

Essa diferença precisa ficar automática ao longo da prática.


44. Condições com bool

Como já possuímos o tipo:

  bool

também podemos utilizá-lo diretamente:

  bool isAlive = true;

if (isAlive)
{
    std::cout << "Player can continue." << std::endl;
}

Não precisamos escrever:

  if (isAlive == true)

quando:

  if (isAlive)

já comunica claramente a intenção.


45. Uma condição booleana já carrega verdadeiro ou falso

Considere:

  bool isAlive = true;

Então:

  if (isAlive)

pergunta conceitualmente:

isAlive é verdadeiro?

Isso é suficiente.

Da mesma maneira, futuramente poderemos trabalhar com condições mais complexas sem escrever comparações redundantes.


46. Ainda não vamos combinar condições

Talvez você esteja pensando em algo como:

  o jogador está vivo
E
possui moedas suficientes

C++ possui operadores lógicos para isso.

Por exemplo, veremos conceitos como:

  AND
OR
NOT

Mas ainda não precisamos introduzi-los aqui.

Nosso objetivo neste artigo é dominar:

  uma condição
↓
um if
↓
uma ação condicional

Complexidade virá progressivamente.


47. Nosso primeiro fluxo de decisão útil

Vamos juntar uma regra simples de dano:

  #include <iostream>

int main()
{
    int playerHealth = 30;
    int damage = 50;

    int newHealth = playerHealth - damage;

    if (newHealth < 0)
    {
        newHealth = 0;
    }

    playerHealth = newHealth;

    if (playerHealth <= 0)
    {
        std::cout << "Player defeated." << std::endl;
    }

    std::cout << "Health: " << playerHealth << std::endl;

    return 0;
}

Resultado:

  Player defeated.
Health: 0

Agora existem duas decisões independentes:

  newHealth < 0?

e:

  playerHealth <= 0?

48. Testando outro cenário

Altere:

  int damage = 10;

Agora:

  30 - 10 = 20

Então:

  newHealth < 0

é falso.

E:

  playerHealth <= 0

também é falso.

Resultado:

  Health: 20

Nenhum dos blocos executa.


49. Um pequeno cenário de compra

Também podemos testar:

  #include <iostream>

int main()
{
    int playerCoins = 80;
    int itemPrice = 50;

    std::cout << "Coins Before Purchase: " << playerCoins << std::endl;

    if (playerCoins >= itemPrice)
    {
        playerCoins -= itemPrice;

        std::cout << "Item purchased." << std::endl;
    }

    std::cout << "Coins After Purchase: " << playerCoins << std::endl;

    return 0;
}

Resultado:

  Coins Before Purchase: 80
Item purchased.
Coins After Purchase: 30

Agora teste:

  int playerCoins = 30;

Resultado:

  Coins Before Purchase: 30
Coins After Purchase: 30

A compra não acontece.


50. Nosso código agora possui caminhos

Isso é novo.

Antes:

  sempre executa

Agora temos:

  condição verdadeira → determinado bloco executa

ou:

  condição falsa → determinado bloco é ignorado

Nosso programa começou a ter fluxo condicional.

Isso está muito mais próximo do comportamento de um jogo.


51. Muitos sistemas futuros dependerão disso

Imagine:

  Se Health <= 0 → jogador derrotado

Se Coins >= Price → permitir compra

Se Experience >= Required → progressão disponível

Se Inventory estiver cheio → impedir coleta

Se Door estiver destrancada → permitir passagem

Se Crop estiver madura → permitir colheita

Se NPC estiver próximo → permitir interação

Cada uma dessas regras começa com uma condição.


52. Não precisamos construir esses sistemas agora

Esses exemplos mostram onde estamos indo.

Não significam que criaremos agora:

  InventorySystem
EconomySystem
NpcSystem
InteractionSystem
FarmingSystem

Ainda estamos aprendendo controle de fluxo.

O projeto continuará simples:

  CppGameEngine/
└── main.cpp

A arquitetura crescerá quando o problema exigir.


53. Microdesafio — jogador vivo

Comece:

  int playerHealth = 25;

Mostre:

  Player is alive.

somente quando:

  playerHealth > 0

Uma possível solução:

  if (playerHealth > 0)
{
    std::cout << "Player is alive." << std::endl;
}

Depois teste:

  playerHealth = 0;

A mensagem não deve aparecer.


54. Microdesafio — experiência

Comece:

  const int EXPERIENCE_REQUIRED = 100;
int playerExperience = 120;

Mostre:

  Experience requirement reached.

apenas se o jogador possuir experiência suficiente.

Uma possível solução:

  if (playerExperience >= EXPERIENCE_REQUIRED)
{
    std::cout << "Experience requirement reached." << std::endl;
}

Agora teste:

  playerExperience = 80;

55. Microdesafio — divisão segura

Comece:

  int value = 100;
int divisor = 5;

Só execute:

  int result = value / divisor;

quando:

  divisor != 0

Uma possível implementação:

  if (divisor != 0)
{
    int result = value / divisor;

    std::cout << "Result: " << result << std::endl;
}

Depois teste:

  int divisor = 0;

A divisão não deve acontecer.


56. Microdesafio — compra

Comece:

  int playerCoins = 75;
int itemPrice = 40;

A compra só deve acontecer quando:

  playerCoins >= itemPrice

Dentro do if:

  playerCoins -= itemPrice;

Resultado esperado:

  Coins: 35

Depois teste:

  playerCoins = 25;

O valor deve permanecer:

  25

57. Código final do capítulo

Vamos reunir algumas decisões simples, sem introduzir alternativas com else ainda:

  #include <iostream>

int main()
{
    const int EXPERIENCE_REQUIRED = 100;

    int playerHealth = 30;
    int damage = 50;
    int playerCoins = 80;
    int itemPrice = 50;
    int playerExperience = 120;

    std::cout << "=== INITIAL STATE ===" << std::endl;
    std::cout << "Health: " << playerHealth << std::endl;
    std::cout << "Coins: " << playerCoins << std::endl;
    std::cout << "Experience: " << playerExperience << std::endl;

    int newHealth = playerHealth - damage;

    if (newHealth < 0)
    {
        newHealth = 0;
    }

    playerHealth = newHealth;

    if (playerHealth <= 0)
    {
        std::cout << std::endl;
        std::cout << "Player defeated." << std::endl;
    }

    if (playerCoins >= itemPrice)
    {
        playerCoins -= itemPrice;

        std::cout << "Item purchased." << std::endl;
    }

    if (playerExperience >= EXPERIENCE_REQUIRED)
    {
        std::cout << "Experience requirement reached." << std::endl;
    }

    std::cout << std::endl;
    std::cout << "=== CURRENT STATE ===" << std::endl;
    std::cout << "Health: " << playerHealth << std::endl;
    std::cout << "Coins: " << playerCoins << std::endl;
    std::cout << "Experience: " << playerExperience << std::endl;

    return 0;
}

Resultado esperado:

  === INITIAL STATE ===
Health: 30
Coins: 80
Experience: 120

Player defeated.
Item purchased.
Experience requirement reached.

=== CURRENT STATE ===
Health: 0
Coins: 30
Experience: 120

58. O que esse programa já consegue decidir?

Temos três perguntas diferentes:

  Health chegou a zero?

Há Coins suficientes?

Experience atingiu o necessário?

Cada uma possui:

  condição
↓
true ou false
↓
ação opcional

Nosso programa finalmente começou a reagir ao estado.


59. Limitação proposital

Quando:

  playerCoins < itemPrice

nada acontece.

Não mostramos:

  Insufficient coins.

Quando:

  playerHealth > 0

também não mostramos uma mensagem alternativa.

Isso é proposital.

Ainda sabemos apenas dizer:

  SE isso for verdadeiro → execute

No próximo capítulo precisaremos dizer:

  SE isso for verdadeiro → faça A

CASO CONTRÁRIO → faça B

Essa necessidade dará origem ao próximo recurso.


60. Validando o capítulo

Faça:

  Ctrl + S

Depois:

  Ctrl + Shift + B

Confirme:

  Build succeeded

Execute:

  Ctrl + F5

Depois altere os valores para produzir cenários diferentes.

Cenário A

  int playerHealth = 100;
int damage = 20;

Esperado:

  Health: 80

Cenário B

  int playerHealth = 20;
int damage = 50;

Esperado:

  Player defeated.
Health: 0

Cenário C

  int playerCoins = 100;
int itemPrice = 40;

Esperado:

  Item purchased.
Coins: 60

Cenário D

  int playerCoins = 20;
int itemPrice = 40;

Esperado:

  Coins: 20

A compra não deve ocorrer.


61. Checklist de compreensão

Antes de avançar, tente responder:

  • O que é uma condição?

  • Qual tipo de resultado uma comparação produz?

  • Para que serve if?

  • O que acontece quando a condição é falsa?

  • Qual é a diferença entre = e ==?

  • O que fazem >, <, >=, <=, == e !=?

  • Por que usamos chaves no bloco?

  • Por que if (condition); pode causar um bug?

  • Por que uma operação matematicamente válida pode ainda violar uma regra do jogo?

  • Qual é a diferença entre vários if independentes e escolher uma alternativa?

Se alguma dessas respostas ainda estiver confusa, execute novamente os exemplos.


62. Git

Depois da validação:

  git status

Adicione:

  git add CppGameEngine/main.cpp

Ajuste o caminho se necessário.

Commit sugerido:

  git commit -m "feat: add conditional gameplay decisions"

Esse commit representa uma evolução importante:

nosso programa agora avalia condições antes de executar determinadas ações.


63. Atualizando o CHANGELOG

Em [Unreleased]:

  ### Added

- Added conditional gameplay decisions using `if`.
- Added comparison operators for health, coins, and experience rules.
- Added health clamping logic to prevent negative values.
- Added conditional item purchase validation.
- Added safe execution example for potentially invalid operations.

Depois:

  git add CHANGELOG.md
git commit -m "docs: update changelog with conditional decisions"

O ROADMAP técnico continua sem necessidade de alteração neste ponto.


64. O que aprendemos?

Até o capítulo anterior tínhamos expressões como:

  playerHealth - damage

Agora aprendemos a produzir expressões booleanas:

  playerHealth <= 0

Conhecemos operadores de comparação:

  >
<
>=
<=
==
!=

e utilizamos:

  if (condition)
{
    // ação
}

para modificar o fluxo do programa.

Também aprendemos que:

  • condições produzem true ou false;

  • if executa seu bloco somente quando a condição é verdadeira;

  • = é atribuição;

  • == é comparação;

  • vários if independentes podem executar no mesmo fluxo;

  • chaves tornam a estrutura explícita;

  • um ; colocado indevidamente depois do if pode gerar comportamento incorreto;

  • warnings merecem atenção;

  • regras do jogo precisam ser transformadas em condições objetivas;

  • não devemos inventar sistemas completos quando ainda estamos estudando o fundamento que eles utilizarão.


Conclusão

Nosso programa começou como uma sequência totalmente linear.

Depois passou a armazenar valores.

Aprendeu tipos.

Ganhou constantes.

Passou a transformar dados com operadores.

Aprendeu a combinar operações em expressões.

Agora finalmente consegue fazer algo novo:

  PERGUNTAR
↓
AVALIAR
↓
DECIDIR
↓
AGIR

Isso muda profundamente o que podemos construir.

Agora já podemos representar regras como:

  Se Health ficar abaixo de zero → usar zero

Se Coins forem suficientes → realizar compra

Se Experience atingir o necessário → informar progressão

Se divisor for diferente de zero → realizar divisão

Nosso programa deixou de apenas executar cálculos.

Ele começou a reagir aos resultados desses cálculos.

Mas ainda existe uma limitação.

Observe:

  if (playerCoins >= itemPrice)
{
    std::cout << "Item purchased." << std::endl;
}

Quando a condição é verdadeira, sabemos o que fazer.

Mas quando ela é falsa?

Queremos provavelmente outra resposta:

  Coins suficientes?
│
├── SIM → comprar
│
└── NÃO → informar que faltam moedas

Agora temos duas alternativas.

E isso nos leva diretamente ao próximo capítulo.


Próximo capítulo

Capítulo 11 — Quando existem alternativas

Série: Do zero ao jogo comercial com C++ e SFML

No próximo artigo vamos partir exatamente desta limitação:

  if (playerCoins >= itemPrice)
{
    std::cout << "Item purchased." << std::endl;
}

e perceber que nosso programa precisa representar dois caminhos:

  condição verdadeira → executar uma ação

condição falsa → executar outra ação

Será o momento de conhecer:

  else

Depois avançaremos para situações em que existem mais de duas possibilidades, preparando naturalmente o caminho para:

Capítulo 12 — Muitas opções: quando switch ajuda.

Nosso programa já sabe perguntar.

Agora ele aprenderá a responder de maneiras diferentes dependendo da resposta.

Subscreve "Aeca" para receber atualizações diretamente na tua caixa de entrada
aeca

Subscreve aeca para reagir

Subscrever

Comentários

Ainda não há comentários. Sê o primeiro a comentar!

Subscreve Aeca para receber atualizações diretamente na tua caixa de entrada