マルチプレイヤー
複数の人で、1 つのシーンを同時に共有できます。メッセージを送り、値を共有し、全員向けにオブジェクトを生成し、ほかのプレイヤーをなめらかに動かしましょう。
ここで紹介する呼び出しは、プロジェクトでマルチプレイヤーがオンになっていて、しかもルームが開いているときだけ動きます。それ以外のときは何もしません。そのため、同じスクリプトが 1 人用のプレビューでもそのまま動き、2 つのバージョンを用意する必要はありません。
ルームにいる人
ctx.net.id; // この参加者の ID
ctx.net.peers(); // 今ルームにいる全員
ctx.net.onJoin((peer) => {});
ctx.net.onLeave((peer) => {});
自動で同期されるもの
自動で同期されるのは、shared に設定した物理ボディの位置だけです。それ以外は、自分で送ります。
これは意図した動作です。木箱、ドア、ボールは、全員に同じ場所に見える 1 つのオブジェクトであるべきです。一方、プレイヤーキャラクターは共有してはいけません。共有すると、1 人が歩いたときに全員のアバターが動いてしまいます。→ 所有の設定
メッセージ
メッセージは、誰かが得点した、ラウンドが始まった、ドアが開いた、といった出来事を伝えるときに使います。
ctx.net.send('score', { points: 10 });
ctx.net.on('score', ({ from, data }) => {
/* … */
});
メッセージは、自分以外の全員に届きます。届いたという確認は返ってきません。
共有ステート
全員で同じ値を持つ必要があるときは、共有ステートを使います。シンプルなマップで、全員がそのコピーを持ちます。2 人が同じキーに書き込んだ場合は、最後の書き込みが残ります。
ctx.net.state.set(ctx.net.id, { name, colour, ready: true });
ctx.net.state.get(somePeerId);
ctx.net.state.all();
ctx.net.state.on(ctx.net.id, (value) => {});
プレイヤーごとに値を持たせるときは、ctx.net.id をキーにします。各自が自分の項目に書き込み、ほかの人の項目を読みます。
全員向けに生成する
const id = ctx.net.spawn(props.bullet, { position, rotation });
ctx.net.despawn(id);
ID はすぐに受け取れますが、オブジェクトが表示されるのは少しあとです。ID は全員で同じです。
オブジェクトを削除できるのは、それを生成した人だけです。誰かがルームを抜けると、その人が生成したオブジェクトも削除されます。
ほかのプレイヤーをなめらかに動かす
ここはいちばん失敗しやすいところなので、専用の呼び出しを用意しています。
ctx.net.follow(otherPlayerObject, { position, rotation });
ctx.net.unfollow(otherPlayerObject);
位置を受け取るたびに follow を呼び出します。1 秒に 10〜20 回が普通です。あとはオブジェクトが、最新の位置から少し遅れて、なめらかに動きます。
同じ位置で follow をもう一度呼び出しても、負荷はかかりません。そのため、手元にある最新の位置で毎フレーム呼び出してもかまいません。
更新が届く頻度はフレームよりずっと少ないので、オブジェクトは止まっては跳ぶ、を繰り返します。ほかのプレイヤーが、なめらかに歩かずにカクカクと進んでいるように見えます。最新の位置に向かってゆっくり近づける方法でも解決しません。更新と更新の間には新しい目標がないので、オブジェクトは止まってから急に動き出してしまいます。
うまくいく構成
ゼロからつくるなら、次の構成でたいていのことはまかなえます。
- 各プレイヤーは自分のキャラクターを操作します。キャラクターは local のボディで、本人の入力で動きます。
- 自分の位置は、
net.stateかメッセージで、1 秒に 10〜20 回送ります。 - ほかの人のキャラクターは
followで動かします。 - ワールドのオブジェクトは shared のボディにします。同期は物理が自動で行います。
- スコアとラウンドのステートは
net.stateに置きます。書き込むのは、その出来事を担当している人です。
次へ: トラブルシューティング — うまく動かないときの対処法。