NoCode × Unity ～コードを1行も書かずにRunゲームを作る～

こんにちは！元 Life is Tech! メンターのジーニーです！
Life is Tech! では Unity、Maya、AfterEffects などを教えていました。

## 注意！
本記事は NoCode でどの程度のゲームを作れるかを実験した **ネタ記事** です。
Unity でゲームを作る場合、通常少なからずコードを書いたり読んだりする必要があります。

もしどうしてもコードを書きたくない！という場合は Bolt などのビジュアルスクリプティングツールを使いましょう。
(いつの間にか Bolt 無料化してたんですね、知らなかった、、、)
https://assetstore.unity.com/packages/tools/visual-scripting/bolt-163802

## はじめに
プログラミング未経験者が **Unity なら3Dゲームを簡単に作れる！**と思って手をつけたものの、案外初歩の段階でコードが出てきて、**結局それなりにコード書かないといけないじゃん！**ってなって挫折…、結構あるあるだと思っています。
**まして時代は** **NoCode。**ならば意地でもコードを書かずにゲーム作ってやる！！

…ということで、今回の記事では一切コードを書かずにゲームを作ってみました。

## ルール
ゲームを作る前にルールを決めます。今回は以下の**3つの縛り**で作りました。

- **コードは一切書かない**
- **外部 Asset は一切使わない**
- **使用可能パッケージは Unity UI のみ**

ホントのこと言うと3つ目ちょっとセコいのですが、これがないと流石に厳しいし、最初から入っているやつなのでお許しを、、、

参考情報として、諸々のバージョンは以下の通りです。

- Unity: 2019.4.0f1
- Unity UI: Version 1.0.0

## できたもの
今回は Run ゲームを作りました。落下せずにゴールに向かうシンプルなものです。
操作は左右移動とジャンプのみ。

できたものを貼っておきます。
https://unityroom.com/games/nocode_rungame
![1fc1663aa1164fe17cc5ed40d8853a67.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/925774/de6f4c2e-3749-9b53-54f8-4406722a2a67.gif)
クオリティはお察しですが、一応遊べるものにはなっています。

使ったアセットはたったこれだけ。C# スクリプトは勿論ゼロ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/925774/a4962718-6940-8477-b394-9604c8403bc5.png)
WebGLの書き出しファイルの合計サイズはたった **3.66MB**！ [^※1]
ビルドもびっくりするほど高速でした。

## 実装方針
今回は下記の実装方針を取りました。

- ゲーム中の「動き」はすべて Animation で表現する
- ゲームパッドは Unity UI の機能で作る

ゲーム中のアニメーションは

- 左右の移動、ジャンプなど**プレイヤーの操作に関わる部分はプレイヤー自身を動かす**
- 前進やステージギミックなど**プレイヤーの操作に関わらず動く部分はステージを動かす**

としました。このほうがアニメーションの動作チェックなどやりやすいかなと、なんとなく思ったからです。
(このようにしたことの弊害が後々出てくるのですが…)

## プレイヤーを作る
プレイヤーオブジェクトの設定は下の通りです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/925774/47b237f3-f2f9-37c7-b84c-3d52a57db8c3.png)
ポイントは、

- Animator の Apply Root Motion にチェックを入れる
- Rigidbody の Drag、Angular Drag は 0 にする
- Rigidbody の Use Gravity にチェックを入れる
- Rigidbody の Constraints の Freeze Rotation はすべてチェックを入れる

くらいでしょうか。
Apply Root Motion にチェックを入れると、**現在いる位置から相対的にアニメーションするようになります。**

プレイヤーのアニメーション遷移は下の通りです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/925774/0f4aeb36-5dab-0970-4f88-ffe3458bd138.png)
名前の通りですが、Wait が入力待ち(= 何もアニメーションしない)、Right が右移動、Left が左移動、Jump がジャンプです。
図の左を見ると分かるように Right、Left、Jump の3つのトリガーが定義されており、この3つのトリガーを使って状態を遷移させます。 

## ゲームパッドを作る
ゲームパッドの UI はプレイヤーの子オブジェクトとして配置します。
**Canvas の Render Mode を World Space にしておき、Event Camera は忘れずにメインカメラを指定します。**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/925774/73fafa6e-d322-b588-876c-2b6a5ee3f35a.png)
例えば右ボタンは以下のようになっています。
各ボタンの役割をする **Image** には **Event** **Trigger** **コンポーネント**をアタッチし、クリック時 (Pointer Click) のイベントとして、Player オブジェクトの **Animator.SetTrigger** を指定します。
**引数として Right、Left、Jump と入れてあげれば、ボタンをクリックした際に先程定義した3つのトリガーを操作できます。**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/925774/0ec13c36-6d47-6c4d-64f8-32e73fa1c1f2.png)
なお、**Event Trigger を使う場合は、必ず Image の Raycast Target にチェックを入れましょう。**これが入っていないとポインタをボタンの上に持っていっても反応してくれません。
逆に、**テキストなど当たり判定が必要のない UI はRaycast Target のチェックを外しておくのが無難です。**

## ステージを作る
ステージは空オブジェクトを作り、その子オブジェクトとして複数の Plane を置いています。
ゴールに置いたゲートに文字を入れたかったので Canvas も入れています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/925774/565a8c3e-0c9d-8cf2-de8e-c3d1d9ecc2d8.png)
空オブジェクトにアニメーションを付けると子の Plane もその動きに追従します。
**ステージはゲームオーバー時などに位置をリセットする必要がある(= 相対的な移動をするべきでない)ので、Apply Root Motion のチェックは外しておきます。**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/925774/d1c3b897-dcab-a8b1-dc9f-ebedc61e076d.png)
ステージ移動のアニメーションは絶対座標で指定し、等速で手前方向に動くようにします。
DopeSheet で編集すると速度変化が緩やかなカーブを描いていることがあるので、Curves を編集し下図のように直線になるようにします。これでステージが等速で移動するようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/925774/9b1fb155-178b-2eea-cb67-c483d09e24e5.png)

## その他困ったこと
上記の内容で大体のことは説明できたのですが、実装中に困ったことが2点ありました。
#### プレイヤーどこに落ちるか分からん問題
プレイヤーはミスした際に落下します。
普段なら、ある高さより下に来たらスタート位置に戻したり、ゲームオーバー画面に遷移させたりするわけですが、**スクリプトが書けないのでそんなことできません。**なので、**落下した先にゲームオーバー画面 (らしきもの) を用意してあげる必要があります。**

ただ、最初に決めた仕様で**プレイヤーは左右に好き勝手移動します。**
どこに落ちるかをあらかじめ知ることはできないのです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/925774/03346bc9-7f73-f8c2-44de-ec7efad3d2d4.png)
最初、上のように愚直にゲームオーバー画面と床を置いたものの、これだと落下位置によっては...
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/925774/0d014d48-bc43-9fe3-6454-8620499f08e8.png)
こんな感じになってしまいます。~~なんだよmeOverって...。~~

プレイヤーを動かすのをあきらめ、左右移動も含めアニメーションは全部ステージに押し付けるべきか…。
ぶっちゃけそれでも良かったんですが、**最初に決めた仕様を捻じ曲げたくなかったので、**今回は以下のように解決しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/925774/bb2fedea-ccbc-f77c-112b-17a677c1a549.png)
とりあえず沢山書いとけばどこに落ちても文字が読める！~~お菓子の個包装かよ。~~
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/925774/71b16b5f-dc1a-9968-eb62-5c6da9d2379c.png)
ちなみに全体はこんな感じになっているのですが、**Rect Mask 2D** コンポーネントをアタッチした Image の子に GameOver の文字を敷き詰めた Text を置くと作れます。

#### プレイヤーを初期位置に戻すの大変問題
プレイヤーにアニメーションさせるのをいい加減あきらめろという話ですが、ゲームオーバー画面からプレイヤーを初期位置に戻す方法も悩みました。
プレイヤーは Apply Root Motion にチェックを入れてる影響で、特定座標にワープ！みたいなことがやりづらく、、、。
そこで、この問題は物理的解決策をとることにしました。
![bba3bb71c06e07741f784383c9af134f.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/925774/a687654f-8638-b81f-dd68-94c6902678b6.gif)
絵的に多少面白くなった気がするので、これで良しとしましょう。
~~この動き、どこかで見たことあると思ったら、ボーリングのピン立て直すやつや。。。~~

## まとめ
ネタ記事とは言ったものの、コード無しでも意外とゲームらしきものは作れることが分かりました。
今回の応用で少なくともノベルゲームやシューティングゲームくらいなら作れそうな気はしています。

[^※1]: それでも容量大きくない？という声が聞こえてきそうですが、どれだけ削ってもある程度エンジンコードが残るので、ほぼほぼ限界値です、、、