🛠️ 【開発者向け】Hitsumabushi SDK v0.2.0 観戦モード対応・移行ガイド
Webゲームポータル「ひつまぶし」へ自作ゲームを掲載・開発いただきありがとうございます。
ポータル本体に**「観戦モード(ルーム内の待機メンバーが試合をリアルタイム観戦できる機能)」が正式導入されたことに伴い、Hitsumabushi SDK v0.2.0** をリリースいたしました。
本ドキュメントは、旧バージョン(SDK v0.1.x)をベースに対戦ゲームを実装されたクリエイター様を対象に、観戦機能に完全対応するための改修ポイントと移行手順を解説する移行ガイドです。
[!IMPORTANT]
📌 本ガイドの対象クリエイター様
- Hitsumabushi SDK v0.1.x(旧バージョン)をベースにゲームを開発・掲載されたクリエイター様
- 今後、観戦モードに対応したWeb対戦ゲームを新しく開発されるクリエイター様
※ 今回の観戦モード導入に伴い、旧SDK(v0.1.x)前提で作成されたゲームには一部改修が必要となります。以下の手順に沿ってご確認・アップデートをお願いいたします。
[!NOTE] 本記事は v0.2.0 リリース時点の内容です。 その後のバージョンで対戦ライフサイクルのコールバックが変更され、
onInitGameはonCreateMatch(初回のみ)とonStartMatch(毎試合)に分離・改名されました。本記事中のonInitGameは、現行SDKではonStartMatchに読み替えてください。最新の仕様は Hitsumabushi SDK APIリファレンス を参照してください。
依頼内容サマリー
- 【UI/UX】観戦者画面は「プレイヤー画面をそのまま表示し、操作のみロック」してください
- 観戦者専用の別画面(「観戦中」という静止文字だけ、操作ボタンの非表示など)を作らず、対戦者と同じ迫力・レイアウトで画面を表示してください。
- 【通信】CPU対戦であっても、ゲーム進行パケットは常に送信(ブロードキャスト)してください
- 「相手がCPUなら通信しない」という分岐を無くしてください(部屋にいる観戦者がゲーム開始待ちのまま置き去りになります)。
修正詳細とコード例
1. 観戦者UI:プレイヤー画面の流用 & 操作要素の物理ロック
なぜ必要か?
観戦時に操作パネルを非表示(display: none)にしたり、スコアを「VS」などの固定文字にしてしまうと、観戦者から見てゲームが進行している様子が分からなくなってしまいます。
プレイヤーが見ているゲーム画面(盤面、ゲージ、操作ボタン、キャラアニメーション)がそのまま動き、操作だけが効かない状態がポータルの標準観戦体験です。
修正方針
Hitsumabushi.isSpectator()がtrueの場合でも、画面レイアウトはプレイヤーと同一にする。- インタラクティブな操作要素(盤面、タップボタン、カードなど)に
pointer-events: noneまたはdisabledを適用する。
実装例
Hitsumabushi.init({
onInitGame: function (config) {
var isSpectator = Hitsumabushi.isSpectator();
// 盤面や操作アリーナのタップを物理ロック
var boardElement = document.getElementById('gameBoard');
if (boardElement) {
boardElement.style.pointerEvents = isSpectator ? 'none' : 'auto';
}
// ボタンを消さず、disabled + 半透明にして操作不可にする
var actionBtn = document.getElementById('actionBtn');
if (actionBtn) {
actionBtn.disabled = isSpectator;
actionBtn.style.pointerEvents = isSpectator ? 'none' : 'auto';
actionBtn.style.opacity = isSpectator ? '0.6' : '1.0';
}
}
});
2. 通信モデル:CPU対戦時も常にパケットを送信する(常時ブロードキャスト)
なぜ必要か?
「プレイヤー同士の1対1対戦」の感覚で、以下のようなコードを書いてしまうケースがあります:
// ❌ NGな実装パターン(古い1対1前提)
var isPvpMode = opponent && !opponent.isCpu;
if (isPvpMode) {
Hitsumabushi.sendGameData({ type: 'START_MATCH' });
Hitsumabushi.sendGameData({ type: 'SPAWN_OBJECT' });
}
この実装だと、「CPUとの対戦を誰かが観戦している場合」に通信が完全に停止し、観戦者は試合開始シグナルを受け取れず、初期画面で永遠にフリーズしてしまいます。
修正方針
- ルーム内には常に観戦者がいる可能性があります。
- 相手が人間かCPUかに関係なく、ゲームマスター(着席プレイヤー)は開始シグナル、アクション、オブジェクトの出現、スコア更新を常に
Hitsumabushi.sendGameDataでブロードキャストしてください。 - CPUのアクション(CPUが召喚したトラップ、CPUが打った手など)も、代理シミュレーションしているプレイヤーから同様にブロードキャストしてください。
実装例
// ⭕ 推奨される実装パターン(1対Nブロードキャスト前提)
function startMatchCountdown() {
// 相手が人間でもCPUでも、観戦者のために無条件で送信!
Hitsumabushi.sendGameData({ type: 'COUNTDOWN_TICK', count: 3 });
setTimeout(function () {
Hitsumabushi.sendGameData({ type: 'START_MATCH' });
startLocalRound();
}, 3000);
}
function handlePlayerAction(action) {
if (Hitsumabushi.isSpectator()) return; // 観戦者は操作送信不可
// 相手および観戦者へ常時ブロードキャスト
Hitsumabushi.sendGameData({
type: 'ACTION',
playerId: localPlayerId,
action: action
});
}
3. ゲームマスター(進行役・親)の判定
なぜ必要か?
ルームを作ったホスト自身が「観戦者席」に回っている場合があります。 そのため、「ルーム作成者(ホスト)」をゲーム進行の親にしてしまうと、ホスト観戦者はパケット送信がガードされているためゲームが開始できなくなります。
修正方針
- ゲームの進行(山札のシャッフル、カウントダウン開始など)を司るゲームマスターは、**「着席しているプレイヤーの先頭(
Hitsumabushi.isGameMaster())」**で判定してください。 - (SDK v0.2.0 の
Hitsumabushi.isGameMaster()は、観戦者を除外した着席プレイヤー先頭に対して自動的にtrueを返します)
Hitsumabushi.init({
onInitGame: function (config) {
// 観戦者を除いた着席プレイヤー先頭のみ true になる
var isGameMaster = Hitsumabushi.isGameMaster();
if (isGameMaster) {
// 親としてゲーム開始や山札配布を主導
startNewRound();
}
}
});
チェックリスト(クリエイター様セルフチェック用)
自作ゲームの修正にあたり、以下の項目をご確認ください:
- 観戦者画面でUIが崩れていないか?
- 操作ボタンやスコアボードが
display: noneで消滅せず、対戦者と同じ画面が表示されている。
- 操作ボタンやスコアボードが
- 観戦者画面で操作が確実にロックされているか?
- 盤面やボタンをタップしても何も反応しない(
pointer-events: noneまたはdisabled)。
- 盤面やボタンをタップしても何も反応しない(
- 「相手がCPUなら通信しない」という条件分岐(
isPvp等)が存在しないか?- CPU戦であっても、ゲーム開始シグナルやオブジェクト出現が
sendGameDataされている。
- CPU戦であっても、ゲーム開始シグナルやオブジェクト出現が
- CPU戦を観戦者視点でテストしたか?
- 観戦者ブラウザを開いた状態で「プレイヤー vs CPU」の対戦を開始し、観戦者側でも試合が始まって動いている。