学部２年生のVRゲーム開発日誌(11/3更新)

#はじめに
本記事はタイトル通り理系大学の情報学科に通う学部二年の私が大学の学祭で展示するVRゲームを開発したときの(8月下旬から10月末までの)日誌になっております。
完成してからこの記事を書いていますので、ところどころ何を考えていたのか覚えていないところもありますがそこはご了承ください。

さて、タグにもある通りHTCViveとUnityを使用してVRゲームを開発したわけですが、残念ながら時間が足りず、肝心の体を動かした入力や、コントローラーのリアルな振動など、VRゲームの魅力と言っても差し支えない部分については私は何も開発していないです...
UnityのAssetStoreで公開されているSteamVRのLongbowのモデルをそのまま利用して、ゲームのルールや一通りの流れを作っただけです。
そのため、VRの入力のとり方わかんねぇって人や、すでにVRコントローラーを使ったゲームを自作できる人向けの記事ではないと思います、ごめんなさい。
一応勉強はしたんですけどね、使いこなせなかったです。
その勉強の際に使用したURLは下記しておきます(全部英語ですけど頑張って翻訳してください。GIFとかでも説明してくれるので読めなくても多分なんとかなります。)
Viveコントローラーの入力のとり方や、ものを掴んで投げる、ワープする、くらいまではここのサイトの知識で十分できるようになります。
[HTCVive Tutorial for Unity](https://www.raywenderlich.com/9189-htc-vive-tutorial-for-unity)

というわけで、技術の指南というよりは、開発時にここで詰まって、どのように解決したかや、実装方法の試行錯誤などを感じてもらえればと思います。

また、最終的に使った方法を見出しの後ろにつけておこうと思うので、そちらの技術に関しては少しお話できるかもしれません。

#開発に至ったきっかけ
元も子もないことを言ってしまうと予算が下りないからです()
私は大学公認のサークルに加入しており、主な活動内容は学祭での自作ゲーム展示に向けてゲーム制作をすることです。
そして、今年はVRゲーム用の機材を大学の予算で買ったため、作らないと翌年の予算が大幅減額されてしまうのです。~~(予算8000円の悪夢)~~
まあ、流石にこれは冗談として、せっかく部にVR機器があるのだから、開発しないほうが損じゃないかということで自分から志願しました。

最初は流鏑馬作ってみたいなぁで開発していたのですが、馬のモデルやアニメーション制作が難しいし、何より酔いそうだったので、トロッコにのって流鏑馬やる感じのゲームになりました。
それでもテストプレイした人たちからは酔いそうと言われたのでだめだったみたいですが。

#開発環境
Unity 2019.1.1f1
HTC Vive 77H02568-15M Rev.B

#どんなゲームにするか考える
時期としては8月下旬から9月初旬にかけて。
このあたりはVR機器のinput周りを勉強してました。色々迷ったものの結局流鏑馬に決めて開発を始めることにしました。(VRマ○オカートも考えたけど入力を取るのが難しすぎて断念)

SteamVRのLongbowと的を利用するのが良さそう(基本的にValve社は自作宣言しなければ第三者の使用に寛容っぽい)となったので、流鏑馬に必要な部分はほぼここで完結。(参考：[Valve Third Party Legal Notice](https://github.com/SteamDatabase/SteamTracking/blob/master/ClientExtracted/ThirdPartyLegalNotices.html))

あとはValve社の作った処理にちょっと手を加えれば完成だな！とこの時は思っていました...

#SendMessageを使わないように(ExecuteEvent.Execute)
時期としては9月中旬から10月中旬まで。
的に当てて得点を手に入れていき、ハイスコアで競う形のゲームにしようと思っていました。
矢を放って、的にあたったらひっつくという処理を既にValve社が作ってくれています。
(詳しい実装はSteamVR/InteractionSystem/Longbow/Scriptsにあるarrow.cs(矢)とArcheryTarget(的)を見てください)

実装をすごい雑に説明すると、矢を放つために矢のモデルをInstantiateして、打つ。
矢の速度が一定以上で当たった物体のmaterialが刺さってもよいmaterialなら矢がひっつくという処理をしていました。

新たに追加したい機能として
１，矢を放った回数をカウントする(命中率を計算するため)
２，矢が当たった位置と的の中心位置から距離を求めて、距離に応じた点数を加算する
の二つがあったので、自分の作ったクラス内にこれらを計算する機能をつけて終わりだと思ったのですが...

なぜか矢の方(Arrow.cs)から自分の作ったクラス(今回はGamemaster.csという名前)が見つかりません。
ファイル構成的には
Asset--myfolder--Gamemaster.cs
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;--SteamVR--InteractionSytstem--Longbow--Scripts--Arrow.cs

雑に記述するとこんな感じだったのですが、Gamemasterのインスタンスを生成しようとしてもクラスを認識していないみたいで、できませんでした。
色々探したところUnity自体のコンパイル順が関係しているのではないかということはわかりました。(参考：[Unity公式](https://docs.unity3d.com/ja/2018.1/Manual/ScriptCompileOrderFolders.html))

まあ当たり前ですがArrow.csの隣にGamemaster.csを置けば認識してくれたので、それで解決はできるのですが、自作のクラスが散らばるとあとから見にくいことこの上ない...ということで、SendMessageなるものを使ってゴリ押しで解決しました。
これはgameobjectにくっついてるコンポーネントの関数の中から引数で受け取ったString型と同じ名前の関数を実行するという機能です。

まあ機能の説明聞いただけで良くない部分がわかりますよね。
gameobjectに本当にその名前の関数があるかもわからないし、渡せる引数も上限があります。
更には関数名をString型で渡すので補完が効かず、タイプミスもしやすい。
関数呼び出しを検索することもできません。

さて、ここで導入したのがExecuteEvents.Executeです。
これはSendMessageの改良版で、文字列をキーとするのではなく、インターフェースをキーにして関数を実行してくれます。
何気にC#でインターフェースを使ったのが初めてだったので(機能として知ってはいましたが)、そこには少し詰まりました。
実装の際にはこの方のブログを大いに参考にさせていただきました。この場で感謝申し上げます。
http://tsubakit1.hateblo.jp/entry/2015/04/13/010645


自作クラスが認識されない問題さえ解決すれば、あとは簡単です。
矢が的にひっつくときにはPhysics.raycastを利用してgameobjectにひっつけるかどうかを判定していたので、それを利用して的の命中位置を出しました。
矢の中心位置が矢羽根の位置になっていたせいで、的の中心との距離が変なことになっていたのには気がつくのに1日費やしました...

あとは矢をリリースする処理をしている関数内に、同じように撃った矢の本数を1足す関数を実装することで、もう半分くらいゲームはできたと言えるでしょう。
この時点で的に当てると点数が増えて、的の中心に近いほうが点数が高い、そしてスコアの合計を出せるくらいならできました。あとは自機を動かせば完成！

#ルートに沿って自機を移動させる(NavmeshAgent)
やっていた時期は10月中旬ごろ。
一年生のゲーム制作の際にも使っていて、まだ覚えていたのでこれを使用。
慣れもあって一週間足らずで実装できました。
Animationとも迷ったのですが、細かい調整が効きにくそうという理由でNavMeshAgentを活用することにしました。(今更ながら試すだけならやってみるべきだったとも思います)
こいつにターゲットの位置を与えてあげることで、NavMeshAgentをアタッチしたオブジェクトがターゲットに向かって移動してくれます。
([参考：公式リファレンス](https://docs.unity3d.com/ja/2018.2/ScriptReference/AI.NavMeshAgent.html))
これを応用して、進路上にターゲットを並べて、追跡する対象を次々変えていくことで指定した順番で、順路を決めて動かすことができます。
NavMeshAgent.speedを変更してあげれば移動速度の変更もできるので、一周させる速度の調整なども簡単。
まあ追跡対象を並べていくのが正直めんどくさいのと、曲がるときに結構急カーブになってしまったので、そこは改良の余地ありですかね。
ここに関してはもっと良い方法があるだろうと思っているので、なにかいい方法があればコメントでお願いします。

#的を動かす(Animation)
10月中旬ごろに実装。
ただ移動して動かない的を撃つだけではつまらないと思ったので、動く的を作ろうと思ったのですが、上下と左右に移動するだけくらいにしておいたほうがいいかなと(難しすぎなくていいかな)と思ったので、すぐできるAnimationを使って動かしました。
的のTransform.positionをただキーフレーム打って指定してあげるだけの、公式リファレンスでやっていることくらいしかやっていないので特に解説することがないです...
(公式：https://docs.unity3d.com/ja/2017.4/Manual/AnimationEditorGuide.html)

#カウントダウン・リザルト表示の実装(Timeline)
10月下旬。最後の壁になった実装でした。
初期位置で弓を持つ→的を撃つとカウントダウンが始まる→ゲームスタート→一周する間的を撃って得点を稼ぐ→ゴール→リザルト表示
といった流れのゲームにしようと決まったのですが、カウントダウンはフレームをまたぐ処理だ→コルーチン使うか、という流れでひとまず作ってみたのですが、カウントダウンのSEが無限に再生されてしまう状態を解消できず...
その時期にTimelineなるものの存在を知ったために、最終的にはTimelineで実装しました。
とりあえず公式の情報としては下記の感じ。
https://docs.unity3d.com/ja/2017.4/Manual/TimelineSection.html

名前の通りタイムラインに沿ってオブジェクトの表示・非表示やアニメーションの再生、挙句の果てにはスクリプトの実行までできます。どういう動きをするかのテスト再生もできるので、すごいわかりやすかったです。
このあたりは映像編集をやったことがある人にはわかりやすいUIだと思いました。
ただし、スクリプトの実行にはExposedReferenceというのが出てきて、実装には苦労しました。
実装の際には下記の方のお世話になりました。
https://bardaxel.jp/archives/643

正直作るだけならできるようになりましたが、おまじない感が否めない...

カウントダウンはテキストを弄るスクリプトを実行させながら、SEを流すことで実装。
カウントが０になったときにゲームスタートの処理をするようにもしました。
![カウントダウンタイムライン.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373452/ecf50fd5-e124-c5cb-2ec6-72c0f23b53a3.png)


リザルト画面の表示はカウントダウンよりは複雑で、
終了のブザーを鳴らす→パネル・矢を撃った回数・的にヒットした回数・命中率・命中率によるボーナス倍率・ボーナスを加味した最終得点
の順番で表示しています。
ランキングも実装したため、スコアが表示されると同時にランキングに表示されるようにしました。
![ランキングタイムライン.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373452/94ff32b8-31fc-929f-a751-a35d808be127.png)

#最後に
~~ここまで読んでいただいた方にはどんなゲームになったか見ていただきたいのですが、画面録画できる環境がなく、スクリーンショットもうまくいかなかったので、写真はプロジェクト中のものだけですが公開します...申し訳ないです。
録画やスクショが取れるようになったら追加しておこうと思います。~~

(11/3追記)
youtubeにてテストプレイ動画を公開しました。
ただ画面録画しただけですが、どんな雰囲気のゲームになったかはわかるかと思います。
https://www.youtube.com/watch?v=Wbs4zibV_dc&feature=youtu.be
(追記終わり)

このゲームは2019年の11/2(土)～4(月)まで、中央大学　後楽園キャンパス6418教室にて展示しておりますので、興味があればお越しいただけると幸いです。
一年生が制作したゲームの展示・配布も行っていますので、そちらもよければどうぞ。