Implementando o Clássico Xonix em C++: Um Tutorial Passo a Passo

Repo: https://github.com/mrpunkdasilva/16Games-in-Cpp

Este tutorial detalha a implementação do clássico jogo Xonix, uma fascinante mistura de ação e estratégia. Vamos explorar passo a passo como o jogo é construído, desde a estrutura básica até o al…


This content originally appeared on DEV Community and was authored by Mr Punk da Silva

Repo: https://github.com/mrpunkdasilva/16Games-in-Cpp

Este tutorial detalha a implementação do clássico jogo Xonix, uma fascinante mistura de ação e estratégia. Vamos explorar passo a passo como o jogo é construído, desde a estrutura básica até o algoritmo inteligente de captura de território, tornando-o um guia útil tanto para iniciantes quanto para desenvolvedores que buscam entender lógicas de jogos mais complexas.

O que é Xonix?

Imagine um campo aberto (o "mar") com inimigos se movendo livremente. Você controla um cursor que começa na borda segura (a "terra"). Seu objetivo é se aventurar no mar para desenhar linhas e capturar novas porções de terra.

  • Captura de Território: Você desenha uma trilha na área vazia. Ao retornar para a terra firme, a área que você cercou (e que não contém inimigos) é preenchida, tornando-se sua.
  • Inimigos: Vários inimigos se movem pelo mar. Se eles tocarem sua trilha enquanto você a desenha, você perde.
  • Risco e Recompensa: Quanto maior a área que você tenta capturar de uma vez, maior o risco, mas também maior a recompensa.

Este jogo ensina conceitos cruciais como manipulação de grades (arrays 2D), algoritmos de preenchimento (flood fill) e gerenciamento de estados.

Como Organizar o Jogo

Máquina de Estados do Jogo

Para gerenciar as diferentes telas e modos de jogo (menu, jogando, pausado, fim de jogo), usamos uma máquina de estados finitos. Isso nos ajuda a separar a lógica e manter o código organizado.

enum GameState {
    MainMenu,
    Playing,
    Paused,
    GameOver
};

GameState gameState = MainMenu; // O jogo sempre começa no menu

O fluxo entre os estados é controlado pelas ações do jogador:

graph LR
    A[MainMenu] -->|Clique em Jogar| B(Playing)
    B -->|Pressiona 'P'| C{Paused}
    C -->|Pressiona 'P'| B
    B -->|Inimigo toca trilha| D[GameOver]
    B -->|Jogador toca trilha| D
    D -->|Clique em Jogar Novamente| B

A Estrutura de Dados Central: A Grade (grid)

O coração do Xonix é uma matriz 2D que representa o campo de jogo. Cada célula da grade pode ter um de quatro valores, cada um com um significado especial:

  • grid[y][x] = 0: Representa o "mar" – a área vazia e perigosa onde os inimigos se movem.
  • grid[y][x] = 1: Representa a "terra" – a área segura e já capturada. As bordas começam como terra.
  • grid[y][x] = 2: A trilha do jogador. Esta é a parte vulnerável que está sendo desenhada no mar.
  • grid[y][x] = -1: Um valor temporário usado exclusivamente pelo algoritmo de captura de território.
const int M = 25; // Altura da grade
const int N = 40; // Largura da grade
int grid[M][N] = {0}; // Inicializa tudo como 0 (mar)

As Principais Mecânicas do Jogo

Movimento do Jogador e dos Inimigos

  • Jogador: Controlado pelas setas do teclado. Ao se mover a partir da terra (1) para o mar (0), ele deixa uma trilha de 2s.
  • Inimigos: Cada inimigo (Enemy) tem uma posição e velocidade. Eles se movem em linha reta e ricocheteiam nas paredes (grid == 1), criando um movimento caótico e imprevisível.
struct Enemy {
    int x, y, dx, dy; // Posição e velocidade

    void move() {
        x += dx;
        if (grid[y / ts][x / ts] == 1) { // Se bater na terra
            dx = -dx; // Inverte a direção horizontal
            x += dx;
        }
        y += dy;
        if (grid[y / ts][x / ts] == 1) { // Se bater na terra
            dy = -dy; // Inverte a direção vertical
            y += dy;
        }
    }
};

Condições de Fim de Jogo

O jogo termina (estado GameOver) em duas situações:

  1. Inimigo Colide com a Trilha: Se um inimigo tocar uma célula com valor 2.
  2. Jogador Colide com a Própria Trilha: Se o jogador, ao se mover, entrar em uma célula que já faz parte de sua trilha atual (valor 2).
// No loop de atualização do jogador
if (grid[y][x] == 2) gameState = GameOver;

// No loop de verificação de inimigos
for (int i = 0; i < enemyCount; i++)
    if (grid[a[i].y / ts][a[i].x / ts] == 2) gameState = GameOver;

O Algoritmo de Captura de Território (Flood Fill)

Esta é a parte mais engenhosa do jogo. Quando o jogador retorna à terra firme (grid == 1), o jogo precisa decidir qual área será preenchida.

O processo ocorre em duas fases:

Fase 1: Marcar as áreas dos inimigos

O jogo usa um algoritmo de flood fill (preenchimento de área) para descobrir quais partes do "mar" são alcançáveis pelos inimigos.

  1. A função drop(y, x) é chamada para a posição de cada inimigo.
  2. Esta função é recursiva: se uma célula é mar (0), ela a marca como temporária (-1) e chama a si mesma para todos os vizinhos que também são mar.
  3. Ao final, todas as células do mar que um inimigo pode alcançar estarão marcadas com -1.
void drop(int y, int x) {
    if (grid[y][x] == 0) grid[y][x] = -1; // Marca a célula
    // Chama recursivamente para os vizinhos
    if (y > 0 && grid[y - 1][x] == 0) drop(y - 1, x);
    if (y < M - 1 && grid[y + 1][x] == 0) drop(y + 1, x);
    if (x > 0 && grid[y][x - 1] == 0) drop(y, x - 1);
    if (x < N - 1 && grid[y][x + 1] == 0) drop(y, x + 1);
}

Fase 2: Preencher a área capturada

Após marcar as áreas dos inimigos, o jogo percorre toda a grade para tomar a decisão final:

// Este loop é executado após a chamada de drop() para todos os inimigos
for (int i = 0; i < M; i++)
    for (int j = 0; j < N; j++)
        if (grid[i][j] == -1) grid[i][j] = 0; // Se foi alcançada por um inimigo, volta a ser mar
        else grid[i][j] = 1; // Caso contrário, torna-se terra firme!

Qualquer célula que não foi marcada com -1 (ou seja, a trilha do jogador e qualquer porção do mar que ficou isolada dos inimigos) é convertida em terra (1).

graph TD
    A[Jogador retorna à terra] --> B{Iniciar Captura}
    B --> C[Para cada inimigo, chamar drop, inimigo.y, inimigo.x]
    C --> D{Flood Fill marca áreas alcançáveis com -1}
    D --> E[Percorrer toda a grade]
    E --> F{Célula == -1?}
    F -- "Sim" --> G[Transformar em Mar - 0]
    F -- "Não" --> H[Transformar em Terra - 1]
    G --> E
    H --> E
    E --> I[Território Capturado]

Estrutura do Código no main.cpp

O loop principal do jogo é organizado em torno da máquina de estados.

int main() {
    // ... Inicialização de janela, texturas, fontes, etc. ...
    GameState gameState = MainMenu;

    while (window.isOpen()) {
        // ... Processamento de eventos (input do jogador) ...
        // A lógica de input muda com base no gameState (menu, jogo, etc.)

        // Lógica de atualização do jogo
        if (gameState == Playing) {
            // Mover jogador
            // Mover inimigos
            // Verificar colisões
            // Verificar se o jogador capturou uma área
        }

        // Lógica de renderização
        window.clear();
        if (gameState == MainMenu) {
            // Desenhar menu
        } else {
            // Desenhar a grade (terra, mar, trilha)
            // Desenhar jogador
            // Desenhar inimigos
            if (gameState == Paused) {
                // Desenhar texto "Pausado"
            }
            if (gameState == GameOver) {
                // Desenhar tela de "Game Over" e menu
            }
        }
        window.display();
    }
    return 0;
}

Conceitos Importantes Aprendidos

  • Manipulação de Grid 2D: Como usar uma matriz para representar um mundo de jogo complexo com diferentes tipos de terreno.
  • Algoritmo de Flood Fill: Uma aplicação prática e poderosa de recursão para análise de áreas conectadas. É um algoritmo fundamental em muitos jogos e aplicações gráficas.
  • Máquina de Estados Finitos: Um padrão de design essencial para organizar o fluxo de um jogo, tornando o código mais limpo e fácil de gerenciar.
  • Lógica de Risco vs. Recompensa: O design do jogo incentiva o jogador a tomar decisões estratégicas, equilibrando o perigo de criar uma trilha longa com a vantagem de capturar mais território.

Extensões Possíveis

O código atual é uma base excelente para adicionar novas funcionalidades:

  • Sistema de Pontuação: Adicionar pontos com base no tamanho da área capturada.
  • Níveis e Dificuldade: Aumentar o número ou a velocidade dos inimigos a cada nível.
  • Vidas: Permitir que o jogador perca mais de uma vez antes do "Game Over".
  • Power-ups: Itens que podem congelar os inimigos ou aumentar a velocidade do jogador temporariamente.


This content originally appeared on DEV Community and was authored by Mr Punk da Silva


Print Share Comment Cite Upload Translate Updates
APA

Mr Punk da Silva | Sciencx (2025-08-29T15:30:54+00:00) Implementando o Clássico Xonix em C++: Um Tutorial Passo a Passo. Retrieved from https://www.scien.cx/2025/08/29/implementando-o-classico-xonix-em-c-um-tutorial-passo-a-passo/

MLA
" » Implementando o Clássico Xonix em C++: Um Tutorial Passo a Passo." Mr Punk da Silva | Sciencx - Friday August 29, 2025, https://www.scien.cx/2025/08/29/implementando-o-classico-xonix-em-c-um-tutorial-passo-a-passo/
HARVARD
Mr Punk da Silva | Sciencx Friday August 29, 2025 » Implementando o Clássico Xonix em C++: Um Tutorial Passo a Passo., viewed ,<https://www.scien.cx/2025/08/29/implementando-o-classico-xonix-em-c-um-tutorial-passo-a-passo/>
VANCOUVER
Mr Punk da Silva | Sciencx - » Implementando o Clássico Xonix em C++: Um Tutorial Passo a Passo. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2025/08/29/implementando-o-classico-xonix-em-c-um-tutorial-passo-a-passo/
CHICAGO
" » Implementando o Clássico Xonix em C++: Um Tutorial Passo a Passo." Mr Punk da Silva | Sciencx - Accessed . https://www.scien.cx/2025/08/29/implementando-o-classico-xonix-em-c-um-tutorial-passo-a-passo/
IEEE
" » Implementando o Clássico Xonix em C++: Um Tutorial Passo a Passo." Mr Punk da Silva | Sciencx [Online]. Available: https://www.scien.cx/2025/08/29/implementando-o-classico-xonix-em-c-um-tutorial-passo-a-passo/. [Accessed: ]
rf:citation
» Implementando o Clássico Xonix em C++: Um Tutorial Passo a Passo | Mr Punk da Silva | Sciencx | https://www.scien.cx/2025/08/29/implementando-o-classico-xonix-em-c-um-tutorial-passo-a-passo/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.