VRネトゲの作り方:Mikulus Kinect OnlineにおけるPhoton Cloudの活用

この記事はOculus Rift Advent Calendarの4日目です。

はじめに

こんにちは。Oculus Rift Advent Calendar 12/4担当のNeedleです。

先日11/9に開催された第一回Oculus Game Jamで作ったMikulus Kinect Onlineの、特にネットワーク部分について書こうと思います。

Mikulus Kinect Onlineとはこんなソフトです。


なお現在、第五回ニコニコ学会β 研究してみたマッドネスセッションの投票受付中です。投票は本日12/4 23:59までですので、もし良ければこちらのページから投票をよろしくお願いいたします!

Mikulus Kinectとは

Mikulus Kinect Onlineの話の前に、まずその元となったMikulus Kinectについて説明します。

Mikulus Kinect

Mikulus Kinectは、Oculus RiftとKinectとUnity3Dを組み合わせて、プレイヤーが自分の手足や体がVR内で見える状態でミクさんと握手や頭を撫でるといったコミュニケーションが取れるアプリです。

Oculusを着けて最初に感じたのが頭の角度に対する映像の追随の滑らかさでしたが、一方で感じたのが、頭の位置移動が反映されないことや自分の体が見えない事による違和感でした。そのときKinect登場時に話題になったNao_uさんの動画を思い出し、自分でもやってみようと思ったのがきっかけです。「VR内でAIキャラの目を見る」という体験は、モニタ越しのゲームとは全く違ったものとして感じられました。

ここで作ったMikulus Kinectはあくまで 人←→AI というシングルプレイヤーのデモでした。これをマルチプレイヤー化し、複数の人が同じVR空間内に入れるようにしたのが、Mikulus Kinect Onlineになります。

オンライン化にするにあたっての課題

オンライン対応にはネットワークミドルウェアのPhoton Cloudを使いました。以前ワークショップで少しだけ触ったことがあったからというのもありますが、ゲームジャムの会場がPhotonを日本展開されているGMOさんのオフィス内で、中の人に質問をしやすかったからでもあります。Unity用のPhoton CloudはUnity自身の内蔵ネットワーク機能に似た形で使えるよう作られているので、そちらを使う場合も同じ手法を応用できると思います。

ここからはMikulus Kinectをオンライン化する際に直面した課題について書いていきます。

アバターキャラの出現(○○さんがログインしました)

シングルプレイのMikulus Kinectではプレイヤーが操作するアバターキャラを最初からHierarchyの中に置いていましたが、ネットワーク環境では誰かがログインする度にその人のアバターキャラを出現させる必要があるため、Hierarchyにおいておくわけにはいきません。アバターキャラのGameObjectはPrefabにして、ProjectのResources内に置いておきます。

ログイン時のコードはこんな感じです。
(アバターキャラPrefabの名前はここではMKMotionCaptureContainerとなっています)

void OnJoinedRoom(){
    // 参加者全員の世界に新規プレイヤーのアバターが出現する。
    // PhotonNetwork.InstantiateでInstantiateできるのはResourcesフォルダの中身のみ。
    // インスペクタでGameObjectメンバ変数にPrefabを割り当てて使ったりは出来ないので注意
    GameObject character = PhotonNetwork.Instantiate("MKMotionCaptureContainer",
        Vector3.zero, Quaternion.Euler(new Vector3(0,180,0)), 0);

    // こっちの内容は参加者全員ではなく自分の世界でだけ反映されるので、
    // 自分のアバターキャラにモーションキャプチャ&ヘッドトラッキングの関連付けを行う
    character.name ="MKMotionCaptureContainer(Me)";
    mocap.EngagedUsers[0] = character;
    character.GetComponent<HeadFollowsEyeSmoothed>().enabled = true;
    character.GetComponent<SetZigBias>().enabled = true;
    myPhotonView = character.GetComponent<PhotonView>();
    character.transform.parent = meObject.transform;
}

photonviewdetailモーションキャプチャ関節の共有

Photon CloudにはPhotonViewというスクリプトがあります。基本的には、ネットワーク越しにプレイヤー間で共有したいGameObjectにこのスクリプトを貼っておくことで、そのGameObjectの位置・角度・パラメータといった状態を共有できるようになります。キャラクターの動きを共有するにはこれを使います。

通常のオンラインゲームでは、一人のプレイヤーが動かせるのは基本的に自分のプレイヤーキャラ1体です。つまり一人あたりのPhotonViewは1個で十分でした。

これがMikulus Kinect Onlineにおいては話が変わります。全身の関節をKinectでキャプチャし、MMDモデルの関節に割り当てているため、位置・角度をPhotonViewで共有するのも、キャプチャしている関節全てで行わなければなりません。 ((余談ですが、各関節に使うPhotonViewState Synchronizationタイプは、「頻繁に位置が変わる」「送信失敗したデータを再送する必要はない」事から、Unreliable On Changeにしておくのが良さそうです。))

mikuwithskelparts2任意のキャラクターモデルへの対応

以上を踏まえて、単にミクのモデルだけを動かしたいのであれば、ミクモデルの各関節のGameObjectに黙々とPhotonViewを貼っていけばOKです。

しかし将来的にはKAITOやリン・レンなどアバターキャラ(モデル)の切替を可能にしたいので、決め打ち対応はなるべく避けたいところです。Kinectでキャプチャされる関節は24もあるので ((Kinect v2ではキャプチャされる関節数が更に増えているはずです。)) 、モデルを変える度にPhotonViewを貼る作業を24回繰り返すのは生産的ではありません。一方で、モデルによっては関節の数が違い ((シテヤンヨとかゆっくりとかMogg式ミクとか…)) 、24個全部を使わないこともありえます。

よって、PhotonViewの割り当ては初期化時に自動的に行えるとベストです。

PhotonViewスクリプトの自動割り当て

これらの問題を解決するため、当初は以下のような手順を考えました。

  • アバターキャラPrefabのInstantiate時にモーションキャプチャで動かせる関節GameObjectの一覧を作る
    • その関節GameObjectの配列をforeachでループし、それぞれにAddComponent()PhotonViewスクリプトを生成して貼り付ける

しかしこれだと動作しません。

調べて分かったのですが、PhotonViewスクリプトはPrefabのInstantiate時点であらかじめ共有したいGameObjectに貼られていなければならず、初期化時に生成するのでは間に合わないようでした。

しばらく悩みましたが、発想を逆転し、結果として以下の様な手順になりました。

  • Unityエディタを使い、PhotonViewが貼られた空GameObjectを24個作って入れておく
    jointphotonviews

    • アバターキャラPrefabのInstantiate時にモーションキャプチャで動かせる関節GameObjectの一覧を作る
    • その関節GameObjectの配列をforeachでループし、それぞれに先ほど作っておいたPhotonView付きGameObjectを関連付ける
    • モデルの関節数が24個未満だった場合、PhotonView付きGameObjectが関連付けられずに余るので、余った分はDestroy()して始末する

要は初期化時に生成するのがダメでも、初期化時に破壊するのはOKという性質を利用しています。

以下は該当部分のソース。

using UnityEngine;
using System;

public class NetworkPopulator : Photon.MonoBehaviour {
    public GameObject jointPhotonViewContainer;

    void Start () {
        MatchZigfuSkeletonToMMDModel zigfuMMDModel =
            GetComponent<MatchZigfuSkeletontoMmdModel>();
        if (!zigfuMMDModel) {
            Debug.LogError("No MatchZigfuSkeletonToMMDModel found");
            return;
        }
        // skelTransformsにはトラッキングが有効な関節の一覧が入っている
        Transform[] skelTransforms = zigfuMMDModel.trackedMMDBodyParts;

        MKNetworkCharacter[] jointPhotonViews =
        jointPhotonViewContainer.GetComponentsInChildren();

        foreach (ZigJointId jId in Enum.GetValues(typeof(ZigJointId))) {
            int joint = (int)jId; // 毎回キャストしないよう
            if (skelTransforms[joint]){
                // 該当関節があるならPhotonView割り当て
                jointPhotonViews[joint].transform.parent = skelTransforms[joint];
                jointPhotonViews[joint].transform.localPosition = Vector3.zero;
                jointPhotonViews[joint].SetTargetTransform();
            } else {
                // ないなら破壊
                Destroy(jointPhotonViews[joint].gameObject);
            }
        }
    }
}

その他参考資料

2013/12/4現在、Photon公式のドキュメンテーションページが落ちてるようです。
公式ではないですが、こちらのスライドがPhotonの基本概念を理解するのに役立ちそうです。

まとめ&今後の展望


こうしてなんとかゲームジャム終了までに動作する形にまとめる事ができ、その後のデモやTwitterにおいても好評を得ることが出来ました。(一緒に開発した@sinkukkuさん、ありがとうございます。)

Mikulus Kinectで実装した「VR内でAIキャラの目を見る」という体験も不思議な感覚でしたが、「VR内でネットワーク越しに他の人間の目を見る」というのは、生身の人間がアバターキャラの中にいると知っているだけに、また更に違う体験に感じられます。これについては、また別の機会に書ければと思います。

ただ、粗はまだまだ多いですし、当初やってみたかった事もできていないことは沢山あるので、今後少しずつやっていきたいところです。今後考えているところとしては

  • 新規に入ってきた人の立ち位置や向きを重ならないようにずらす
  • その為にPhoton, Kinect (Zigfu), Oculusそれぞれで向いている方角を揃える
  • モーションキャプチャの動きを記録し、後から再生できるようにする
  • 人間が動かすキャラとAIが動かすキャラを混在させる
  • MecanimのIKを活用して肘関節などの位置・角度情報の共有を省き、通信量を減らす
  • 表情制御(HMDかぶってるのにどうやって…?w)
  • 公開する!

などなど。

長いエントリに最後までお付き合いいただき、ありがとうございます。再度になりますが、現在、第五回ニコニコ学会β 研究してみたマッドネスセッションの投票受付中です。投票は本日12/4 23:59までですので、もし良ければこちらのページから投票をよろしくお願いいたします!

Oculus Rift Advent Calendar、明日は@blkcatmanさんです。

「電脳メガネ元年」

電脳メガネサミットとMGOS

先週末の8月4日、福井県鯖江市で開催された「さばえIT推進フォーラム “電脳メガネサミット 〜近未来のメガネを語る〜”」に夏休みを使って行ってきました。

電脳メガネサミット

鯖江市は日本のメガネの殆どを生産するメガネの街として知られています。このイベントは現在開発が進んでいる「頭に付けるコンピュータ」、いわゆる「電脳メガネ」の現在と未来を語るという趣旨のシンポジウムでした。普及しつつあるデバイスどころか、まだカテゴリが立ち上がってすらいないものを取り上げるという、市が主催するイベントとしてはちょっと類を見ないほど先進的な題材だと思いますw。

ゲストも、「電脳メガネ」という名前やメガネ型コンピュータの概念を一躍有名にしたアニメ「電脳コイル」のプロデューサーや、エプソンMOVERIOの開発担当者さんなど架空・現実の電脳メガネにまつわる面々、また電脳メガネのアプリケーションアイデアコンテスト参加者らによる聞き応え十分な話が聞けて、はるばる行ったかいがありました。また、ゲストのみならず来場者も、懇親会で話した場ではかなり濃い面々が揃っていて、文字通り時間を忘れて話しこむほどでした。

と、全体的にとても面白いイベントだったのですが、話が展開していく中であれっ?と思った点もありました。それを抱えて会場ホールから出てきた際、地元のテレビ局に感想を求められたのですが、その場で考えをまとめきる事ができず結局コメントは辞退してしまいました。

その後、他の方の感想ツイートなども読みながらもう少し考えてみましたが、何に違和感を感じたのかまとめておこうと思います。

電脳メガネ=ハードウェアの問題?

MOVERIOのロック画面具体的には、電脳メガネ実現への課題が「通常の眼鏡は数十g程度だが、電脳メガネはまだ数百gと重い」「大きくて不恰好」など、ハードウェアの話に終始していたように感じられました。

それらハードの問題はもちろんそれぞれ重要な課題で、まさに鯖江の眼鏡に関する技術や職人芸の腕の見せ所です。しかし、電脳メガネにとって最大の問題はむしろソフトウェアやユーザインタフェース(UI)・ユーザ体験(UX)、そしてOSなのでは?と思い、それについて質問してはみましたが、「それもあるね」程度の扱いで、あまり突っ込んだ話は聞けませんでした。

アイデアコンテストに寄せられたアイデアは面白そうで、聞いていてワクワクするようなものが沢山ありました。ただ、それらを実験デバイスではなく常用できる製品として実際に実装し、更には単発の製品ではなく生態系の一要素として展開・普及させられるためには、まずそのアイデアを実装できるためのプラットフォームが存在することが前提です。

その為には、まずは現行のスマートフォンでやれている行動を満足にメガネでも出来るように、ユーザ側に向けてはハード・ソフトの統合的なUXを、開発者側に向けてはSDK/APIの設計を整備する方が先決だと思うのです。それにはUXのハードウェア面を実現するための物理的なものづくりの技術も必要ですが、UXのソフトウェア面や、開発者向けの環境を実現するための全く異なった技能もまた非常に重要になってきます。

MGOSを作る

Google Project Glass UIモックアップ過去、人とコンピュータのインタラクションのパラダイムが変わった際には、その新しいパラダイムを最初から前提とした新しいOSが産まれ、そのOSを核とした生態系が発生する事がありました。

「ウィンドウ・アイコン・メニュー・ポインタ」ベースのGUIが登場した際にはMac OSとWindows、モバイルタッチスクリーンUIの際にはiOSやAndroidが、それぞれ新たに普及したOSとして登場してきました。同様に、ヘッズアップディスプレイ(HUD)やARといったパラダイムが受け入れられるためには、HUDやARを活用する事をはじめから前提としたUI/UXを持つ新しいOSを作らなければならないのではないでしょうか。

実際、近未来で展開する「電脳コイル」の劇中登場人物たちのメガネ上で動いていたのはWindowsでもAndroidでもなく、架空のメガネ用OS「MGOS」でした。劇中の電脳メガネのハードウェアとしての祖先となるようなデバイスは現在徐々に登場しつつありますが、MGOSの祖先となるようなOSについては、少なくともあまり聞いたことがありません。

このOS(あるいはそれに値するソフトウェア基盤)をどうやって作るか?どのようなものを作るか?といった議論があまりされていなかったという点が、おそらく感じた違和感の核なのだろうと思います。UI/UX基盤を作る、OSを作る、果ては生態系を作るということは、電脳メガネのハードウェアを作るのにも匹敵する壮大なプロジェクトで、一昼夜でできることでは無いだけに余計に。

メガネの時代をもたらすのは誰になるのか

個人的には、電脳メガネをはじめとしたウェアラブルコンピュータの時代の到来はそう遠くないと思っています。ですが、それをもたらすのが誰になるのか、というのは分かりません。
正確な文を覚えていないのですが、電脳メガネサミットの中では「日本発の電脳メガネを」といった趣旨の発言がありました。また、鯖江市長さんのブログエントリには、以下のようにあります。

微細な情報機器をめがねに組み込める技術力、優れた掛け心地を実現するノウハウを持つ産地は、世界で唯一ここ鯖江にしかありません。

世界に先駆けて「めがねの電脳化」に産地をあげて取り組むことは、「産地再生の一つの鍵」になります。

この記述だけを読むと、ソフトウェア・UI・OSといった部分が言及されていないようにも受け取れてしまうのですが、杞憂であることを祈っています。

勿論、自分でそれを作る試みに取り組んでもいない癖に偉そうに、と言われるとぐうの音も出ないのですが…orz

ひとまずは出来ることから、手持ちのMOVERIOで色々実験してみようかな、と調べているところです。

オヤジとデンスケ

ニコニコ学会で発表してみた

2012年4月28日、幕張メッセで開催されたニコニコ超会議内でのイベント、ニコニコ学会において「腱鞘炎の効率厨がPC環境を改善するとこうなる」というタイトルで発表を行ってきました(第3セッション「研究してみたマッドネス メカの部」)。ああいった場での発表は久しぶりだったので至らない点もありましたが、あの場に関わることが出来て良かったと思ってます。本当に面白かった!

なお、ここで発表した操作環境ですが、ControllerMateドライバソフトウェアの設定やポップンコントローラのボタン配置など細かく説明しきれていない部分も沢山あるので、いずれ書きたいなと思っています。僕は医者ではありませんし、腱鞘炎や関節炎は人によってダメージを受ける部位が異なるのでこの環境が誰にでもベストと言うわけではありませんが、似たような環境を作りたいという方がもしいたら、Twitterの@needleやブログコメントで連絡を頂ければと思います。

発表時の資料

実演時間を確保する為にカットしたスライドを含むバージョンはこちら

発表の様子(ニコニコ生放送タイムシフト動画)

http://live.nicovideo.jp/watch/lv89954863#03:43:00

応募時の動画

また、応募とは直接関係ありませんが、Twitter上での操作環境関連のTweetをまとめたTogetterまとめもあります。