Caputchin
マーケットプレイスゲーム開発

マーケットプレイスゲームを作る

このチュートリアルの終わりまでに、1 つの JavaScript のバンドルに、完全で遊べる Caputchin のゲームを持ち、リプレイ可能にして公開する準備ができていることでしょう。これは退屈な、すべてのステップをやる版です。どこに合うかは先に マーケットプレイスにゲームを出荷する を読んでください。

Node とバンドラー(esbuild、rollup、vite、webpack。どれでも)が要ります。作るのに Caputchin のアカウントは要りません。公開するときだけです。

1. SDK をインストールする

npm install @caputchin/game-sdk

SDK は小さいです。register ヘルパーに TypeScript の型を足したものです。ウィジェットのランタイムをバンドルしないので、あなたのゲームは小さいままです。

2. ゲームファクトリーを登録する

ゲームは、ファクトリー関数 を伴う register への単一の呼び出しです。ウィジェットはサンドボックス化された iframe の中であなたのファクトリーを呼びます。その呼び出し自体が開始のシグナルです(待つべき別の開始イベントはありません)。

import { register } from "@caputchin/game-sdk";

register((container, bridge, ctx) => {
  // container - a DOM element inside the sandboxed iframe; render into it.
  // bridge    - push-only channel to the host (pass / error / setSize).
  // ctx       - the per-round context (seed + resolved locale/skin/config).

  const cleanup = startGame(container, bridge, ctx);

  // Return an optional cleanup function; the widget calls it on unmount.
  return cleanup;
});

マニフェストは register に渡し ません。サーバーがインデックス時に caputchin.json を読み、解決済みのプリセットを ctx としてあなたのファクトリーに送り下ろします。完全な面は SDK リファレンス を参照してください。

3. ラウンドごとのコンテキストからレンダリングする

ファクトリーの 3 つ目の引数 ctx は、訪問者ごとに変わるすべてを運びます:

  • ctx.seed - ラウンドごとのシード。すべてのランダム性をこれから導きます(ステップ 5 参照)。検証済みのセッションの外では null
  • ctx.locale - 解決済みの言語の文字列(ctx.locale._lang に加えてあなたのキー)、または null
  • ctx.skin - 解決済みの色とアセットの URL(ctx.skin._theme、常に lightdark、に加えてあなたのキー)、または null
  • ctx.config - 解決済みのゲームプレイの構成、または null

あなた自身のキーをこれらから読みます。あなた自身はプリセットを決して解決しません。マニフェストが合致するブロックを宣言しないとき、それぞれが null なので、常に組み込みの既定にフォールバックしてください:

function startGame(container, bridge, ctx) {
  const title = ctx.locale?.title ?? "Tap the targets";
  const accent = ctx.skin?.accent_color ?? "#2da44e";
  const targetCount = ctx.config?.target_count ?? 5;
  // ...render with these...
}

レイアウトに明示的なサイズが要るなら、最初の描画の後に一度 bridge.setSize(width, height) を呼びます。

4. 勝ち、負け、エラー

あなたのゲームが、プレイヤーがいつ勝つかを決め、ブリッジを通じてホストに伝えます:

  • 勝ちでは、ラウンドの トレース(ステップ 5)とともに bridge.pass({ trace }) を呼びます。
  • 負けまたは放棄では、何も 呼びません。沈黙が失敗のシグナルです。
  • ゲーム自体が壊れたら(アセットが失敗、例外)、bridge.error({ code, message }) を呼びます。それは、プレイヤーの負けではなく、ゲーム内部の失敗のためです。
function onWin(traceString) {
  bridge.pass({ trace: traceString });
}
function onAssetFailure(err) {
  bridge.error({ code: "asset-load-failed", message: String(err) });
}

5. トレースを記録する

これが、ゲームを リプレイ可能 にするステップです。プレイヤーが動くにつれて、結果を駆動する入力(どのターゲットを、どの順で当てたか、関係するならタイミングつき)を記録し、その記録を文字列かバイト配列にシリアライズします。それがあなたの トレース です。シードと組み合わさって、トレースはあなたのロジックに正確な結果を再現させなければなりません。サーバーがそれを再実行するからです。

2 つのルールがそれを可能にします:

  1. すべてのランダムな選択を ctx.seed から導きます。 結果に影響する何かのために Math.random() を呼んだり、壁時計を読んだりしないでください。
  2. リプレイに十分を記録します。 トレースにシードを足したものが、あなたのゲームロジックへの完全な入力です。
function makeRng(seed) {
  // seed is ctx.seed; feed it so the server reproduces the same sequence.
  let s = hashSeed(seed);
  return () => (s = (s * 1103515245 + 12345) & 0x7fffffff) / 0x7fffffff;
}

seed、トレース、判定の形は リプレイ契約 が定義します。そのページは、この記録されたプレイを、サーバーがリプレイするヘッドレスの run アーティファクトに変えることを扱います。決定論的なロジックを手で書くのが面倒に聞こえるなら、エンジンキット が、任意で、あなたのためにそれをします。

6. サンドボックスのためにバンドルする

ウィジェットはちょうど 1 つのスクリプト URL を読み込むので、すべてがその 1 つのファイルに入っていなければなりません。1 つの自己完結した出力のために、バンドラーを設定してください:

  • アセット(スプライト、音、フォント)をデータ URL としてインライン化する。
  • コード分割を無効にする。
  • WASM を使うなら、base64 として埋め込み、バイトからインスタンス化する。

不透明オリジンで厳格 CSP の iframe の中で 動かない パターン:

  • パス相対の fetch('./sprite.png') - 取得する元のパスがありません。
  • 動的な import('./chunk.js') - 2 つ目の URL がブロックされます。
  • new Worker('./worker.js') - 代わりにインラインの Blob URL から worker を生成してください。
  • 実行時の外部 CDN のフェッチ - connect-src がそれらをブロックします。

7. ローカルでテストする

特別なハーネスはありません。静的な HTML ページにウィジェットを埋め込み、game-src をあなたのローカルのバンドル出力に向け、ブラウザでそれを遊びます:

<caputchin-game sitekey="cpt_pub_..." game-src="http://localhost:8080/game.js"></caputchin-game>

これは カスタムゲーム が使うのと同じ game-src のパスです。マーケットプレイスでは、代わりにリポジトリを公開して、プラットフォームがあなたのためにバンドルを固定します。

次のステップ

あわせて読む

このページの内容