Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】Bejeweled

2025年09月12日に「Dev.to」が公開したITニュース「Bejeweled」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

C++とSFMLで名作マッチ3パズル「Bejeweled」の開発を解説する。ゲームループ、ジェムの交換・落下、組み合わせ検出などゲームの基本メカニクスに加え、SQLiteでのスコア永続化も習得可能。ゲーム開発を通じてプログラミングの幅広い概念を実践的に学ぶことができる。

出典: Bejeweled | Dev.to公開日:

ITニュース解説

Bejeweledは、同じ色のジェムを縦か横に3つ以上並べて消す「マッチ3」形式のパズルゲームである。ジェムを消すと新しいジェムが上から落ちてきて、空いた場所を埋め、プレイヤーはポイントを獲得する。このゲームは、時間がなくなるか、それ以上動かすことができなくなるまで続く。ゲームを構成する基本的な要素は、ジェムが配置される8x8などの「ゲームグリッド」、プレイヤーが操作する「ジェム」、3つ以上並んだときに発生する「組み合わせ(マッチ)」、プレイヤーが行う「ジェムの交換(スワップ)」、消えたジェムの後に上から落ちてくる「落下(グラビティ)」、そして新しいジェムでグリッドを埋める「補充(リフィル)」である。

すべてのゲームは、「ゲームループ」と呼ばれる継続的な実行サイクルを中心に構築される。このループは、プレイヤーからの入力処理、ゲームの状態更新、そしてグラフィックの画面描画を担当する。Bejeweledでは、アニメーションの滑らかさやプレイヤーの操作への応答性にとって、このゲームループが極めて重要だ。具体的には、「ゲーム開始」から「メインループ」に入り、「イベント処理(プレイヤー入力)」、「ゲームロジック更新(メカニクス、時間、状態)」、「グラフィック描画(画面表示)」を繰り返し、ゲーム終了までこれが続く。

Bejeweledのようなゲームを構築するには、データの表現からアニメーションのロジック、永続化まで、すべてのステップを慎重に考える必要がある。ゲームグリッド上のジェムは、自然に2次元配列で表現される。例えば、grid[10][10]のような配列を使うことで、実際の8x8のゲームボードに対して、配列の端(インデックス0と9)を「番兵境界」として利用し、隣接するジェムのチェックや境界条件の処理を簡素化できる。各ジェムは、pieceという構造体で表現される。この構造体には、画面上のピクセル座標を示すxy、グリッド上の論理的な位置を示すcolrow、ジェムの種類や色を示すkind、組み合わせの一部であるかを示すmatchフラグ、透過度を制御するalpha、そして特殊なジェムであるかを示すspecialフラグが含まれる。alphaはジェムが消える際のアニメーションに使われ、specialは爆弾などの特殊能力を持つジェムを区別するために使用される。

主要なゲームメカニクスは、プレイヤーの操作とゲームの反応の連続的なサイクルで動作する。 まず「ジェムの交換(スワップ)」では、プレイヤーが隣接する2つのジェムをクリックすると、それらの位置が論理的に交換される。しかし、この交換が有効な組み合わせを生み出さない場合、ゲームはその交換を元に戻す必要がある。これは、isSwapというフラグと、交換後にマッチが検出されなかった場合にswap関数を再度呼び出すことで実現される。 次に「組み合わせの検出(マッチファインディング)」は、ジェムが交換された後、ゲームはグリッド全体をスキャンし、3つ以上の同じ種類のジェムが縦横に並んでいるかをチェックする。forループでグリッドをi=1から8j=1から8まで走査し、各位置から水平方向または垂直方向に3つのジェムのkindが一致するかを確認する。組み合わせが見つかった場合、該当するジェムのmatchフラグが1に設定される。このmatchフラグは、後の消失、落下、補充のプロセスで重要な役割を果たす。4つ以上のジェムが揃った場合は、特別なジェムが生成され、それが爆発することで周囲のジェムも連鎖的に消えるなどの特殊効果を持つこともある。 「消失アニメーションとスコア加算」のフェーズでは、matchフラグが1のジェムは即座に消えるのではなく、alpha値(透明度)が徐々に減少するアニメーションを経て、画面からフェードアウトする。このアニメーションが進行中はisMovingフラグがtrueに設定され、他のゲームロジックが一時停止する。この期間に、プレイヤーのスコアが加算される。 「ジェムの落下(グラビティ)」は、消失したジェムの場所が空いた後、その上にあるジェムが重力によって下の空いたスペースに落ちてくる。この処理は、グリッドの最下部から上に向かって(i=8から0へ)走査することで効率的に行われる。これにより、一番下の空きスペースから順に上にあるジェムが適切に落下する。ここでもisMovingフラグが使用され、落下アニメーションが完了するまで他の処理は待機する。 最後に「グリッドの補充(リフィル)」では、既存のジェムが落下した後も上部に残っている空きスペース(matchフラグが1になっている場所)に、新しいランダムな種類のジェムが生成されて補充される。新しいジェムは、画面の上部から降ってくるようなアニメーション効果を出すため、初期のy座標を画面外の負の値に設定する。これら新しいジェムについてもmatchalphaspecialなどのフラグはリセットされ、次のゲームサイクルに備えられる。

ゲームには、残り時間を示すgameTimerと、プレイヤーの現在の得点を示すscoreのシステムが組み込まれている。gameTimerはフレームごとに減少し、ゼロになるとゲームオーバーとなり、scoreが記録される。ゲームの進行は、「MainMenu(メインメニュー)」、「Playing(プレイ中)」、「GameOver(ゲームオーバー)」、「HighScores(ハイスコア)」といった「ゲームの状態(GameState)」を管理する有限状態機械(FSM)によって制御される。enum GameStateでこれらの状態が定義され、プレイヤーの操作やゲーム内の条件に応じて状態が遷移する。例えば、メインメニューで「プレイ」を選択するとPlaying状態に、時間が尽きるとGameOver状態に移行する。このFSMは、コードの整理と異なる画面のロジックが混ざらないようにするために非常に有効である。

さらに、プレイヤーのスコアをゲームのセッション間で永続的に保存するため、軽量な埋め込み型データベースであるSQLiteが利用される。sqlite3 *dbというポインタを通じてデータベースと接続し、openDatabase()でデータベースファイルを開き、createTable()でスコアを保存するためのhighscoresテーブルを作成する。ゲームが終了するとsaveHighScore(int score)関数で現在のスコアが日付と時刻とともにデータベースに挿入され、loadHighScores()関数ではデータベースから上位10件のスコアを読み出し、画面に表示する。これにより、プレイヤーは自身の進捗や他のプレイヤーとの比較を確認できる。

Bejeweledのmain.cppファイルは、標準的なゲーム開発の構造に従っている。SFMLのグラフィック機能、C++の標準ライブラリ、そしてSQLiteのためのヘッダーファイルがインクルードされる。タイルサイズやオフセット、ジェムの構造体、グリッド、ゲーム状態、SQLiteのデータベースポインタといった定数やグローバル構造体が定義される。swapresetGame、SQLite関連の補助関数が用意されている。main()関数では、乱数ジェネレーターの初期化、SFMLウィンドウの作成、フレームレート制限、画像やフォントの読み込みとスプライトやテキストの作成、SQLiteデータベースの初期化とテーブル作成、ゲームの初期設定が行われる。そして、while (app.isOpen())でゲームのメインループが実行され、フレームごとに時間の管理、プレイヤーイベントの処理、Playing状態でのゲームロジックの更新、そして現在のゲーム状態に応じた描画が行われる。プログラム終了時には、データベース接続が適切に閉じられる。

このBejeweledのプロジェクトを通じて、システムエンジニアを目指す初心者は、イベント駆動プログラミング、有限状態機械による状態管理、2次元配列の操作、マッチ検出などのアルゴリズム、アニメーションの実装、SQLiteによるデータ永続化、deltaTimeを使ったタイミング制御、基本的なUI/UXデザイン、そしてランダムなジェム生成といったプロシージャルコンテンツ生成の概念を実践的に学ぶことができるだろう。これは、パズルゲームのロジックと視覚要素、データ永続化を組み合わせた、ゲーム開発における広範な知識習得に最適なプロジェクトとなる。

関連コンテンツ