78K Views
March 26, 24
スライド概要
XR Kaigi 2023での登壇資料です。
https://www.xrkaigi.com/pages/speaker_detail?speaker_id=xrkaigi2023_speaker_kyohei_koda
発表動画はこちらよりご覧いただけます。
https://youtu.be/WJH0PdiTbUE
バーチャルキャストの舞台裏: メタバースの長期運用を実現する技術と戦略 XR Kaigi 2023 株式会社バーチャルキャスト 打田 恭平
自己紹介 • 打田 恭平(HN:とりすーぷ) • 株式会社バーチャルキャスト • 相互体験開発セクション マネージャ • リアルタイムサーバ/Unityクライアント開発 • プロダクト全体のアーキテクチャ監修 • 個人活動 • 著書 : UniRx/UniTask完全理解 より高度なUnity C#プログラミング • Microsoft MVP 2018~
本セッションの内容 • サービス開始から5年が経過する 「バーチャルキャスト」がどのような技術や戦略で 運用されてきたのかを解説します
本スライドについて • 発表時間に収めるために説明を省略している箇所があります • 後ほど公開されるスライドはフル版となるため、 詳しく知りたい方はそちらをご覧下さい
もくじ • バーチャルキャストの概要 • ルームとスタジオ • ルーム開発の経緯 • メタバースとしてのバーチャルキャスト開発 • 全体サービス構成 • クライアントのアーキテクチャ構成 • 長期運用に向けた組織としての取り組み • プロダクトの品質維持のための意識、取り組み
バーチャルキャストの概要
バーチャルキャスト • 2018年にVR生放送配信ツールとしてリリース • 1つのVR空間にリモートから複数人が参加して生放送番組が作れる • VR参加者と生放送の視聴者とでインタラクションが行える
バーチャルキャスト • 2021年、メタバースとしての機能を大幅強化 • 「配信」だけでなく「メタバース」も遊べるサービスへ進化
バーチャルキャストの特徴 • VCI(Virtual Cast Interactive)が使える • VR空間内で使用可能なアイテムのフォーマット • スクリプトやエフェクトの埋め込みが可能 • ユーザが自由に作成しバーチャルキャストの世界へ持ち込むことができる
ストア機能 • ユーザが自由にVCIやVRMの販売できるストアページ • VR内から直接VCIアイテムを選択して購入することができる
クライアント対応プラットフォーム • Windows • MetaQuest2 ~
バーチャルキャストがもつ 2つのシステム バーチャルキャストの概要
どちらかを選択する
スタジオ • サービス初期から開発されていた配信向けシステムの名称 • 配信特化の機能を多く持つ • 凸機能(他の配信者の乱入) • 生放送サイトとのコメント連携 • 生放送サイトとのギフト連携(Vギフト機能) • デスクトップ/外部モニター入力のキャプチャ機能 • ホワイトボード機能
ルーム • 「メタバース」としての体験を中心としたシステム • 「その空間にあるコンテンツで遊ぶ」をメインとする • 自由に自分のルームを作り、その中身をカスタマイズできる • ルーム内に置いたVCIアイテムは保存され永続的に残る
なぜシステムが2つ存在するのか? • 2つにわけざるを得なかった経緯がある
スタジオ開発から ルーム誕生までの経緯 バーチャルキャストの概要
スタジオの開発 • 2018~2020年頃:スタジオ開発初期 • 「生放送配信ツール」としての用途を想定して開発 • 「配信が盛り上がるための機能はどんどん追加しよう」という方針で 様々な機能が次々に追加されていった
スタジオがどうなったか • 多くのユーザに愛されるシステムへと成長 • 開発スパンが早く、次から次へと新機能が投入されていった • 「VRで配信するための必要な機能」がすべて詰まったツールとして ユーザから高く評価されている
一方で… • 「開発の身動きが取りづらくなってしまった!」 • 実装が密結合だらけになり機能追加や改修が困難となってしまった • どこかを書き換えたらどこかの機能がすぐ壊れてしまう、という状態に • 保守だけで手一杯であり新規に機能を追加する余裕が無くなっていた • 「機能を削ってスリム化する」という選択ができなくなった • どんな複雑な仕様の機能であってもヘビーユーザがいるため削れない
どうなったか • スタジオは愛用してくれている多くのユーザがいる • だがスタジオは保守するだけでもギリギリであり、 これ以上の機能追加は高コストかつ高リスクである • 一方で、会社としてはまだまだやりたいビジネスが多くあり どんどん機能を拡張していきたい • スタジオとは別のシステムを並行して作ろう
「ルーム」の開発へ • 「配信」から「空間のコンテンツ」主体へ • せっかく作り直すのだから「配信特化のシステム」ではなく、 「配信にも使えるより汎用的なシステム」を目指す • いわゆる「メタバース」を目指す • ゼロから再設計する • スタジオは一旦置いておいてゼロベースで仕様や機能を考え直す
メタバースとしての バーチャルキャスト開発
ルームのサービス構成 メタバースとしてのバーチャルキャスト開発
バーチャルキャストが目指すメタバース • 「VCIアイテム」を最大限活用する • ルーム内に自由にアイテムを持ち込んで遊ぶことができる • 持ち込んだアイテムをルームに保存できる • アバターによる全身を用いたコミュニケーション • 音声通話によるリアルタイムでの会話 • 全身のトラッキング • 表情やリップシンクの同期
スタジオとルームの比較 スタジオの仕様 対象 ルームの仕様 配信 体験の主体 空間内のコンテンツ 1人1スタジオのみ 1人が持てる 空間の数 制限なし 同じアイテムは1人1個まで 出せるアイテム 制限なし Y=0固定の平面移動のみ 移動の制約 制限なし ホストユーザのローカル 空間内の権限設定 Web上でルームごとに設定 クライアントPCで管理する 空間の状態 サーバ上で管理する クライアントがホストとなる ネットワーク モデル 専用サーバが処理する (一部はクライアントホスト型を併用)
スタジオとルームの比較 スタジオの仕様 対象 ルームの仕様 配信 体験の主体 空間内のコンテンツ 1人が持てる 空間の数 スタジオはクライアントホスト型のため 同じアイテムは1人1個まで 出せるアイテム クライアントのPCで制御されることが多かったが、 ルームではサーバ側での制御を基本とする Y=0固定の平面移動のみ 移動の制約 1人1スタジオのみ 制限なし 制限なし 制限なし ホストユーザのローカル 空間内の権限設定 Web上でルームごとに設定 クライアントPCで管理する 空間の状態 サーバ上で管理する クライアントがホストとなる ネットワーク モデル 専用サーバが処理する (一部はクライアントホスト型を併用)
実現に必要なシステム • ルーム空間内の状態を管理するシステム • ルーム内のアイテムの状態や座標を管理 • ルームの状態を保存して再訪問時に復元する仕組み • 高スループットな通信システム • アバターやアイテムのモーションなどを低遅延に通信するシステム
実現に向けた戦略 これらを1つのサーバシステムで構築するのではなく • ルーム状態管理サーバ • リアルタイム同期サーバ の2つに分散して実現することにした
ルームのサービス構成
ルーム状態管理サーバ ステートフルに ルームの状態を保持するサーバ (アイテムの存在や配置場所など) 高頻度で通信する必要はないが 信頼性は高い必要がある
ルーム状態管理サーバのもつ責務 • ルームの「現在の状態」を管理しそれを保存する • 参加者の状態 • 参加者の権限管理 • 各オブジェクトの位置姿勢 • 各オブジェクトの内部状態 • 信頼性の高いメッセージ伝送 • ルーム内における各種RPCの転送 • VCIアイテムのメッセージ機能の仲介
ルーム状態管理サーバの技術選定 • 「MagicOnion / gRPC」を採用 • クライアントと密な連携が必要なサーバであるため、 サーバとクライアントをC#で統一できるのはメリットが大きい • インメモリで完結させる • 「1ルーム」は必ず「1サーバインスタンス」内に収めて動作させる • RDBやNoSQLは使用しない
リアルタイム同期サーバ ステートレスに キャラクタのアニメーションや 動いているアイテムの位置姿勢等を 高速で同期するサーバ 常時通信し続けるため 高スループットである必要がある
リアルタイム同期サーバの責務 • 送られたバイナリデータをそのままブロードキャストする • ステートレスである • ロジックはほぼ持たない
リアルタイム同期サーバの技術選定 • 「Rust / C++」を採用 • 根幹の通信部分は枯れているC++で実装 • 外部への制御APIの提供部分をRustで実装 • TCP/RUDPの併用
VoIPサーバ VR空間内での 音声通話を担うサービス スタジオ用に作ったものを そのまま再利用
サービスディスカバリ ルームインスタンスの ネットワーク上での位置を 解決するサーバ ロードバランサも兼任
バックエンド バーチャルキャストの サービス全体のバックエンドサーバ ・アカウント情報 ・ルームの情報 ・フレンド管理 ・所持アイテムの管理 など
Webフロント Webのフロントエンド ユーザが実際にアクセスする Webページを提供 https://virtualcast.jp/
ルーム稼働時の通信の様子 ルーム接続時は これら3つのサーバと 常時通信する
ルーム稼働時の通信の様子 クライアント同士は 直接通信しない 各種サーバを介して 状態を同期する
環境 • Google Compute Engine上で稼働 • 当初は「Google Kubernetes Engine」で動作させていた • Kubernetesはネットワークの細かい設定ができず運用に難があったので もっと簡易に運用できるGCEに移行した
オブジェクト同期の流れ メタバースとしてのバーチャルキャスト開発
最初の状態 • 1つのルームに2つのクライアントが参加している状態 2つのサーバを組み合わせて 1つのルームインスタンスを 動作させている
例:アイテムの管理 今からアイテムを1つ出します!
1. ルーム状態管理サーバにリクエストを送る アイテムを (10, 5, 0) に 置きたいです
2. サーバ上で権限チェックを行う この参加者は作成権限 を持っているか?
3. サーバ上にアイテムの存在を配置する 権限OKなので アイテムを(10, 5, 0) に 配置します! (10, 5, 0) サーバのメモリ上に アイテムの存在を作り、 (10, 5, 0)という状態を 保持する
4. 各クライアントにイベント通知を行う アイテムが(10, 5, 0) に 出現しました! ! ! (10, 5, 0)
5. アイテムをVR空間内に実体化する サーバの状態を VR空間上に再現するぞ! GameObjectの Instantiate (10, 5, 0) (10, 5, 0) サーバの状態を VR空間上に再現するぞ! GameObjectの Instantiate (10, 5, 0)
6. アイテムの「存在」はサーバ側で管理 アイテムAは 「(10, 5, 0)にあるよ」 「今の所有者はXさんだよ」 「保存されるよ」 (10, 5, 0) (10, 5, 0) (10, 5, 0)
7. アイテムを動かした アイテムを 動かした! (10, 5, 0) (10, 6, 0) (10, 5, 0)
8. 座標をリアルタイム同期サーバで同期 アイテムを 動かした! (10, 5, 0) (10, 6, 0) アイテムAが (10, 6, 0)に動いた アイテムAが (10, 6, 0)に動いた (10, 5, 0) ↓ (10, 6, 0) ! 状態の保持はしない 受け取った情報を そのまま転送する
9. ルーム状態管理サーバへも通知する そういえば アイテムAが (10, 6, 0)に動いてました (10, 6, 0) (10, 6, 0) アイテムAの 座標が変化したから 更新しておこう (10, 5, 0) ↓ (10, 6, 0)
通信頻度 座標送信は ルーム状態管理サーバへは 1アイテムあたり 0.1 ~ 1 TickRate の同期頻度で問題ない 一方でRPC系は 即時性が重要なので リクエストがきたら 即送信 (10, 6, 0) (10, 6, 0) 座標送信は リアルタイム同期サーバへは 10 TickRate前後 (全体のアイテム量に応じて調整) (10, 6, 0)
10. 新参者が参加したときの同期 新しくルームに参加しました! (10, 6, 0) (10, 6, 0) (10, 6, 0)
11. 現在のルーム状態を問い合わせる いま参加したので ルームの現在の状態を 教えてください! (10, 6, 0) (10, 6, 0) (10, 6, 0)
12. サーバの状態をクライアントに返す 新規参加者が来たときは ルーム状態管理サーバの持つ状態を 「今のルームの正しい状態」 として扱う アイテムAが(10, 6, 0) にあるよ (10, 6, 0) (10, 6, 0) (10, 6, 0)
13. VR空間上にアイテムを生成する アイテムをVR空間に 生成して同期完了! GameObjectの Instantiate もしこのときに生成座標が 多少ズレてたとしても すぐにリアルタイム同期サーバから 最新の値が送られてくるため それで補正すればOK (10, 6, 0) (10, 6, 0) (10, 6, 0) (10, 6, 0)
ルームの状態保存について • ルームの状態の保存 • ルーム状態管理サーバのメモリ上のアイテム状態を書き出す • ルームの状態の復元 • 書き出した情報をルーム状態管理サーバのメモリに展開する アイテムA Xさんが出した (10, 6, 0)にある アイテムB Yさんが出した (-15, 1, 0)にある アイテムC Yさんが出した (0, -1, 20)にある ルームの最終状態を書き出して 「バックエンドサーバ」に 保存する
ルームのシステムの肝 • 「ルームの状態」のマスターデータがサーバ側にある • クライアント上の表示はサーバ上にある状態の「再現」にすぎない • ルーム制御に重要なロジックがクライアント側に 含まれていない • 「バーチャルキャストのUnityクライアント」以外がルームサーバに 接続してルームを操作してもOK!
活用:ルーム内アクター同期システム • ルーム内のアクター(参加者)の「モーション」や「音声」を コピーして他のルームに転送するシステム
【07/20(水)20:30~】Vキャスちゃんねる#107【ルームアクター同期システムで遊ぶ】 https://www.youtube.com/watch?v=YbM7ITdkX4s
「ルーム内アクター同期システム」は クライアントアプリの一種 各ルームサーバに接続してデータの 橋渡しを行うことで ルーム内のアクター情報をコピーする
ルームの クライアントアーキテクチャ メタバースとしてのバーチャルキャスト開発
クライアント開発環境 • Unity • バージョンは2022.1(2023年11月現在) • Universal Render Pipeline • 使用ライブラリ • MagicOnion • UniRx/UniTask • Zenject • MessagePack/Protocol Buffers など
ルームを設計するにあたり • スタジオで得た教訓を活かそう • スタジオは何がよくなかったのかを分析
スタジオでの反省 • 抽象化がほとんど行われていない • 具象クラスが密結合の中心にいて神クラスに育っている • モジュール分割がほとんどされていない • どこに何を置くべきかが明確になっておらず、全体がぐちゃぐちゃ • 外部モジュールに強く依存してしまっている • 動作に重要なクラスがMonoBehaviourを継承してしまっている • 自動生成されたデータ構造がそのままドメイン内で使い回されている
スタジオのアーキテクチャ図 「スタジオ」が 外部モジュールに直接依存している! (抽象化を挟まずにダイレクト依存) 「スタジオ」の パッケージ内部は分割されおらず モノリシックなシステムになっている
スタジオがこうなった原因 • スタジオは「設計思想」が希薄だった • 設計思想:このときはこうあるべきという設計上の理念、ルール • 設計思想が希薄だと「何が正しい状態なのか」を判断できないため プロダクトがいま健全な状態なのかどうかすらわからなくなる ルームでは設計思想をちゃんと考えることにした
アーキテクチャ設計で参考にした書籍 Clean Architecture 達人に学ぶソフトウェアの構造と設計 (アスキードワンゴ) Adaptive Code C#実践開発手法 第2版 (日経BP) https://asciidwango.jp/post/176293765750/clean-architecture https://bookplus.nikkei.com/atcl/catalog/18/P53540/
ルームの設計思想 1. 外部モジュールとの密結合を避ける 2. ルームの挙動は具体的な実装の影響を受けてはいけない 3. ルームへの操作を一貫させる
1. 外部モジュールとの密結合を避ける • 外部要因でプロダクトが振り回されるのを避ける • もし外部モジュールに突然の仕様変更などが起きたとしても、 ルームの振る舞いには影響が出ないようにしたい • 極端な例を挙げるなら「Unity以外に載せ替えてもルームは動くようにしておきたい」 (実際、Unityからの載せ替えは現実的ではない。気持ちとしてそれくらいの疎結合さにしておきたいという意味) 「自分たちがコントロールできないもの」に 強く依存してしまうのを避ける
2.ルームの挙動は具体的な実装の影響を受けてはいけない • 「ルームはどう振る舞いたいか」を最優先したい • 「具体的な実装」によってルームの挙動が決まってしまうのを避けたい • どんな実装方法に差し替えても、ルームの動作は変わらないでほしい • 例:「サーバとの通信プロトコルはHTTPだから、HTTPステータスコードでの エラーハンドリングをルームのコアロジックに含めちゃおう」みたいなのはNG 過去の実装が邪魔をして 将来的な選択肢を狭めるのを防ぎたい
3.ルームへの操作は一貫させる • 個人が好き勝手に実装するのを禁止する • 「この処理はここのパッケージに書く」をルール付けする • 人によって実装がブレるのを防ぎたい コードの可読性の向上、拡張性の向上、 属人化の防止など
それぞれのモジュールごとに Assembly Definition Files が定義されている (dll分離されている)
ルームの概念や振る舞いを扱う重要なレイヤ これらモジュールはすべてピュアC#である (UnityEngineのAPI や MonoBehaviour に非依存)
外部モジュール群 バーチャルキャストの意思とは無関係に 変更される可能性がある場所 (我々がコントロールできない領域)
「外の世界」と「ルーム」を繋ぐレイヤ 具象クラスがメインとなる
具象が中心 抽象が中心
こちらが「どう実現するか」を決める こちらが「ルームはどう振る舞いたいのか」を決める
「ルームの中心的概念」と「具体的な実装方法」の間に境界を設けた 具象が中心 抽象が中心 これが 1.外部モジュールとの密結合を避ける 2.ルームの挙動は具体的な実装の影響を受けてはいけない を表している
3.ルームへの操作を一貫させる 外界からルームへの「API」はUseCasesが提供する UseCasesさえ触れればルームを制御できる
アーキテクチャの詳細解説 メタバースとしてのバーチャルキャスト開発
VirtualCast.EnterpriseBusinessRules バーチャルキャストのクライアントにおいて最も安定なモジュール。 一度定義すればほぼ不変であるstructやEnum定義がおかれている。 例:「UserId構造体」「RoomKey構造体」「PlayerType列挙型」
VirtualCast.Rooms.Core 「ルーム」の動作に重要なオブジェクト・ロジックが定義されるモジュール。 ルームの機能改修はここを根幹に行われる。 例:「Roomクラス」「Participantクラス」「PhotoCaptureCameraクラス」
VirtualCast.Rooms.Infrastructures 「ルーム」の動作を実装するモジュール。 例 ・ルーム状態管理サーバとの通信 ・リアルタイム同期サーバとの通信 ・VRコントローラからの入力処理 ・カメラで撮影した画像の保存処理
VirtualCast.Rooms.Views UnityEngine/MonoBehaviour/GameObjectを扱う。 ルーム内の3Dオブジェクトの描画や相互作用を行う。
VirtualCast.Rooms.Presenter Rooms.CoreとViewsを結ぶPresenter。 Model-View-Presenterパターンで Rooms.CoreとViewsを結合している。
PhotoCaptureCamera VR空間内で写真撮影ができるプリセットアイテム • 撮影画像はローカルPCに保存される 撮影した写真データ
「使う!」
①Viewは 「3Dオブジェクトの描画」と 「プレイヤーとのインタラクション」 を責務とする 「使う!」
「使う!」 ②カメラが「使われた」という イベントをCoreに伝える
「使う!」 ③「写真撮影を実行する!」
「使う!」 ④撮影処理を委譲!
⑤UnityのAPIを使って 写真を保存! 「使う!」
Rooms.Core.PhotoCaptureCamera これが本体(大本)となるクラス 撮影するという振る舞いがTakePhotoAsyncという メソッドとして定義されている
Rooms.Core.ITakePhotoService 「写真撮影をする」を抽象化したもの。 ただのインタフェース。
GameObject Rooms.Views.PhotoCaptureCameraView VR空間内に表示されるオブジェクト VRのユーザはこのオブジェクトを持ったり使ったりできる MonoBehaviourを継承している
Rooms.Presenter. PhotoCaptureCameraPresenter CoreとViewを繋ぐ仲介役 Viewのオブジェクトを「手に持って使った」ときに、 Coreの「TakePhotoAsync」を実行する
Rooms.Infrastructures. UnityTakePhotoServiceImpl 「写真撮影をする」をUnityAPIを用いて実現したもの。 カメラ映像をRenderTextureに書き込んでから 画像ファイルとしてファイル出力する。
「手に持って使う!」
「アイテムを使ったイベント」をPresenterが検知! Rooms.Coreの「TakePhotoAsync()」を呼び出す
写真撮影処理を呼び出す! (ただし実際にどういう処理が走るのかは Rooms.Coreは知らない)
具象クラスが Unityにおいて最適な方法で「写真撮影」を実現する 画像として保存
VirtualCast.UI バーチャルキャストのUI機構(リングメニュー)の実装モジュール。 ユーザの操作をルームに反映する。
VirtualCast.ApplicationBusinessRules.Usecases 略して「ABR.Usecases」と呼ぶ。 ルームに対する一連の手続きを簡単にして提供する。
VirtualCast.ApplicationBusinessRules.Interfaces 略して「ABR.Interfaces」と呼ぶ。 ルームの動作を補助する機能を抽象化して定義するモジュール。
VirtualCast.Frameworks ABR.Interfacesへの 実装を提供するモジュール。 外部モジュールとのAdapterや 腐敗防止層としての役割を持つ。
例:「アイテムを出す」 • 「VCIアイテム」を自分の正面に出す
これは「UI」が描画しているメニュー 「新規作成ボタン」を押すと 「アイテムを出す」という処理行われる
UIは UseCasesに定義された 「VCIアイテムを出す」という処理を 呼び出す
一言で「アイテムを出す」と 言ってもその手続きは複雑 これを外からメソッド1つで 実行できるようにするのが 「ABR.UseCases」
これはサーバに 問い合わせないとわからない こういった 「外部に問い合わせないといけない」 みたいな処理を 抽象化して隠蔽するのが ABR.Interfaces
UseCasesさえ呼び出せば 期待する結果が得られる UseCasesは ABR.InterfacesやRooms.Coreへの操作を Easyになるようにまとめてくれるモジュール
Frameworksが ABR.Interfacesを 実装する ABR.Interfacesは 外部モジュールへのアクセスや 具体的な処理の詳細を抽象化して UseCaesに提供する
外部モジュールを適切に呼び出したり 何かの処理を実際に行ったり Frameworksは具体的な実装の知識を持つ
UseCase内では単にインタフェースを呼び出すだけで終わり。 裏でサーバと通信して現状を確認しているがUseCase側からは その詳細を知る必要はない。
UseCaseが他のUseCaseを頼ることもある
ルームとスタジオとの共存
ルームとスタジオとの共存 スタジオとルームで共通する概念を Enterprise Business Rules に定義する
スタジオ内のモジュールを使いたい場合 • 「スタジオに密結合している重要なシステム」がある • タイトル画面で行った設定の情報など • ルームでそのまま使いたいが、スタジオに実装が食い込んでいる
スタジオとルームの接合 仲介するインタフェースを挟む 「Stairwayパターン」で 間接的にスタジオ内のモジュールにアクセスする ルーム スタジオ
このアーキテクチャを 運用してどうだったか メタバースとしてのバーチャルキャスト開発
このアーキテクチャの良かった点 • 実装・保守のコストが大きく減った • 新規実装時に「どこに何を作ればいいのか迷う」が減った • 全体が把握しやすいので既存機能の保守も楽になった • 実装の追加・差し替えが容易にできるようになった • ルームの中心実装を触らずに新デバイスへ対応やインフラの 差し替えが可能となった
インフラの差し替え • ルーム初期はまだリアルタイム同期サーバは完成していなかった • そのためルームリリース初期はスタジオで用いていた通信インフラに相乗り • リアルタイム同期サーバの完成を持って、ルーム側のインフラをそちらに差し替えた • その際、「具体的な通信方法」の管理は全てInfrastrucuturesで扱っていたため インフラ差し替えにあたってRooms.Core内部に変更は一切必要なかった
ここのモジュール内の実装を変更するだけで 通信インフラの差し替えが実現できた
パフォーマンスについて • このアーキテクチャ起因で パフォーマンスが低下するようなことはなかった • GCアロケートが大量に起きる、といったことはない • 強いて言うならDIのコストが若干あるが、 レンダリング周りの負荷に比べたら微々たるものであるので問題は無い
テストについて • ロジック/手続きのテストは書きやすくなった • Rooms.CoreやABR.UseCasesはかなりテストがしやすい • ロジックや手続きに集中したテストが書ける • ただし具象を扱うレイヤのテストのやりにくさは変わらず • Unity等の外部モジュールに依存した部分はテストしにくい • むしろテストしにくい部分をこのレイヤに分離した、と考える (Humble Objectパターン)
ルームは成功を収めた • 長期運用に耐えうるシステムが構築できた • ルームの要求を無理なく実現できる全体のシステムが構築できた • 保守性や拡張性において全く問題が起きていない
アーキテクチャは構築して終わりではない • アーキテクチャは維持する努力が必要 • エンジニアのみで維持するのは不可能であり、組織全体での維持が必要
長期運用に向けた 組織としての取り組み
内容 • 組織全体としての取り組み • 開発現場での取り組み • 品質保証(QA)の実施
組織全体としての取り組み 長期運用に向けた組織としての取り組み
組織としての取り組み • 運営に関わるすべての人にアーキテクチャの重要性を 認識してもらう • すべての人:エンジニア、企画、デザイナ、プロダクトオーナ、役員… • アーキテクチャを維持することがプロジェクトの品質維持に繋がる、 を社内での共通認識とする
アーキテクチャが崩れる場面 • 無茶なスケジュールで開発が進んだとき • とにかく手を動かして実装しないと間に合わない!みたいな場面では アーキテクチャは軽視されがちになる • プロダクトの大前提をひっくり返されたとき • 設計思想と相反するような機能は アーキテクチャを破壊して無理矢理作る以外にやりようがない • エンジニアのスキルが追いついていないとき • 設計手法やアーキテクチャに対しての十分な知識が足りていないとき
バーチャルキャストでの体制 • プロダクトの意思決定にエンジニアも関わる • トップダウン的に工数やスケジュールが決まらないようにする • アーキテクチャに則ったベストな手法を企画立案段階から一緒に考える • アーキテクチャの保全に関わるコストは惜しまない • 保守やリファクタリングに必要な工数を常に確保する • 開発が遅れたとしてもアーキテクチャを無視するような作り方を強要しない • 新人教育やスキル育成にもコストをかける
開発現場での取り組み 長期運用に向けた組織としての取り組み
開発現場での取り組み • 歪んだ実装が入り込まないように徹底する • 「割れ窓」の対策をする
割れ窓理論 建物の窓が壊れているのを放置すると誰も注意を払っていない という象徴になり、やがて他の窓もまもなく全て壊される - wikipediaより引用
設計の「割れ窓」 • 歪みは放置すると大きくなる • 「既存実装がこうなってた」は免罪符になりがち • 歪んだ既存実装を放置するとそれを真似され歪みが広がっていく • 気づいたころには全体が腐っていた…という状況になりうる
歪みを防ぐために • 事前設計と設計レビューの実施 • 規模が大きい実装を行う場合は事前設計とそのレビューを実施する • 「急がば回れ」の意識を持つ • たとえ急ぎでも設計規則に則った正しい実装を常に優先する • 一旦新規実装を止めてリファクタリングを行う期間を設ける • ドキュメントの整備を行う • その仕様や設計となった意図がわからないと何が正解なのかわからない • Design Docを整備する
Design Doc • エンジニアによるエンジニアのための資料 • 実装の背景・目的 • どのような議論がされて要件・仕様が決定したのか • 他にどんな代替案があり、なぜそれが採用/却下されたのか • どのような設計をしたのか • 実装時に出したプルリクエストへのリンク • 実装後に判明した現在抱えている課題・問題点
品質保証(QA)の実施 長期運用に向けた組織としての取り組み
品質保証(QA) • プロダクトが正しく動作していることを確認すること • 「QAテストを行う」「QAを行う」と呼ぶ • とくに自動テストではカバーしきれない部分を 人の手で機能ごとに確認項目表を準備して動作確認を行う
自動テスト • すべてテストの自動化は難しい • 単体テスト・結合テストなら自動化しやすい • E2Eテストの自動化はコストが高く実現が難しい
自動テストでカバーしきれない部分の確認 • プロダクトの品質をQAテストの実施で担保する • デバイス実機を用いた動作確認 • 動作確認項目表(テストケース)による網羅チェック • QAテストの定期実施 • 新規機能に対するQAテスト
意味のあるQAを行うためには • 意味のあるQAを行うためには事前準備が必要 • 機能要件の把握 • 確認項目表の作成 • 実施スケジュールの確保 • テスト環境の整備 QA担当者にいきなり「今からQAお願いします!」といっても すぐには実施できない
バーチャルキャストでは • QA実施を前提としたプロジェクト進行の実施 • QA担当者に要件定義段階から関わってもらい機能の全容を把握してもらう • 機能仕様書の作成を実施する • QA期間を予め確保しておく(1週間~数ヶ月ほど) • スケジュールに影響がある不具合が見つかった場合は リリース延期の判断を行う
機能仕様書 • 機能の振る舞いが網羅された資料 • 機能の概要や要件 • どのような課題を解決したいのか、機能の目的 • 機能として期待される挙動を具体的にまとめたもの • 機能が仕様通りに正しく動作した場合はどうなるのか? • UIの見た目がどう変わるのか? • どういう効果音が鳴るのか? • 異常系や特定条件下においてはどういう挙動に変化するのか?
最後に 全体のまとめ
長期運用を実現するための技術 • メタバースに最適なアーキテクチャの構築を行う • サーバを「ルーム状態管理サーバ」「リアルタイム同期サーバ」に分離 • クライアントは次の設計思想を意識してアーキテクチャの構築を行った 1. 外部モジュールとの密結合を避ける 2. ルームの挙動は具体的な実装の影響を受けてはいけない 3. ルームへの操作を一貫させる
長期運用を実現するための戦略 • アーキテクチャの維持は組織全体で行う • プロダクト運営に関わるすべての人がアーキテクチャの重要性を理解する • アーキテクチャ維持に必要なコストは惜しまない • エンジニアもアーキテクチャ維持に必死に取り組む • QAを意識したプロジェクト進行を心がける