さくっとmocopiを使ったゲームを作ってみた

# はじめに

とりあえずこんな感じで奥から来る壁の穴を出来るだけ壁に触れずに抜けていく内容になります。

https://twitter.com/aodama/status/1616303220317650944

https://twitter.com/aodama/status/1616695735143256065

# 開発環境と必要なもの

* Unity 2022.2.2f1
* mocopi Receiver Plugin for Unity(v1.0.0β)
* mocopi
* mocopiアプリが動くスマホ（私はiPadPro(11インチ Gen2)とXperia 5でテストしました）

mocopi Receiver Plugin for Unityはここからダウンロード出来ます。
https://www.sony.net/Products/mocopi-dev/jp/downloads/DownloadInfo.html#Unity_Plugin

mocopiアプリ対応スマホはここに一覧があります。主にiPhoneとXperiaですね。
https://www.sony.jp/mocopi/#spec


まずmocopiアプリでスマホとセンサーのペアリング＆キャリブレーションを済ませてください。

# Unityプロジェクトの準備

新規プロジェクトを作りmocopi Receiver Plugin for Unityをインポートします。
私はURPを使いましたが、URPだとmocopi Receiver Plugin同梱の3DモデルがピンクになるのでLit等正常に描画出来るシェーダーに変更してください。

# mocopiとの接続テスト

MocopiReceiver/Samples/ReceiverSample/Scenes/ReceiverSampleシーンを開いてmocopiアプリと接続テストをしましょう。
ここで上手く動かないと進めても面白くないので、まずはセンサーの内容がちゃんとUnityまで届き、アバターに反映できることろまで確認してください。

**mocopiアプリの送信ボタンを押し忘れ（そもそも送信モードにする方法が初見殺し…）やWindows側のファイヤウォールの設定でデータ受信が出来ないことがあるので注意です**

# シーンの準備

ゲームはReceiverSampleシーンをベースにして作るので、ReceiverSampleシーンを開いている状態で、シーンを別名で保存してください。
この別名で保存したシーン上にゲームを構築していきます。

# アバターに当り判定を設定する

MocopiAvatarをそのまま使いますが、当り判定が入っていないのでモデル内に当り判定用のColliderを設定します。
手動で入れてもいいですが、SAColliderBuilderを使えば一瞬で終わります

SAColliderBuilder
https://assetstore.unity.com/packages/tools/sacolliderbuilder-15058

MocopiAvatarにSABoneColliderBuilderコンポーネントを追加します。
当り判定はトリガー判定で欲しいのでIs Triggerをチェックします。

Processボタンを押せばいい感じにColliderが追加されます。位置やサイズを調整したいならInspectorからSABoneColliderBuilderのパラメータを調整して再度Processボタンを押すか、追加されたColliderを手動で調整しましょう。
最後にMocopiAvatar配下のGameObjectのレイヤーをPlayerに設定してください。
![アバターの設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227733/a464be49-9d03-a4a5-42cd-a3ef16d26201.png)

# 床を作る

見た目だけの床を置きます（当り判定とかには使わない）
私はStandard Assets/PrototypingのFloorPrototypeとPlatformPrototypeを組み合わせて作りましたが、でっかいCubeでもPlaneでも何でも構いません。もちろん凝ったステキなモデルでもいいと思います！
![床.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227733/e559173a-a45b-3344-eba2-e4beca025b6c.png)

# 壁を作る

とにかく沢山のCubeを並べます。手動で並べてもいいですが、Asset Storeで便利アセットを使うなり、n*m個のCubeを生成＆配置するエディタ拡張を作ってもいいと思います。

並べるCubeの数は多くすればCPUパワーを食い、少ないと当り判定が大雑把になってしまうので環境や用途に合わせて調整してください。
私は10cmのCube縦に26個、横に21個の合計546個のCubeを1つの壁として扱うようにしました。
![壁.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227733/767b3938-fbf8-0561-8e9f-bd62819efd3b.png)

各CubeにはRigidbodyとBox ColliderをAdd Componentし、
Box Colliderは
Is Triggerをtrue
Rigidbodyは
Is KinematicをTrue
にしています

また、各Cubeには以下を記述したスクリプトを作りAdd Componentしてください。

```cs
private void OnTriggerEnter(Collider other)
{
    var layer = other.gameObject.layer;
    if (layer == LayerMask.NameToLayer("Player"))
    {
        rigidbody.isKinematic = false;
    }
}
```

このスクリプトを追加することで、Playerレイヤー（つまりアバター）と接触したCubeだけが物理運動で落下するようになります。
ただ落下するだけだと地味なので、接触したらパーティクルでエフェクトを入れたり、RigidbodyのAddForceメソッドで飛ばしてもいいと思います。

**注意点としてはCubeの並びは規則的（例えば左下を原点に横優先に並べる等）にしておかないと、後で穴をあける（マスクを設定する）ときに苦労します**

# 壁の生成～移動～破棄

壁は画面奥の方にInstantiateして画面手前に移動させ、アバターと接触後画面外まで移動したら破棄します。
![壁の生成移動破棄.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227733/1525388e-fe11-a247-ab69-27c23e98f961.gif)

この壁の

生成→移動→（アバターとの接触）→破棄

の繰り返しがゲームのメイン部分になります。
一度生成した壁はキャッシュして再利用する等のUnityの基本的なところは実装しておくとベターです。
※再利用する場合は接触して落ちたCubeをちゃんと再初期化してください

ゲームの終了条件は
* 一定数壁に接触したらゲームオーバー
* 要したマスクを全て通過したら終了

など好みで判定してください。

# マスクデータの作成

ゲームルールは壁に当たらないようにすり抜けることなので、事前に壁に穴を開けておく必要があります。
やり方は色々ありますが、穴をあける処理自体は各CubeをSetActive(false)にして非表示にするだけなので、例えば配列でマスクパターンを用意して壁の生成時にマスクパターンに沿って各CubeをSetActive()します。この際、マスクパターンとCubeの並び順は合わせてください。

```cs
var mask = new bool[WallHeight, WallWidth];
```

この配列自体の中身はjsonで読み込むなり、ビットマップから生成するなり好きな方法でいいと思います。
私はマスクデータ作成用のREC MODEを用意してアバターのあけた穴をマスクデータとして記録してファイルに保存し、ゲーム時はファイルのマスクデータを読み込んで生成した壁に反映するようにしています。

https://twitter.com/aodama/status/1616305078042001410

A-Z文字版は文字の3DモデルにMesh Colliderを入れてアバターの代わりに壁に接触させてマスクデータを作りました。
![文字マスク.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227733/f649c34c-86c2-54c8-d99b-a6a10334fd9b.png)

# スコア等

壁が画面外に移動した時、全Cubeの中でRigidbodyのIs Kinematic == falseのCubeが接触したCube数になります。
※実際の実装はCubeに追加したスクリプトでIsHitフィールドを用意して計算しています

# 拡張

ここまでの実装で最低限ゲームとして遊べるようになっていると思いますが、他にも

* 接触すると加点になるブロックを入れる
* マスクエディタを入れる
* アニメーションするマスクを入れる

などを入れることでより面白くなるかもしれません。

# 最後に

かなりザックリした説明になりましたが、アバターに当り判定を入れることで色々なゲームに使えるようになると思います。

mocopiはセンサーが6個しかないため人の動きを正確に記録する用途にはあまり向かないと思います。ただそれを補うだけのすぐに使える手軽さが最大のアドバンテージだと思います。