テーマ
開発・テストガイド
SDK はサーバーなしでのローカル開発から、マルチプレイテスト、パブリッシュまでの一連のワークフローをサポートしています。
ローカル開発(素の vite)
素の vite で 1 player 用の生 debug をする場合:
bash
npm run dev
# http://localhost:5173 を開く素の vite (single-frame) の動作:
- 親 frame として
index.htmlがそのまま出るだけ。 SDK は!isHostedで早期 return し、run()/init()は何もしない - pure vite の HMR や 1 player view のスタイル調整には十分
複数 player でのマルチプレイシミュレートやサーバー authoritative logic の debug には次節の uzu dev を使う。
uzu dev (harness + in-memory サーバー)
uzu dev は scenario の dev command を子 process で起動しつつ、 別 port で harness page (iframe grid + HUD + inspector) と in-memory GameRoom (本番と同一 WebSocket プロトコル) を serve するサブコマンドです。
scenario ディレクトリ配下では以下のいずれかで起動できます:
bash
# scenario ディレクトリ内で
pnpm run harness # 各 scenario の package.json に "harness": "uzu dev" が仕込まれている
# node_modules/.bin 経由で直接
npx uzu devscenario 側は run<S>({ logic, events }) を書くだけ。 mount コードや import 文は不要です。
動作
- 起動すると 2 つの HTTP server が並走する:
- scenario の vite (or 任意の) dev server —
dev.command(defaultpnpm run dev) を子 process で起動 - CLI harness server — free port で開き、 harness page +
/ws/games/dev/:roomIdの WebSocket endpoint を提供
- scenario の vite (or 任意の) dev server —
- 子 iframe には
?server=ws://localhost:<port>&roomId=<key>&seatId=dev_N&players=<json>&seatKind=player&revisionId=devが付き、 SDK は既存 online mode で dev-server に接続します (本番 Cloudflare Worker への接続経路と同じ shape) - manifest で
"admin": true/"spectator": trueを宣言すると、 グリッドに進行管理席 (admin_0) / 観戦席 (spec_0) の iframe が追加されます。 これらは roster (players) に載らないまま?seatKind=admin/?seatKind=spectatorを付けて接続するので、setup()のplayersにも現れません。 scenario 側はstate.playersに自席が居ないことで観測者と判定し、 GM ビューか観戦ビューかはonStateの第 3 引数 (mySeatKind) で分けます - GameRoom の roster は dev-server が manifest から組んで注入します (server 権威)。 接続クエリの roster 申告は採用しないため、 リロード前の古い harness タブが reconnect しても roster は汚れません
- virtual server は Node.js 上で走るため、 disconnect / reconnect / message ordering / seq gap リカバリなど dev でしか再現しなかった bug が early に出ます
- 各 iframe の inner viewport 短辺が
devMinIframeShortEdge(default 360 CSS px) を下回らないよう、 親 frame のbodyにtransform: scale()を自動で当てる。 5p landscape を mac 標準モニタで開くような狭い viewport でも iframe 内部のwindow.innerWidth/Heightは実機サイズ相当を保つ (zoomは layout プロパティで WebKit では iframe 内のvh解決に混入するため使っていません)
プレイヤー / orientation の解決順序
--orientation <portrait|landscape>(orientation のみ)manifest.jsonのcharacters配列長 →playerCountmanifest.jsonのplayerCount/orientation- ハードコード fallback (
playerCount: 2,orientation: 'portrait',devMinIframeShortEdge: 360)
--orientation は manifest を書き換えずに縦横を入れ替えます。 セルの比率と擬似ノッチの 向き (縦持ちは上下、 横持ちは左右) が同時に変わるので、 同じ scenario で両方の SafeArea を確かめられます。
bash
npx uzu dev --orientation landscapemanifest.json の dev フィールド
dev.command と dev.readyPattern は任意フィールド。 詳細は manifest リファレンス 参照。
jsonc
{
"dev": {
"command": "pnpm run dev",
"readyPattern": "Local:\\s+http://[^\\s]+:(\\d+)",
},
}HUD
各プレイヤー画面の左上に、本番アプリと同じ "UZU" ボタンが載ります。 クリックするとメニューが開きます。
| 項目 | 動作 |
|---|---|
| 🖥 この画面だけ開く | そのプレイヤーの単体表示 (?player=N) を別タブで開く |
| Emulator | Web エミュレータをこの dev-server に接続して開く |
| State | State Inspector の表示 / 非表示 |
| Reset State | ゲーム state をリセットする |
隣の 📱 アイコンは、 そのプレイヤーの画面をスマホ実機で開くための QR を出します (同一 LAN)。
収まりを見るときの表示の選び方
グリッドの各ペインは、iframe の短辺が devMinIframeShortEdge 以上・縦横比が宣言どおり (landscape なら 19.5:9) になるよう組まれます。ウィンドウが広いと、ペインは下限より大きく組まれます。 短辺が下限まで下がるのは、ウィンドウが狭くてグリッドに transform: scale() が掛かっている間だけです。
いま何を見ているかは画面右上のバッジが言い切ります。
| バッジ | 意味 |
|---|---|
viewport 1440×665 | 等倍表示。見えている大きさ = scenario が使う大きさ |
viewport 780×360 · 表示 ×0.90 | scenario は 780×360 でレイアウトしているが、画面には 0.90 倍で出ている |
グリッドで見切れを確かめるときは、縮小表示が掛かるまでブラウザのウィンドウを狭めてください。縮小表示中 (バッジに倍率が出ている間) は見た目の大きさが実機と違うので、文字が読めるか・指で押せるかを目で判断しないでください。
| 見たいもの | 使う表示 |
|---|---|
| 誰に何が見えているか、全席で進行が噛み合うか | グリッド |
| 画面外に出ていないか | 縮小表示が掛かるまで狭め、擬似ノッチを ON にしたグリッド/単体表示をウィンドウごと基準サイズへ |
| SafeArea を避けられているか | 擬似ノッチを ON にしたグリッド(--orientation で縦横両方) |
| 文字が読めるか、押せる大きさか | 単体表示 (?player=N)、または 📱 の QR から実機 |
| タップ判定・通話 | 📱 の QR から実機 |
「🖥 この画面だけ開く」で開く単体表示は、ブラウザのウィンドウがそのままゲームのビューポートになります。縮小が掛からないので実寸で見られる代わりに、ウィンドウサイズを自分で合わせる必要があります。広いウィンドウのまま確認すると、小さい画面でだけ起きる見切れを取りこぼします。
自動化するなら
Playwright 等でウィンドウサイズを固定して単体表示を開くのが確実です。要素をクリックできたことは、見えていたことの証明になりません(多くのドライバは画面外の要素へ自動スクロールして当てにいきます)。収まりを見るなら、クリックの成否ではなく座標か scrollHeight を確かめてください。
State Inspector
State Inspector で現在の state を JSON 形式でリアルタイムに確認できます(200ms 自動更新)。
デバッグボタンをクリックし、State Inspector を選択してください。
ビルドとパブリッシュ
ビルド
bash
npm run build # tsc && vite build → dist/ に出力パブリッシュ
uzu publish コマンドでビルドからデプロイまでを一括実行します。
bash
uzu publish # ビルド → ZIP → R2 アップロード → リビジョン登録
uzu publish --change-notes "手番表示の修正" # リビジョンに変更メモを添えるpublish 先は UZU 本番環境です。publish には認証が必要で、ローカルでは uzu login で保存した認証情報が使われます。
リビジョンを登録せずに動くものを他人に見せたいだけなら、プレビュー配信 を使ってください。
処理フロー:
manifest.jsonからid,playerCount,outputを読み取りmanifest.jsonのbuildコマンドを実行outputディレクトリを ZIP 化- R2 バケットに ZIP をアップロード
manifest.jsonのserverActionLogicPathの有無で logic を配るかを判定- あり → logic をビルドして R2 にアップロード (プレイ要求時にサーバーがロードする)
- なし → クライアント zip のみ (state 同期を使わない 1 人用シナリオ)
- UZU にリビジョン登録
- UZU Studio の URL を出力
CI から publish する (publish token)
INFO
publish token は現状 dev 環境のみ対応で、--env を指定できる社内メンバー (@sally-inc.jp) だけが使えます。
ブラウザを開けない CI では、publish token を環境変数で渡します。uzu-cli は UZU_PUBLISH_TOKEN があればそれを短命の ID Token に交換して publish します (認証情報の優先順位は UZU_PUBLISH_TOKEN → uzu login の認証情報)。
bash
uzu token create --env dev --name "github actions" # 平文はこの 1 回だけ表示される
uzu token list --env dev
uzu token revoke <id> --env devyaml
- name: Publish
run: pnpm exec uzu publish --env dev
env:
UZU_PUBLISH_TOKEN: ${{ secrets.UZU_DEV_PUBLISH_TOKEN }}- token は発行者本人の権限で動きます。発行者が publish できるシナリオだけが対象です。
- 平文は再表示できません。失くしたら revoke して発行し直します。
uzu tokenコマンドはuzu loginの認証情報でしか使えません。UZU_PUBLISH_TOKENからは token を発行・失効できません (漏れたトークンで失効を回避されないため)。
テストプレイ
publish したバージョンは、アプリから立卓して実機でテストプレイできます。その履歴は UZU Studio のシナリオ詳細 → リリース管理のバージョン一覧から見られます。
- 各バージョンの行に テストプレイ N 件 が出ます。押すと、そのバージョンのテストプレイが新しい順に並びます
- 一覧に出るのは立卓日時とステータス (募集中 / プレイ中 / 終了 など) です
- 各行から、その卓をアプリで開くリンクと、エミュレータで開くリンクをたどれます
数えるのは、作品がまだ公開されていないあいだに立った卓のうちキャンセルされていないものです。リンク限定公開のあいだに遊ばれた卓も入ります。公開後に最新版を指定して立てた卓は含まれません。
履歴を見られるのはそのシナリオのメンバーだけです。