聖夜にUnityでクソアプリを作る

# 初めに
皆さんこんばんは～！
クリスマスをいかがお過ごしでしょうか？
クリスマスの定番と言ったら何でしょうか？…そう**クソアプリ開発**ですよね！！
ということで今回は自分が作ったクリスマスにぴったりのクソアプリとその作り方について紹介していこうと思います

# 今回作るもの
今回作るもの（作ったもの）はこちらになります。
このアプリについて簡単に説明すると、スマホの画面をタッチした場所に某漫画の人気キャラクターが勇ましいBGMと共に３Dで登場するというものです。
聖夜に作るにふさわしいクソアプリですよね！

https://twitter.com/SAshunchan/status/1472873942298013697

Unityでapkファイルとしてこのアプリを作り、それをAndroid（バージョンは7以上ならOK）のスマホで動かしています。
もしこのアプリを自分のスマホで動かしたい方は自分のGitHubのリポジトリからダウンロードしてください

https://github.com/santooooooo/TryAR


# 作り方
このアプリは以下のサイトの情報をもとに作りました。

https://dev.classmethod.jp/articles/unity-ar-plane-detection/

https://book.mynavi.jp/manatee/detail/id=59718

https://www.sejuku.net/blog/83535

このアプリの作成手順は上記の記事（特に一番目の記事）で詳しく説明されているので、この記事では作成手順については扱いません。しかし、その代わりにこの記事ではこのアプリの動作（挙動）を担うスクリプトを詳しく見ていこうと思います。（スクリプトについて興味がない方は読み飛ばしてしまって大丈夫です！）

## アプリの動作（挙動）を担うスクリプト
そのスクリプトは以下の通りです。コメントアウト部分は解説用につけたものや元から書いてあるのを消し忘れたものなので、気にしなくてよいです。

```cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;           // .......(1)

public class PlaneDetection : MonoBehaviour
{
    ARRaycastManager raycastManager;
    [SerializeField] GameObject obj;
    AudioSource audioSource;                 
    [SerializeField] AudioClip sound1;                // .......(2)

    // Start is called before the first frame update
    private void Awake()                            // .......(3)
    {
        raycastManager = GetComponent<ARRaycastManager>();
        audioSource = GetComponent<AudioSource>();
        audioSource.clip = sound1;
    }

    // Update is called once per frame
    private void Update()                        // .......(4)
    {
        if(Input.touchCount == 0 || Input.GetTouch(0).phase != TouchPhase.Ended || obj == null)
        {
            return;
        }

        var hits = new List<ARRaycastHit>();
        if(raycastManager.Raycast(Input.GetTouch(0).position, hits, TrackableType.Planes))
        {
            var hitPose = hits[0].pose;
            Instantiate(obj , hitPose.position, hitPose.rotation);
            audioSource.Play();
        }
    }
}
```
ここからはコメントアウトとして付け加えた番号順でこのスクリプトの解説をしていきます。（このアプリの作成を通じて初めてC#を触れた身ですので、解説がガバいところがあります）

(1)まずは宣言されている名前空間の確認をします。

- `System.Collections`および`System.Collections.Generic`はC#においてコレクションの利用のために宣言されています。コレクションとは配列よりも縛りの緩い要素の集合体です。（自分が使用してきたPHPやJSだと要素の集合体にコレクションや配列という区別がなく、基本的には要素の集合体＝配列です）
- `UnityEngine`にはUnit内の要素の操作に必要なものが含まれていて、この名前空間内の`MonoBehaviour`クラスや`Input`クラスの利用のためには宣言必須のものとなっています。
- `UnityEngine.XR.ARFoundation`には3Dオブジェクトを表示する場所の検知するクラスなどが含まれており、ARを利用する上では必須のものとなっています。
- `UnityEngine.XR.ARSubsystems`には検知する場所の形状を表す定数などが含まれており、こちらもARを利用する上では必須のもの（？）となっています。

(2)次に継承するクラスや初めに宣言する変数について見ていきます。

- `PlaneDetection`クラスが継承しているのは`MonoBehaviour`クラスというものであり、このクラスを継承することで継承先のクラスはUnity内のオブジェクトのコンポーネントが参照できる`GetComponent`メソッドやUnityの世界内に新たなオブジェクトを配置する`Instantiate`メソッドが使えるようになります。
変数`raycastManager`は後程行われる`UnityEngine.XR.ARFoundation`内の`ARRaycastManager`クラスのインスタンスの代入のために宣言されます。
- 変数`obj`にはこのアプリで表示するオブジェクトが入ります。変数の前に`[SerializeField]`と書くことで、Unityのインスペクターウィンドウから表示したいオブジェクトを指定できます。
- 変数'audioSource'は後程行われる`UnityEngine`内の`AudioSource`クラスのインスタンスの代入のために宣言されます。
- 変数`sound1`には再生したい音声ファイルが入ります。これもUnityのインスペクターウィンドウから表示したいオブジェクトを指定できます。

(3)そして今度は`Awake`メソッドを見ていきます。`Awake`メソッドは`MonoBehaviour`クラスが所有するメソッドで、このスクリプトが実行される際に最も早く、そして一度だけ行われるメソッドです。

- `GetComponent`メソッドを使用し、変数`raycastManager`、`audioSource`にそれぞれGameObject内のコンポーネントである`AR Raycast Manager`、`Audio Source`が代入されます。ちなみに`GetComponent`の横にある`<>`は`ジェネリック`と呼ばれ、これによりこの中に書かれた型に対応したクラスを返してくれるそうです。（PHPにはこんな簡単に返り値の型を指定できる方法が（おそらく）ないので良いな～）
- `audioSource`の`clip`プロパティに先ほどセットした`sound1`を代入します。

(4)最後に`Update`メソッドを見ていきます。`Update`メソッドも`Awake`メソッドと同様に`MonoBehaviour`クラスが所有するメソッドで、このアプリの毎フレームごとに呼び出されます。公式サイトでは、[Not every MonoBehaviour script needs Update.](https://docs.unity3d.com/jp/current/ScriptReference/MonoBehaviour.Update.html)と書かれていたので、Unityの動作をつけるうえでは基本となるメソッドのようです。

- 最初のif文では、`Input`クラスを使用してアプリの使用者が画面のタッチを行る状態の場合かつ表示するオブジェクトが指定されている場合のみ次のステップへ進み、それ以外は何もしません。
- 変数`hits`に`System.Collections.Generic`内の`List`クラスによってコレクション化された[`ARRaycastHit`](https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@3.0/api/UnityEngine.XR.ARFoundation.ARRaycastHit.html)が代入されます。`ARRaycastHit`にはアプリ内のオブジェクトを表示する場所の位置情報などが書き込まれます。
- 二つ目のif文では`ARRaycastManager`クラスの[`Raycast`メソッド](https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@4.1/api/UnityEngine.XR.ARFoundation.ARRaycastManager.html)を使用し、アプリ内でオブジェクトが配置可能な場所に使用者がタッチした場合次のステップへ進み、そうでない場合は何もせずに次の`Update`メソッドへ進みます。`Raycast`メソッドについてもう少し詳しく言うと、第一引数`Input.GetTouch(0).position`が第三引数`TrackableType.Planes`において検知可能とされた場合に、その位置情報を第二引数の`hits`へ渡すという感じです。
- `Raycast`メソッドで渡された位置情報を変数`hitPose`へ代入し、第2、3引数にその情報による場所と表示する向き、そして第１引数に表示するオブジェクトがセットされた`Instantiate`メソッドが実行され、アプリ内にオブジェクトが表示されます。
- 一番最後に`AudioSource`クラスの`Play`メソッドが実行され、変数`sound1`としてセットした音声ファイルが鳴ります。


# 終わりに
初めてUnityやC#を触り、またWebアプリしか作ってこなかったにも関わらず簡単にAndroidで動くアプリを作れて感動しました。
今度はARマーカーを使ったアプリやWebで動くARアプリなんかも作れたらな～と考えています。