Purrfect Code の作り方: デベロッパー向けパズルゲーム

8月 01, 2024

Link to Youtube Video (visible only when JS is disabled)

Purrfect Code」は、新しい箱押し型プログラミング パズルゲームで、Flutter、Dart、Flame ゲームエンジンが使われています。デベロッパーは、コードを書いてパズルを解くことが求められます。


ゲームの概要

「Purrfect Code」のプレーヤーのミッションは、Google の新しい宇宙ステーション本部で清掃ロボットのプログラミングをアップデートすることです。そこには、猫が入った箱が誤って到着しています。ゲームの目的は、ロボットを操作して、すべての箱をテレポーター プレートに乗せ、猫が無事に家に帰れるようにすることです。プレーヤーは JavaScript を書いてロボットの動作を制御し、グリッドベースのパズルの中を動かしながら、効率的な解決策を探します。

このゲームでは、知性と創造性を活用しながら、楽しくコーディングを学ぶことができます。各レベルはプログラミングのコンセプトに注目したものになっています。ゲームには 5 つのレベルがあり、徐々に複雑になっていきます。

ゲームループは次のようになっています。

  1. プレーヤーがロボットを動かす JavaScript コードを書きます(上下左右)。

2. コードを実行し、ロボットがレベルを解決する様子を確認します。

3. 猫が入ったすべての箱がテレポーター プレートに乗ると、レベルをクリアしたことになります。

4. 移動したスペースの数やコードの簡潔さなど、ソリューションの効率に応じたスコアがプレーヤーに与えられます。

Flutter、Dart、Flame でゲームを作る

Purrfect Code」を作る上で選んだのは、Flutter と Dart でした。この 2 つは、マルチプラットフォームのアプリケーションやゲームを作成するための強力で柔軟なフレームワークを提供しているからです。Flutter のウィジェット システムとリアクティブ プログラミング モデルを使うことで、ブラウザのさまざまな画面サイズに対応したレスポンシブなユーザー インターフェースを作成できました。また、Dart の強力な型付けとオブジェクト指向プログラミング機能のおかげで、コードを簡単に構造化し、コードベースをきれいな状態に保つことができました。Flame ゲームエンジンは Flutter 上で動作し、ゲーム開発に必要な基本機能の基盤を提供してくれるすばらしい仕組みなので、ゲームプレイのロジックや、このゲームならではの機能に集中することができました。Purrfect Code には、IDE から着想を得た UI と、アニメーション スプライトとサウンドを含むゲームビューの両方があるので、ほかにはないゲームとアプリのハイブリッドと言えます。このような性質を考えれば、このプロジェクトで Flutter と Flame が選ばれたのは明らかなことです。


Chrome と JavaScript: ブラウザの組み込みサポートを活用する

ゲームのプログラミング コンセプトを決めた後、プレーヤーが使うプログラミング言語を選択する必要がありました。デベロッパーに親しまれ、人気のある言語が理想です。当初は、ゲーム内プログラミング言語に Python を使おうと考えました。しかし、いくつかの検討を経て、Chrome が提供する組み込みの JavaScript サポートを活用するために JavaScript を使うことにしました。JavaScript なら、追加の言語インタプリタを読み込まなくても、ブラウザのネイティブ機能を利用できます。この決定により、開発プロセスがシンプルになっただけでなく、最小限の依存関係ですばやくゲームを読み込めるようになり、プレーヤーにとってシームレスなエクスペリエンスを実現できました。

Flutter は今年の Google I/O で 3.22 がリリースされ、WebAssembly(WASM)サポートが安定版になりました。そのため、ゲームのパフォーマンスにとって不可欠な部分を最適化できました。特定のゲームロジックを WASM にコンパイルすることで、「Purrfect Code」がブラウザで効率的に実行され、パフォーマンスに妥協することなく、スムーズでレスポンシブなゲーム体験を提供できるようになりました。

var dir = [moveEast,moveNorth,moveWest,moveSouth];
for(i=0;i<4;i++){
    for(j=0;j<5;j++)dir[i]();
}

Flame ゲームエンジン: モジュール型の効率的なゲーム開発

「Purrfect Code」を生み出すために、Flutter で動作するオープンソースのモジュール型ゲームエンジンである Flame ゲームエンジンを利用しました。これにより、ゲーム開発でよく使われる多くの機能が提供されます。Flame は、Flutter の堅牢なインフラストラクチャを活用し、プロジェクトの構築に必要なコードをシンプルにします。また、シンプルで効果的なゲームループ実装に加え、オーディオ再生、スプライト管理、アニメーション機能、衝突、Flame コンポーネント システム(FCS)など、幅広い必須機能が提供されます。このエンジンは、コンポーネントベースのアーキテクチャになっており、スプライト レンダリングやアニメーションがサポートされるので、視覚的に魅力的なグラフィック、スムーズなアニメーション、インタラクティブなゲームプレイ要素を、不要な作業を行うことなく作成できました。Flame を活用することで必要不可欠な機能をすぐに使えるようになるので、開発プロセスを効率化でき、プレーヤーにとって魅力的なゲーム体験作りに集中できました。


Flame でゲーム機能を実装する

Flame では、シンプルで効果的なゲームループ実装に加え、オーディオ再生、スプライト管理、アニメーション機能、衝突、Flame コンポーネントシステム(FCS)など、幅広い必須機能が提供されます。

Flame のスプライト レンダリングとアニメーション システムにより、グラフィック コードを書くことなく、ゲームのキャラクターや環境を生み出すことができました。スプライト シートを作成し、アニメーション シーケンスを定義することで、キャラクターの動きや特殊効果にスムーズにアニメーションをつけることができます。「トップダウン」型ゲーム視点用のビジュアル ソーティング システムは、Flame の優先順位システムを使って記述しました。アート担当は、スプライトの奥行き感を高め、ゲームビューが「グリッド的」に見えないようにするため、たくさんの形状が重なり合うようなレベルを作りました。ロボットが後ろに回り込んだときには、正しくオクルージョンする必要があります。Flame の優先順位システムでは、さまざまな視覚要素に優先順位を割り当てることができるので、正しい順序で描画し、適切な順番で重ねることができます。このシステムには、十分な柔軟性があります。シャドウ システムを導入することで、個々のコンポーネントにオブジェクトの動きと連動した影のアニメーションを付けることができ、アートに奥行き感を出して、視覚的に理解しやすい生き生きとしたシーンを実現できました。

その好例となるのが、コードベースの BoxShadow クラスです。これは、ゲーム内で動く箱のオブジェクトにダイナミックでインタラクティブな影を付ける方法を示しています。SpriteAnimationComponent を拡張し、GridElement と HasVisibility ミックスインを実装することで、箱の影とテレポート アニメーションのスプライト シートを読み込み、オープン、クローズ、アイドル、テレポート状態という複数のアニメーションを定義して、グリッドベースのレイアウトに統合することができました。onLoad メソッドでアニメーションを読み込み、グリッド位置に基づいてコンポーネントの初期位置と優先順位を設定します。update メソッドでは、箱がオクルージョン オブジェクトの前後に移動した場合に、コンポーネントの優先順位を動的に更新します。Flame の優先順位とアニメーション システムをこのように利用することで、影のエフェクトを作成することができます。これにより、ユーザーは常に仮想空間を視覚的に理解できるようになり、信頼性が高く、一体感のある視覚表現を実現できます。

box_shadow.dart の抜粋を次に示します。詳しくは、GitHub で完全なクラスをご覧ください

@override
  Future<void> onLoad() async {
    await _loadAnimations().then((_) => {animation = _boxClosed});
 
 
    position.add(Vector2(
        ((gridPosition.x * gridPixelDimensions.x) + gridPixelOffset.x),
        ((gridPosition.y * gridPixelDimensions.y) + gridPixelOffset.y)));
    priority = getLayeredGridValue();
  }
  @override
  void update(double dt) async {
    super.update(dt);
 
 
    if (getLayeredGridValue() != priority) {
      priority = getLayeredGridValue();
    }
  }

Google デベロッパー プログラムとバッジ

ゲームのスコープを小さく保ち、簡単なウェブページとしてデプロイできるようにしたかったので、ゲームのバックエンドを設定することは避けたいと考えました。しかし、人気ゲーム プラットフォームの実績システムと同じように、進捗に応じてデベロッパーが報酬をもらえるようにしたいとも考えました。そこで、別のバックエンド システムを作ってプレーヤーの進捗や実績を追跡する代わりに、Google デベロッパー プログラムと連携する仕組みを導入しました。具体的には、レベルをクリアしたプレーヤーがリンクをクリックすることで、プロフィール用のバッジを獲得できるようにし、達成感やデベロッパー コミュニティでの認知度を上げられるようにしました。

Purrfect Code Google Developer Program profile badges

Project IDX: 効率的な開発環境

「Purrfect Code」の開発中に、クラウドでのフルスタック、マルチプラットフォーム アプリ開発のための AI 支援ワークスペースである Google の Project IDX を活用しました。私たちはすでに VS Code での作業に慣れていたので、Project IDX はゲームのコーディング、デバッグ、テストに使い慣れた環境を提供し、すぐに使い始められるようにしてくれました。 Flutter と Dart がすでにセットアップされており、ブラウザで使用できる状態になっているため、ローカル環境の構成に煩わされることなく、すぐに開発に取り掛かることができます。 Project IDX が提供するインテリジェントなコード補完、リアルタイム エラー チェック、統合されたデバッグ ツールは、生産性を高く保つのに役立ちました。興味がある場合は、Project IDX を使用すると、Purrfect コードをすぐに試し、ブラウザから直接そのコードを探索できる優れた方法になります。 このリンクをクリックすると、IDX でプロジェクトを直接開き、自分でプロジェクトを実行できます。これが Flutter アプリであるかどうかを尋ねるボックスに必ずチェックを入れてください。


Firebase で高速かつ安全にホスティング

世界に向けて Purrfect Code を安全かつ効率的に配信する方法として、Firebase Hosting を選びました。このプラットフォームのゼロ構成 SSL により、コンテンツは確実に HTTPS で提供されるため、強固なセキュリティを実現できます。さらに、最新のウェブ フレームワークや GitHub リポジトリの自動ビルドがサポートされているので、アップデートをすばやくデプロイできます。Firebase CLI、ローカル エミュレーション、プレビュー URL により、テストとコラボレーションのプロセスも効率化されます。こういった機能のほか、AI 連携用の Gemini サンプル テンプレートを使ってゲームを進化させる余地も生まれることから、Firebase Hosting はこのゲームを立ち上げる上で理想的な選択肢となりました。


Firebase で WASM を構成する

Purrfect Code は Web Assembly を使っているので、デプロイ時にいくつかの追加の手順が必要となります。firebase.json 構成では、preeploy コマンドを追加して、ウェブビルドを WASM でビルドできるようにしました。“--no-strip-wasm” 引数で、コードで最後の最小化ステップが実行されないようにしています(エラーの判別やデバッグが難しくなるため)。WASM でマルチスレッドとメモリ共有を利用するため、cross-origin-opener-policy と cross-origin-embedder-policy も必要になります。

"hosting": {
      "predeploy": "flutter build web --wasm",
      "public": "build/web",
      "ignore": [
        "firebase.json",
        "**/.*"
      ],
      "headers": [
        {
          "source": "**/*",
          "headers": [
            {
              "key": "cross-origin-opener-policy",
              "value": "same-origin"
            },
            {
              "key": "cross-origin-embedder-policy",
              "value": "require-corp"
            }
          ]
        }
      ]
    }

学習とリソース

「Purrfect Code」の開発を通して、さまざまなリソースを利用し、既存プロジェクトからインスピレーションを受けました。このプロジェクトも、その 1 つとなれば幸いです。ここでは、貴重な学習リソースや参考プロジェクトを紹介します。


参考プロジェクト

ベスト プラクティスや実装のアイデアとして、Super DashI/O Flip ゲームを参考にしました。どちらのプロジェクトも、Flutter ゲームの構造化、ゲームの状態処理、ゲームのメカニズムの実装について、貴重な知見を提供してくれました。Super Dash は、今回のゲームと同じく、シンプルでバックエンド サービスを必要としないので、特に参考になりました。I/O Flip はもう少し大きく、バックエンドだけでなく、生成 AI 機能にも対応しているため、こういった機能が必要なゲームを開発するデベロッパーには、興味深いものとなるでしょう。Flutter でゲームを作ることに興味がある方にとっては、どちらも貴重なリソースです。


まとめ

Purrfect Code」が楽しいゲーム体験を提供するだけでなく、Flutter と Flame を使用したゲーム開発に興味のある開発者にとっての学習リソースとしても機能することを願っています。 FlutterFlameChrome の組み合わせが最適であることが判明しました。ゲームとアプリのハイブリッドに、UI 開発、グラフィック レンダリング、サウンド管理などのための強固な基盤を提供します。ゲームのソース コードを探索し、さらに拡張して実験することをお勧めします。新しい機能、レベル、ゲームプレイの仕組みを追加する可能性は数多くあります。 Project IDX のコードベースにジャンプし、これが Flutter アプリであるかどうかを尋ねるボックスにチェックを入れて、創造力を発揮してください。