ブラウザベースのメタバースを作る5
前回までのあらすじ
この記事シリーズではブラウザベースのメタバースを作っていきます。
前回はメタバース実装の準備として、2Dの箱をアバターに見立てて位置と画像の同期を実装しました。
記載するコードの動かし方
第1回をまだ見てない人は、第1回の最後にある環境構築の項目を呼んで環境を構築してください。
その後、ターミナルを開いて以下のコマンドを入力してhttpサーバを起動します。
cd 配置先ディレクトリ
node httpd.js
次に、もう1窓ターミナルを開いて以下のコマンドを入力してシグナリングサーバを起動します。
cd 配置先ディレクトリ
node wss_signaling.js
配置先ディレクトリを基準として、ファイルの種類によって以下のディレクトリに配置します。
ファイル種類 | 配置先 |
---|---|
html | web/ |
js | web/ |
vrm | web/asset/vrm/ |
fbx | web/asset/animation/ |
glb | web/asset/model/ |
ssl証明書 | ssl/ |
その他設定ファイル | config/ |
ロードマップ
- 環境構築
- three-vrmのサンプルを基にしてプレイヤー操作でアバターを動かせるようにする
- WebRTCによるチャットを実装する
- (前回)WebRTCによるdom要素の位置同期と画像同期を実現する
- (今回)他のプレイヤーのVRMアバターを表示して同期する
今回やりたいこと
今回は、4回目で作った同期処理で、2回目で作ったVRMアバターシステムを同期します。
3回目でシグナリングサーバは完成したので、今回もクライアントの試作だけになります。
実装方針
2回目で作った move_vrm_refactor_phys.html と4回目で作った p2p_syncpic.html を統合してVRMアバターを同期するようにします
p2p_syncpic.htmlの2D箱アバターをmove_vrm_refactor_phys.htmlのVRMアバターに置き換える形で統合します。
ベースはp2p_syncpic.html側にします。
3D描画処理のコードはmove_vrm_refactor_phys.htmlから持ってきます。
所作のリモートへの反映はモーション再生時にリモートにメッセージを投げて、受信側でそのモーションを持っていれば再生、ない場合は送信側にアニメーションfbxの送信を要求します。
この通信用にrpcというデータチャネルを追加します。
rpcで、チャットと移動とファイル転送以外の状態同期の通信を行うようにします。
rpcで扱う処理は拡張される可能性が高いのでファイル転送制御用としてtransportはそのまま残します。
統合する際に、追加・削除・修正する必要がある項目を検討します。
move_vrm_refactor_phys.html側の要素
- DOM
ID | 内容 | 移植 |
---|---|---|
myCanvas | rendererの描画対象 | 移植 |
emotes | モーション選択要素 | 移植 |
btn_emote | 選択モーション実行ボタン | 移植 |
- クラス
名前 | 内容 | 移植 | 改修点 |
---|---|---|---|
Avatar | VRMアバター処理 | 移植 | |
InputSystem | アバター操作入力処理 | 移植 | 移動発生時に位置データ送信 |
- 関数
名前 | 内容 | 移植 |
---|---|---|
initRenderSystem | 描画処理初期化 | 移植 |
setupWorld | ワールド設定 | 移植 |
loadVrmFromUrl | urlからVRM読み込み | 移植 |
loadVrmFromArrayBuffer | バイナリデータをVRMとして読み込み | 移植 |
animation_loop | フレーム処理関数 | 移植 |
onDropFile | ファイルドロップ時処理 | p2p_syncpic.html側のonDropFileにvrmとfbxの対応を移植する |
setupEmote | モーション選択要素設定 | 移植 |
doEmote | モーション実行 | 移植 リモートに通知する処理追加 |
toggleEmote | 所作ui表示切替 | 移植 |
setLocalMotion | モーション設定 | 移植 リモートへの通知を追加 |
initPhysics | 物理演算の初期化 | 移植 |
createPlayerBody | プレイヤーの物理判定作成 | 移植 |
setPlayerVelocity | プレイヤーの移動入力の物理演算への反映 | 移植 |
setPlayerJump | ジャンプ操作の物理演算への判定 | 移植 |
getPlayerPhysics | プレイヤーの物理演算の位置と速度取得 | 移植 |
fixFollowCamera | 地形に遮蔽されている場合カメラの位置を調整する | 移植 |
createStaticBody | 地形判定の作成 | 移植 |
createStaticBodyFromMesh | 地形判定の作成 | 移植 |
tickPhysics | 物理演算の実行 | 移植 |
procMove | 物理演算のプレイヤー位置への反映 | 移植 |
procVelocity | 移動入力の処理 | 移植 |
p2p_syncpic.html側の要素
move_vrm_refactor_phys.html側で定義しているものと重複するクラス・関数・削除は削除します。
また、Avatar,InputSystemの仕様が変わるので、それらを扱う処理は修正が必要になります。
Avatarへの入力情報の仕様が変わるので、transformの通信に使っている関数も修正が必要になります。
- DOM
ID | 内容 | 移植 |
---|---|---|
box_space | 箱アバター表示コンテナ | 箱アバター用なので移植せず削除 |
- クラス
名前 | 内容 | 移植 | 改修点 |
---|---|---|---|
Avatar | アバター単位の処理をまとめている | move_vrm_refactor_phys.html側を使うので移植せず削除 | |
InputSystem | 入力処理 | move_vrm_refactor_phys.html側を使うので移植せず削除 |
AvatarとInputSystemはmove_vrm_refactor_phys.html側のものを使うのでこちら側は削除です。
- 関数
名前 | 内容 | 移植 | 改修点 |
---|---|---|---|
animation_loop | フレーム処理関数 | move_vrm_refactor_phys.html側を使うので移植せず削除 | |
packTransform | アバター状態を送信用データに変換 | 移植 | データ項目拡張 |
unpackTransform | 受信データをアバター状態に変換 | 移植 | データ項目拡張 |
sendTransform | アバター状態送信 | 移植 | データ項目拡張 |
createLocalPlayer | ログイン時のアバター関係初期化処理 | 移植 | AvatarとInputSystemの初期化パラメータ変更 |
createOther | 他のプレイヤー参加時のアバター追加処理 | 移植 | Avatarの初期化パラメータ変更 |
removeOther | 他のプレイヤー離脱時のアバター削除処理 | 移植 | |
moveOther | 他のプレイヤーの移動通知受信時処理 | 移植 | パラメータ拡張 |
sayOther | 他のプレイヤーの発言時処理 | 移植 | |
restoreLocalAvatar | indexedDBからアバターを復元 | 移植 | 保存場所の変更 |
setLocalAvatar | アバターを設定しindexedDBに保存 | 移植 | 保存場所の変更 |
setRemoteAvatar | リモートアバターに設定 | 移植 | |
sendAvatar | 他のプレイヤーにアバターを送信する | 移植 | |
onCompleteFileReceive | ファイル受信完了時の処理 | 移植 | モーション受信時の処理追加 |
onDropFile | 画面にファイルをドロップしたときの処理 | 移植 | move_vrm_refactor_phys.htmlの実装を参考にしてvrm,fbxをドロップした場合の対応を追加 |
onDropFileでvrm/fbxをドロップした場合は、アバター変更/モーション追加の処理をすると同時に、indexedDBへの保存をします。
これにより、リロード時や部屋移動時もアバターとモーションを維持します。
意図しないアバターを選んでしまわないようにログインが必要になった時点で保存したアバターとモーションは削除します。
- 変数
名前 | 内容 | 移植 | 改修点 |
---|---|---|---|
dic_ticks | フレーム処理関数で実行する処理を積む連想配列 | move_vrm_refactor_phys.html側を使うので移植せず削除 | |
avatars | アバター(箱)の管理用連想配列。 | 移植 | 中身がmove_vrm_refactor_phys.htmlのAvatarに変わる |
channels_def | データチャネル定義。 | 移植 | rpcの処理追加 |
新規に追加する要素
- 関数
名前 | 内容 | 説明 |
---|---|---|
restoreEmote | モーション復元 | indexedDBに保存したモーションを復元する |
setRemoteMotion | リモートアバターにモーション設定 | リモートから受信したデータを設定 |
実装結果
ソース
動画
操作方法
操作 | 動作 |
---|---|
W | 前進 |
S | 後退 |
A | 左移動 |
D | 右移動 |
space | ジャンプ |
マウスドラッグ | 視点制御 |
C | カメラ切り替え |
ファイルドロップ(.vrm) | アバター変更 |
ファイルドロップ(.fbx) | モーション追加 |
ファイルドロップ(そのほか) | ファイル転送 |
M | 所作選択ui表示切替 |
名札クリック | 所作選択ui表示切替 |
テスト方法
https://localhost:10443/p2p_syncvrm_phys.html を2窓で開きます。
それぞれの画面で部屋aを指定してログインします。
移動やチャットがリモートにも反映されることを確認します。
vrmファイルをドロップすると自分のアバターが更新されます。
リモートにも反映されることを確認します。
モーション用fbxファイルをドロップするとそのモーションを再生します。
自分の名札をクリックすると、モーション再生用のドロップダウンと所作ボタンが表示されます。
ドロップダウンでモーションを選択して所作ボタンを押すと、そのモーションを再生します。
このとき、リモートにもモーションが反映されることを確認します。
ブラウザのリロードを行い、アバターが維持されることを確認します。
リモートにも反映されていることを確認します。
自分の名札をクリックして、追加したモーションが維持されていることを確認します。
所作ボタンでモーションを再生されることを確認します。
このとき、リモートにもモーションが反映されることを確認します。
修正点の解説
ほとんどの修正は、Avatarクラス及びInputSystemクラスを2章のものに差し替えることへの対応ですが一部複雑なものがあります。
- 位置同期の情報
4章の実装は2次元用なので、3次元の座標と速度、アバターの向きを通信するように修正する必要があります。
関連関数 |
---|
packTransform |
unpackTransform |
sendTransform |
moveOther |
- 位置同期を送信する条件
4章では、移動入力が変化したか1秒以上経過で送信しています。
しかし物理演算がある場合は、入力がなくても位置と速度の変化がありえます。
そこで、実際の位置と速度の変動があるか1秒以上経過で送信するようにします。
実際にはそれだと頻度が高すぎるので最低送信間隔を設定します。
関連関数 |
---|
InputSystem.tickSync |
- アバター位置の決定
4章の実装では、ローカルアバター・リモートアバター共に入力情報から位置と速度を決定していました。
しかし物理演算がある場合はローカルアバターは物理演算の結果から位置と速度をする必要があります。
このためローカルアバター用とリモートアバター用で処理を分ける必要があります。
関連関数 | 備考 |
---|---|
procVelocityLocal | ローカルアバター用 |
procVelocityRemote | リモートアバター用 |
procMove | ローカルアバター用 |
InputSystem.defaultProcMove | リモートアバター用 |
カスタマイズ指針
ワールドを増やしたい場合は、ページ自体を増やしてカスタマイズすることになります。
ワールドのデザインはsetupWorld関数の中でシーンに対するオブジェクトの追加として記述します。
移動やチャット以外の操作をリモートに反映したい場合は、
const msg = JSON.stringify({"mode":"何かの処理"})
for(const id in connections){
connections[id].send("rpc",msg);
}
//ここでローカルへの反映を実行
こんな感じで送信を行い、受信側はchannels_defのrpcのonmessageで
}else if(info.mode=="何かの処理"){
//ここでリモートプレイヤーの操作を反映する
}
という感じの分岐を追加します。
この試作で未対応の機能
ボイスチャットやVRデバイスについては対応していませんが、この辺については
「WebRTC ボイスチャット」とか「THREE.JS VRデバイス」とかで検索すれば実装のための情報は手に入ると思います。
終わりに
駆け足ですが、最低限の機能を持つメタバースの実装を行いました。
WebRTCやthree-vrmそのものの使い方を書いている記事は検索ですぐに見つかるのですが、それらを使ってクライアント間の同期を行う手順についてあまり見当たらないのでこの記事を書きました。
第一回にも書いた通り、インターネット上で商用サービスを構築するのには向きませんが、プログラミング教育の題材としては面白いのではないかと考えています。
Discussion