Unityで『ニーア オートマタ』の義体システムっぽい機能を作る


『NieR:Automata』がおもしろい

PS4/Steamで絶賛発売中の『ニーア オートマタ』、大変面白いです。独特な世界観とケレン味、ゲームシステムとゲーム世界の境界線のあいまいさ、そして9Sちゃんかわいい。

このゲームに出てきたオンラインシステムが特徴的だったので、Unityを使った自作ゲームでも同じことができないかなと思って作ってみました。

特徴的な「義体システム」

『ニーア オートマタ』にはオンライン機能として「義体システム」というのがあります。
http://dengekionline.com/elem/000/001/471/1471850/

ゲーム中で主人公の2Bが死んでしまうと、その亡骸が「義体」としてマップ上に残ります。
再挑戦する際にその「義体」を回収することで失ったアイテムを再び入手できるのですが、マップには他のプレイヤーの義体も散らばっています。オンラインシステムで他のプレイヤーが死んだ位置を同期し、表示しているわけです。

他のプレイヤーの義体を調べると「義体レポート」として、その人が残したメッセージを閲覧することができます。
さらに、そのプレイヤーを仲間にして引き連れることができます。(仲間にせず、アイテム回収も可能)
他のプレイヤーとゆるくつながった特徴的なオンラインシステムといえるでしょう。

似たようなシステムをUnityで自作「してた」

昨年の夏(2016年8月)、こんなデモゲームを作りました。



動画:https://www.youtube.com/watch?v=VeVGiGeSvLY

スライド:https://speakerdeck.com/ncmb/unity-plus-ncmbdezuo-ru-kasutamusutezifalseonraingong-you-sisutemu

  • 見下ろし型アクションゲーム
  • プレイヤーは剣士
  • 敵にやられるとその場に墓ができる
  • 墓にメッセージと魔法効果を残せる
  • 墓はオンラインでほかのプレイヤーに共有される
  • 他のプレイヤーの墓を調べてメッセージを読み、魔法効果を得ることができる

...だいたい一緒やないかい!
というわけで、このデモの実装をベースに、もうちょっと『ニーア』を意識したものを作ってみます。
サーバー側実装には上記デモと同様、ニフティクラウド mobile backend(以下NCMB)を使います。

ニフティクラウド mobile backendとは

「ニフティクラウド mobile backend」(以下、NCMB)はmBaaSと呼ばれるジャンルのアプリ開発向けサービスです。
今回細かい説明は省きますが、Unity含むアプリ開発において

  • サーバーにデータを保存する
  • サーバーからデータを読み込む

これらの機能が 死ぬほど簡単に 実装できるサービスです。ゲームアプリ(フロントエンド)側実装の知識だけでオンラインシステムが組めます。iOS/Android/PC/Macで動作します、VRでも活用できそうです。
文字列などデータのほか、画像・音声などのファイル保存・読み込みもできます。

作成したデモゲームについて

とりあえずプリミティブな全方向シューティングを作りました。
青いやつが自機です。wasdか方向キーで移動、マウスで昇順、クリック推しっぱなしでショットです。
赤いやつが迫ってくるので撃って倒しましょう。体当たりされると即死です。

ソースと実行ファイル:https://github.com/NCMBMania/NAModoki
動画:https://youtu.be/irc9I4GZ-cs

『ニーア』のように、自分が死んだらその位置をオンラインに保存して、ほかの人が参照できるようにします。
「義体システム」まんまだとアレなので本デモゲームでは 「死んだフレンズ回収機能」 とし、他のプレイヤーをフレンドと呼称します。

初期設定

Unityを立ち上げ、プロジェクトにNCMB SDK for Unityを導入します。
詳しくはクイックスタートをご覧ください。アカウントをとって、APIキーをとって、指定のGameObjectを作って記入するだけです。
http://mb.cloud.nifty.com/doc/current/introduction/quickstart_unity.html

ソースを持ってきてエディター上で試す場合も、APIキーを自分で取得してシーン内に配置する必要があります。
このデモゲームでは、「Core.scene」にNCMBの通信関連などゲーム内でずっと常駐するクラスをまとめています。

このシーンはゲーム全体進行管理のスクリプトCore.csが配置されています。シーンCoreを開いた状態でエディター上のプレイボタンを押すと、タイトル画面がロードされます。

名前とメッセージを設定する

このデモゲームはタイトル画面で名前を入力します。空の場合は「UnKnown」になります。

次に、メッセージを入力します。『ニーア』と同じように三択式でメッセージを組み立てます。

名前とメッセージは、ひとまずCore.csに保存されます。

敵にやられて死ぬ

やられた際に、先ほど設定した自分の名前、メッセージ、そして死亡した位置をNCMBへ送信します。
コードは以下の通りです。

    public void SaveFriendDataToNCMB(Vector3 position)
    {
        NCMBObject ncmbObject = new NCMBObject("FriendData");

        // オブジェクトに値を設定//
        ncmbObject["Name"] = PlayerName;
        ncmbObject["Message"] = Message;
        ncmbObject["Position"] = position.ToDoubleArray();

        // データストアへの登録//
        ncmbObject.SaveAsync();
    }

ゲームの進行をつかさどるスクリプトから、SaveFriendDataToNCMB()を呼び出します。引数はプレイヤーが死亡したときの座標です。
あらかじめ保存してあったPlayerName, MessageとあわせてNCMBの「データストア」に保存します。
「データストア」に保存は、NCMBObjectクラスを作成し、値を設定、SaveAsyncを呼ぶことで実行されます。

NCMBの管理画面で、保存されたデータを確認することができます。

マップ内に死んだフレンズを配置する

続いて、保存したデータを読み込んでマップ上にオブジェクトとして配置します。

    public void FetchFriendDataFromNCMB(UnityAction<List<FriendData>> callback)
    {
        NCMBQuery<NCMBObject> query = new NCMBQuery<NCMBObject>("FriendData");

        query.OrderByDescending("createDate");
        query.Limit = 20;

        query.FindAsync((List<NCMBObject> childObjList, NCMBException error) =>
        {
            if (error != null)
            {
                //エラー処理
            }
            else
            {
                //成功時の処理
                List<FriendData> friendDataList = new List<FriendData>();

                foreach (NCMBObject obj in childObjList)
                {
                    string name = (string)obj["Name"];
                    string message = (string)obj["Message"];
                    ArrayList doubleArrayPosition = (ArrayList)obj["Position"];
                    Vector3 position = doubleArrayPosition.ToVector3();
                    friendDataList.Add(new FriendData(name, message, position));
                }

                callback(friendDataList);
            }
        });
    }

NCMBのデータストアから値を取得するために、クエリーを作成しています。上記のコード中の

query.OrderByDescending("createDate");

で日付で降順ソート、

query.Limit = 20;

で最大取得数20個まで、という条件を設けています。

通信結果はNCMBObjectのリストとして取得できますので、foreachやLinqで中身を取り出します。
今回はクラスFriendDataに名前、メッセージ、座標をまとめて持つようにしました。
この処理はタイムラグがあるため、コールバックとして次の処理を渡します。その後、取得したデータをもとにマップ上にゲームオブジェクトを生成します。

マップ上にはこんな感じで配置されます。これにプレイヤーが触れると...

名前とメッセージが画面上部に表示されます。
また、復活したフレンドは僚機としてプレイヤーに張り付くようになり、射撃量が倍になります。

フレンズは最大4体まで引き連れることができます。
他にも、仲間にするかアイテムにするか選ばせたり、時間で消えたりするとより実践的かなと思います。

最近はこの手のオンライン要素が流行りっぽい

『ニーア オートマタ』の義体システム以外にも、こうしたオンラインシステムを持つゲームはいろいろあります。

  • 『デモンズソウル』の幻影、血痕
  • 『仁王』の血刀塚
  • 『グラビティデイズ2』のトレジャーハンティング

総合すると、なんか痕跡を残して、同じマップの同じ位置に違うプレイヤーがそれを見ることができるシステムです。
というか、最初に紹介した剣士のデモも『デモンズソウル』のオマージュだったりします。すいません。

こういうシステムがあると「みんなで遊んでる感」が出るのと、ゲームにランダム要素が加わるので、変化があっていいですね。
もうちょっとするとモバイルゲームにも波及するかも?と思っています。

ぜひ皆さんもオンラインシステムを取り入れて、一味違うゲームプレイを作ってみて下さい。