Aller au contenu

GameWorld

Le GameWorld est le cœur du serveur, responsable de l'état du jeu.

Responsabilités

  • Gestion des entités (joueurs, ennemis, missiles)
  • Simulation physique
  • Détection de collisions
  • Gestion des vagues d'ennemis
  • Broadcast de l'état aux clients

Architecture

classDiagram
    class GameWorld {
        -vector~Player~ players_
        -vector~Enemy~ enemies_
        -vector~Missile~ missiles_
        -WaveManager waveManager_
        -uint32_t currentTick_
        +tick()
        +addPlayer(Player)
        +removePlayer(playerId)
        +processInput(playerId, Input)
        +getSnapshot() GameSnapshot
    }

    class WaveManager {
        -int currentWave_
        -float waveTimer_
        +update(dt)
        +spawnWave()
    }

    class Player {
        -uint32_t id_
        -float x, y
        -int health_
        -queue~Input~ inputs_
    }

    GameWorld --> WaveManager
    GameWorld --> Player

Tick System

Le serveur broadcast les snapshots à 20 Hz (50ms par tick).

// Dans UDPServer.cpp
static constexpr int BROADCAST_INTERVAL_MS = 50;  // 20 Hz

class GameWorld {
    static constexpr float TICK_DURATION = 0.05f;  // 50ms

public:
    void tick() {
        processInputs();
        updatePhysics();
        updateEnemies();
        checkCollisions();
        cleanup();
        currentTick_++;
    }

private:
    void processInputs() {
        for (auto& player : players_) {
            while (auto input = player.popInput()) {
                applyInput(player, *input);
            }
        }
    }

    void applyInput(Player& player, const Input& input) {
        const float SPEED = 300.0f;

        if (input.keys & KEY_UP)    player.y -= SPEED * TICK_DURATION;
        if (input.keys & KEY_DOWN)  player.y += SPEED * TICK_DURATION;
        if (input.keys & KEY_LEFT)  player.x -= SPEED * TICK_DURATION;
        if (input.keys & KEY_RIGHT) player.x += SPEED * TICK_DURATION;

        // Clamp to bounds
        player.x = std::clamp(player.x, 0.0f, WORLD_WIDTH);
        player.y = std::clamp(player.y, 0.0f, WORLD_HEIGHT);

        if (input.keys & KEY_SHOOT) {
            tryShoot(player);
        }

        player.lastAckedInput = input.sequence;
    }
};

Wave Manager

Gestion des vagues d'ennemis.

class WaveManager {
    struct WaveConfig {
        int basicCount;
        int zigzagCount;
        int followerCount;
        int shooterCount;
        bool hasBoss;
    };

    static constexpr WaveConfig WAVES[] = {
        {5, 0, 0, 0, false},   // Wave 1
        {3, 3, 0, 0, false},   // Wave 2
        {2, 2, 2, 0, false},   // Wave 3
        {2, 2, 2, 2, false},   // Wave 4
        {0, 0, 0, 0, true},    // Wave 5 - BOSS
    };

    int currentWave_ = 0;
    float spawnTimer_ = 0;
    int enemiesRemaining_ = 0;

public:
    void update(GameWorld& world, float dt) {
        if (enemiesRemaining_ <= 0 && world.enemies().empty()) {
            // Wave complete
            currentWave_++;
            if (currentWave_ < std::size(WAVES)) {
                spawnWave(world);
            } else {
                world.setVictory();
            }
        }
    }

    void spawnWave(GameWorld& world) {
        auto& config = WAVES[currentWave_];

        for (int i = 0; i < config.basicCount; i++)
            world.spawnEnemy(EnemyType::Basic);
        for (int i = 0; i < config.zigzagCount; i++)
            world.spawnEnemy(EnemyType::Zigzag);
        // ...

        if (config.hasBoss)
            world.spawnEnemy(EnemyType::Boss);
    }
};

Snapshot Generation

Création de l'état pour broadcast.

GameSnapshot GameWorld::getSnapshot() const {
    GameSnapshot snapshot;
    snapshot.tick = currentTick_;

    // Players
    snapshot.playerCount = players_.size();
    for (size_t i = 0; i < players_.size(); i++) {
        auto& p = players_[i];
        snapshot.players[i] = {
            .id = p.id_,
            .x = static_cast<uint16_t>(p.x),
            .y = static_cast<uint16_t>(p.y),
            .health = static_cast<uint8_t>(p.health_),
            .alive = p.isAlive(),
            .lastAckedInput = p.lastAckedInput_
        };
    }

    // Enemies
    snapshot.enemyCount = enemies_.size();
    for (size_t i = 0; i < enemies_.size(); i++) {
        auto& e = enemies_[i];
        snapshot.enemies[i] = {
            .id = e.id_,
            .type = static_cast<uint8_t>(e.type_),
            .x = static_cast<uint16_t>(e.x),
            .y = static_cast<uint16_t>(e.y),
            .health = static_cast<uint8_t>(e.health_)
        };
    }

    // Missiles...

    return snapshot;
}

Constantes

Constante Valeur Description
BROADCAST_INTERVAL_MS 50 Broadcast 20 Hz
WORLD_WIDTH 1920 Largeur monde
WORLD_HEIGHT 1080 Hauteur monde
PLAYER_SPEED 200 Pixels/seconde
MISSILE_SPEED 600 Pixels/seconde
MAX_PLAYERS 4 Joueurs max
PLAYER_TIMEOUT_MS 2000 Timeout déconnexion