Aeca

Capítulo 08 — Operadores: transformando dados

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

Capítulo 08 — Operadores: transformando dados
aeca

Nosso estado já existe. Agora precisamos calcular, modificar e combinar seus valores.

Nos capítulos anteriores, nosso pequeno programa começou a armazenar diferentes tipos de informações e a separar estado mutável de regras constantes.

Agora surge um novo problema: um jogo não pode apenas armazenar estado. Ele precisa transformá-lo.

Quando o jogador sofre dano:

  Health: 100Health: 80

Quando recebe moedas:

  Coins: 25Coins: 40

Quando recebe experiência:

  Experience: 40Experience: 100

Para realizar essas transformações precisamos executar operações sobre os dados.

É aqui que entram os operadores.


1. Começando pelo problema

Imagine este estado:

  int playerHealth = 100;
int playerCoins = 25;
int playerExperience = 40;

Agora três acontecimentos ocorrem:

Player sofre 20 de dano

Player recebe 15 moedas

Player recebe 60 pontos de experiência

Queremos chegar a:

Health: 80

Coins: 40

Experience: 100

Já utilizamos algumas operações nos capítulos anteriores:

  playerHealth = playerHealth - 20;
playerCoins = playerCoins + 10;

Agora vamos entender formalmente o que está acontecendo.


2. O que é um operador?

Um operador é um símbolo da linguagem que representa uma operação realizada sobre um ou mais operandos.

Observe:

  playerHealth - 20

Temos dois operandos, playerHealth e 20, ligados pelo operador -.

Se playerHealth vale 100, a expressão produz 80.

Mas calcular 80 não significa automaticamente atualizar a variável.


3. Calcular não é atualizar o estado

Isto:

  playerHealth - 20;

calcula um resultado, mas não o armazena novamente em playerHealth.

Para atualizar o estado:

  playerHealth = playerHealth - 20;

Conceitualmente:

  playerHealth = 100100 - 2080
↓
playerHealth = 80

Primeiro calculamos. Depois atribuímos.


4. O operador de atribuição =

Em:

  playerHealth = 80;

podemos ler:

atribua o valor 80 a playerHealth.

Não devemos confundir esse = com a igualdade matemática tradicional.


5. Adição com +

  int playerCoins = 25;
playerCoins = playerCoins + 15;

Resultado:

  25 + 15 = 40

No contexto do jogo, adição pode representar recompensa, coleta ou ganho.


6. Subtração com -

  int playerHealth = 100;
playerHealth = playerHealth - 20;

Resultado:

  100 - 20 = 80

Subtração pode representar dano, gasto, consumo ou perda.


7. Multiplicação com *

Imagine um ataque crítico:

  int damage = 20;
int criticalDamage = damage * 2;

Resultado:

  Critical Damage: 40

Ainda não temos um sistema de combate. O exemplo existe apenas para dar contexto ao operador.


8. Divisão com /

  int totalExperience = 200;
int experiencePerLevel = 100;
int completedLevels = totalExperience / experiencePerLevel;

Resultado:

Completed Levels: 2

Mas existe um detalhe importante: divisão inteira.


9. Divisão inteira

  int result = 5 / 2;

O resultado será:

  2

e não 2.5.

Agora compare:

  float result = 5.0f / 2.0f;

Resultado:

2.5

Tipos e operadores trabalham juntos.

E atenção:

  float result = 5 / 2;

continua realizando primeiro uma divisão entre inteiros. O resultado 2 é então armazenado como ponto flutuante.

Esse comportamento será ainda mais importante no próximo capítulo, quando estudarmos expressões.


10. Divisão por zero

Uma expressão como:

  int result = 10 / 0;

não representa uma operação válida para nosso programa; divisão inteira por zero leva a comportamento indefinido.

Quando estudarmos decisões, poderemos verificar condições antes de dividir.

Por enquanto, guarde a regra:

  • o divisor precisa ser válido antes da divisão.


11. Resto da divisão com %

O operador % obtém o resto de uma divisão inteira.

5 % 2 = 1

4 % 2 = 0

Exemplo:

  int totalExperience = 250;
int experiencePerLevel = 100;
int completedLevels = totalExperience / experiencePerLevel;
int remainingExperience = totalExperience % experiencePerLevel;

Resultado:

Completed Levels: 2

Remaining Experience: 50


12. Os operadores aritméticos deste capítulo

| Operador | Operação | Exemplo |

  |-----|--------------------------|-------------------------|
|  +  | Adição                   | playerCoins + 10        |
|  -  | Subtração                | playerHealth - 20       |
| `*` | Multiplicação            | `damage * 2`            |
| `/` | Divisão                  | `totalExperience / 100` |
| `%` | Resto da divisão inteira | `totalExperience % 100` |

Não precisamos estudar todos os operadores C++ de uma vez. Estamos aprendendo os que resolvem nossos problemas atuais.


13. Atribuição composta

Compare:

  playerCoins = playerCoins + 15;

com:

  playerCoins += 15;

Para essa atualização simples, a segunda forma comunica diretamente:

- adicione 15 ao valor atual de playerCoins.

Também podemos escrever:

  playerHealth -= 20;
damage *= 2;
experience /= 2;

Os principais operadores de atribuição composta vistos aqui são:

  • +=

  • -=

  • *=

  • /=

  • %=


14. Incremento com ++

Para subir exatamente um nível:

  playerLevel = playerLevel + 1;

ou:

  playerLevel += 1;

C++ também oferece:

  playerLevel++;

Se playerLevel vale 1, depois passa a valer 2.


15. Decremento com --

  itemCount--;

Se itemCount vale 5, depois passa a valer 4.

Temos:

++ → incrementa uma unidade

-- → decrementa uma unidade

Existem formas prefixa e pós-fixa:

  ++playerLevel;
playerLevel++;

A diferença entre elas se torna importante quando o valor da própria expressão é utilizado. Como o próximo capítulo é sobre expressões, deixaremos essa análise para o momento adequado.


16. Não compacte código apenas porque consegue

Código menor não é automaticamente melhor.

Nossa prioridade continuará sendo:

  clareza
↓
intenção
↓
correção
↓
manutenção

Operadores são ferramentas, não uma competição para escrever a menor quantidade possível de caracteres.


17. Tipos influenciam operações

Já vimos:

5 / 2 → 2

5.0f / 2.0f → 2.5

Isso mostra que o significado e o resultado de uma operação dependem dos tipos envolvidos.

Até o + pode aparecer em outros contextos. Com std::string, por exemplo, podemos combinar textos:

  std::string playerName = "Alex";
std::string message = "Player: " + playerName;

Resultado:

Player: Alex

Não aprofundaremos concatenação agora. O ponto importante é perceber a relação entre tipo e operação.


18. E o << que usamos desde o começo?

Também é um operador:

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

No contexto de streams, estamos utilizando << para inserção no fluxo de saída.

Continuaremos aprendendo-os conforme surgirem problemas reais.


19. Ordem das operações começa a importar

Considere:

  int result = 10 + 5 * 2;

O resultado é:

20

porque a multiplicação possui precedência maior:

10 + (5 * 2)

10 + 10

20

Agora:

  int result = (10 + 5) * 2;

produz:

30

Parênteses alteram o agrupamento e também podem tornar a intenção mais explícita.

Não precisamos decorar agora uma tabela gigantesca de precedência. Esse assunto será aprofundado no próximo capítulo.


20. Aplicando operadores ao estado do jogador

Vamos reunir os conceitos:

  const int MAX_PLAYER_HEALTH = 100;

int playerHealth = MAX_PLAYER_HEALTH;
int playerCoins = 25;
int playerExperience = 40;
int playerLevel = 1;

playerHealth -= 20;
playerCoins += 15;
playerExperience += 60;
playerLevel++;

Resultado:

Health: 80

Coins: 40

Experience: 100

Level: 2

Nosso programa agora está realizando uma pequena transformação de estado.


21. Implementação completa

  #include <iostream>

int main()
{
    const int MAX_PLAYER_HEALTH = 100;

    int playerHealth = MAX_PLAYER_HEALTH;
    int playerCoins = 25;
    int playerExperience = 40;
    int playerLevel = 1;

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

    playerHealth -= 20;
    playerCoins += 15;
    playerExperience += 60;
    playerLevel++;

    std::cout << std::endl;
    std::cout << "=== UPDATED STATE ===" << std::endl;
    std::cout << "Health: " << playerHealth << std::endl;
    std::cout << "Coins: " << playerCoins << std::endl;
    std::cout << "Experience: " << playerExperience << std::endl;
    std::cout << "Level: " << playerLevel << std::endl;

    return 0;
}

Resultado:

  === INITIAL STATE ===
Health: 100
Coins: 25
Experience: 40
Level: 1

=== UPDATED STATE ===
Health: 80
Coins: 40
Experience: 100
Level: 2

22. Operação válida não significa regra válida

Podemos escrever:

  playerHealth -= 200;

e acabar com:

Health: -100

Também podemos escrever:

  playerCoins -= 1000;

e produzir moedas negativas.

Isso nos ensina uma diferença fundamental:

operação válida em C++

regra válida para o jogo

O compilador conhece tipos e operações. Ele não conhece automaticamente nossas regras de gameplay.

Quando estudarmos decisões, começaremos a proteger essas regras.


23. Não precisamos de DamageSystem

Temos apenas:

  playerHealth -= 20;

Ainda não precisamos criar:

DamageSystem

CombatManager

HealthComponent

EventBus

Nossa evolução continua seguindo:

problema

solução simples

uso

limitação

novo conhecimento

refatoração quando necessária

A estrutura permanece:

  CppGameEngine/
└── main.cpp

24. Erro proposital — esquecendo a atribuição

Teste:

  int playerHealth = 100;

playerHealth - 20;

std::cout << playerHealth << std::endl;

Resultado:

100

A expressão calculou um valor, mas playerHealth não foi atualizado.

Corrija para:

  playerHealth -= 20;

Agora:

80

Esse experimento ajuda a separar calcular de armazenar o resultado.


25. Erro proposital — divisão inteira inesperada

Teste:

  float result = 5 / 2;

std::cout << result << std::endl;

Depois altere para:

  float result = 5.0f / 2.0f;

Compare os resultados.

A pergunta importante não é apenas “por que deu diferente?”, mas:

- quais eram os tipos dos operandos quando a divisão aconteceu?


26. Microdesafio — dano e recompensa

Comece com:

  int playerHealth = 100;
int playerCoins = 25;

Aplique:

30 de dano

20 moedas de recompensa

Resultado esperado:

Health: 70

Coins: 45

Uma possível solução:

  playerHealth -= 30;
playerCoins += 20;

27. Microdesafio — ataque crítico

Comece com:

  int baseDamage = 15;
int criticalMultiplier = 2;

Calcule:

Critical Damage: 30

Uma possível solução:

  int criticalDamage = baseDamage * criticalMultiplier;

Observe que preservamos baseDamage e armazenamos o resultado separadamente.

Nem toda operação precisa modificar o operando original.


28. Microdesafio — experiência

Comece com:

  int totalExperience = 250;
int experiencePerLevel = 100;

Calcule:

Completed Levels: 2

Remaining Experience: 50

Uma possível solução:

  int completedLevels = totalExperience / experiencePerLevel;
int remainingExperience = totalExperience % experiencePerLevel;

Aqui / e % trabalham juntos para resolver um pequeno problema.


29. Código final do capítulo

  #include <iostream>

int main()
{
    const int MAX_PLAYER_HEALTH = 100;
    const int EXPERIENCE_PER_LEVEL = 100;

    int playerHealth = MAX_PLAYER_HEALTH;
    int playerCoins = 25;
    int playerExperience = 40;
    int playerLevel = 1;
    int baseDamage = 15;
    int criticalMultiplier = 2;

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

    int criticalDamage = baseDamage * criticalMultiplier;

    playerHealth -= criticalDamage;
    playerCoins += 20;
    playerExperience += 160;

    int completedLevels = playerExperience / EXPERIENCE_PER_LEVEL;
    int remainingExperience = playerExperience % EXPERIENCE_PER_LEVEL;

    std::cout << std::endl;
    std::cout << "=== AFTER GAMEPLAY EVENTS ===" << std::endl;
    std::cout << "Critical Damage: " << criticalDamage << std::endl;
    std::cout << "Health: " << playerHealth << std::endl;
    std::cout << "Coins: " << playerCoins << std::endl;
    std::cout << "Total Experience: " << playerExperience << std::endl;
    std::cout << "Completed Level Units: " << completedLevels << std::endl;
    std::cout << "Remaining Experience: " << remainingExperience << std::endl;

    return 0;
}

Resultado esperado:

  === INITIAL STATE ===
Health: 100
Coins: 25
Experience: 40
Level: 1

=== AFTER GAMEPLAY EVENTS ===
Critical Damage: 30
Health: 70
Coins: 45
Total Experience: 200
Completed Level Units: 2
Remaining Experience: 0

Não atualizamos automaticamente playerLevel a partir de completedLevels, porque isso exigiria definir uma regra concreta de progressão. Nosso objetivo atual é entender operadores, não antecipar todo o sistema de níveis.


30. Validação

Antes do commit:

1. Save

2. Build

3. Run

4. Compare os resultados

5. Faça os erros propositalmente

6. Corrija

7. Execute novamente

No Visual Studio:

Save → Ctrl + S

Build → Ctrl + Shift + B

Run → Ctrl + F5

Confirme que os valores exibidos correspondem às operações realizadas.


31. Git

Verifique:

  git status

Adicione o arquivo real:

  git add CppGameEngine/main.cpp

Ajuste o caminho se sua estrutura local for diferente.

Commit sugerido:

  git commit -m "feat: add player state arithmetic operations"

32. Atualizando o CHANGELOG

Em [Unreleased]:

  ### Added

- Added arithmetic operations for player state updates.
- Added compound assignment examples for health, coins, and experience.
- Added critical damage calculation using multiplication.
- Added integer division and remainder examples for experience progression.

Commit:

  git add CHANGELOG.md
git commit -m "docs: update changelog with arithmetic operations"

O ROADMAP técnico não precisa ser alterado a cada fundamento individual.


33. O que aprendemos?

Conhecemos:

+ adição

- subtração

* multiplicação

/ divisão

% resto da divisão inteira

Também utilizamos:

+=

-=

*=

/=

%=

++

--

E descobrimos que:

- calcular não é o mesmo que armazenar o resultado;

- tipos influenciam operações;

- divisão inteira descarta a parte fracionária;

- mudar apenas o tipo da variável de destino não muda a operação que já aconteceu;

- divisão por zero precisa ser evitada;

- % permite obter o resto de uma divisão inteira;

- precedência começa a importar quando combinamos operadores;

- parênteses podem alterar o agrupamento;

- uma operação válida para C++ não é necessariamente uma regra válida para nosso jogo.


Conclusão

Nosso programa já sabia lembrar informações.

Agora começou a transformá-las:

Health

100 - 20 → 80

Coins

25 + 15 → 40

Damage

15 * 2 → 30

Experience

250 / 100 → 2

Remaining Experience

250 % 100 → 50

Jogos são sistemas em constante transformação. Input, tempo, movimento, combate, economia, inventário, NPCs e progressão alterarão continuamente o estado.

Ainda estamos no console, mas a lógica que futuramente ficará por trás de sprites, animações e interfaces já começou a nascer.

E nossas operações estão começando a ficar maiores:

  int finalDamage = baseDamage + bonusDamage * multiplier;

O que acontece primeiro?

E por que isto:

  int finalDamage = (baseDamage + bonusDamage) * multiplier;

pode produzir outro resultado?

Para responder corretamente, precisamos compreender uma ideia maior: expressões.


Próximo capítulo

Capítulo 09 — Expressões: combinando valores e operações

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

No próximo artigo vamos partir de operações que já conhecemos:

  baseDamage + bonusDamage
baseDamage * multiplier
playerHealth - damage

e começar a combiná-las:

  (baseDamage + bonusDamage) * multiplier

Vamos compreender progressivamente operandos, operadores, expressões, subexpressões, precedência, associatividade, parênteses e tipo do resultado, sem transformar o capítulo em uma tabela gigantesca de regras.

Até aqui transformamos dados com operações relativamente simples.

No próximo capítulo começaremos a entender como C++ combina essas operações para produzir resultados mais complexos.

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