C++によるスネークゲームの動的体節管理実装

スネークゲームのコア機能として、動的な体節管理ロジックの実装を検討します。主な要件は以下の3点です:
  • 進行方向の動的変更
  • 体節の動的追加
  • 座標の連続更新による移動処理
方向制御と位置情報の定義を以下のように実装します。列挙型で進行方向を表現し、位置情報は構造体で管理します。
enum class MovementDirection {
    NORTH,
    SOUTH,
    WEST,
    EAST
};

struct Position {
    int x;
    int y;
};

struct BodySegment {
    BodySegment* next;
    char symbol;
    Position current;
};

class SnakeHead {
public:
    char symbol;
    MovementDirection direction;
    size_t length;
    Position position;
    BodySegment* head;
};
スネーク本体の管理クラスを定義します。体節の追加・移動処理をカプセル化し、ゲームループから独立した動作を実現します。
class MovingSnake {
public:
    MovingSnake();
    ~MovingSnake();
    
    void initialize(char symbol, MovementDirection dir, size_t length, const Position& pos);
    void setDirection(MovementDirection dir);
    void extendBody(const Position& newHead, char symbol);
    Position advance();

private:
    SnakeHead m_head;
};
方向変更は単純なプロパティ更新で実装します。体節追加処理では、新しい頭部をリスト先頭に挿入し、現在の頭部情報を保持します。
void MovingSnake::setDirection(MovementDirection dir) {
    m_head.direction = dir;
}

void MovingSnake::extendBody(const Position& newHead, char symbol) {
    BodySegment* segment = new BodySegment{m_head.head, m_head.symbol, m_head.position};
    m_head.head = segment;
    m_head.position = newHead;
    m_head.symbol = symbol;
    m_head.length++;
}
移動処理の実装で重要なのは、体節の連続更新と末尾処理です。以下のアプローチを採用します:
  1. 新規頭部を追加(extendBodyを呼び出し)
  2. 体長が1の場合:現在位置を保持して新規体節生成
  3. 体長が2以上の場合:末尾体節を削除し座標情報を返却
  4. 方向に応じて新規頭部座標を計算
Position MovingSnake::advance() {
    Position removedPos;
    if (m_head.length > 1) {
        extendBody(m_head.position, m_head.symbol);
        
        BodySegment* current = m_head.head;
        while (current->next->next != nullptr) {
            current = current->next;
        }
        
        BodySegment* tail = current->next;
        removedPos = tail->current;
        delete tail;
        current->next = nullptr;
    } else {
        removedPos = m_head.position;
    }

    switch (m_head.direction) {
        case MovementDirection::NORTH: m_head.position.x--; break;
        case MovementDirection::SOUTH: m_head.position.x++; break;
        case MovementDirection::WEST:  m_head.position.y--; break;
        case MovementDirection::EAST:  m_head.position.y++; break;
    }
    return removedPos;
}
ゲームマネージャークラスでは、スネークの移動とマップ更新を連携させます。advance()が返す削除座標を基に、画面表示を更新します。
void GameMaster::updateGame() {
    Position removed = m_snake.advance();
    m_gameMap.clearCell(removed.x, removed.y);
    
    MapElement newHead;
    newHead.symbol = m_snake.getHeadSymbol();
    m_gameMap.setCell(m_snake.getHeadPosition(), newHead);
    
    m_gameMap.render();
}
実装の検証には、5回の前進動作を1秒間隔で実行するテストケースを使用します。初期位置から南方向に5マス移動し、末尾が正しく更新されることを確認します。
int main() {
    GameMaster game;
    game.initialize();
    
    for (int i = 0; i < 5; i++) {
        game.updateGame();
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
    return 0;
}
実行結果では、スネークの頭部が垂直方向に5マス移動し、体節が連続して追従する様子が確認できます。この実装により、O(n)の計算量で体節管理が可能となり、リアルタイムゲーム動作に適した設計となっています。

タグ: C++ ゲーム開発 リンクリスト コンソールゲーム

7月13日 19:56 投稿