増殖するUnitychan in AR

# 目的
Unity chan をつかってARでアプリケーションを作ってみよう！

続きは[こちら](https://qiita.com/KayaTakashiro/items/71910f8e0d49930f8f76)です

https://qiita.com/KayaTakashiro/items/71910f8e0d49930f8f76

## ARPlaneの代わりに通常のPlaneを使ってアプリをつくろう！

https://youtu.be/YdKlbr87Igw

##### AR FoundationではARのカメラが以下の写真のようになってしまいます。

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1353403/cded575a-1dc6-6785-d5b6-7bccfb794ce9.png)

でも、ARで使うのって　[ARPlane](https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@2.1/api/UnityEngine.XR.ARFoundation.ARPlane.html) と [ARRaycast](https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@4.2/manual/raycast-manager.html)なので、3D ObjectのplaneとPhysicsのRaycastで代用が可能！

なので、まずはAR Foundationを使わずにアプリケーションを作って、それをARに移植します。

## 流れ

1. オブジェクトを設定する
2. ちびキャラのユニティちゃんをクリックしたところに増殖させて表示する
3. ちびキャラのユニティちゃんをみんなまとめて移動させる
4. コライダーをつけて重ならないようにする
5. ちびキャラのユニティちゃんのアニメーションをつける
6. 音や物理マテリアルをつける

### 1.　オブジェクトを設定する。

まずはダミーのシーンを作って、Planeを写真のように設定します。

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1353403/b569dd12-99e0-3f02-d97f-0dfd8f06c717.png)

次に、Hierachyから空のオブジェクトを作成し, Controllerと名前を変更します。

### 2. ちびキャラのユニティちゃんをクリックしたところに増殖させて表示する

まず、ちびキャラのユニティちゃんについては[**SDユニティちゃん**](https://unity-chan.com/download/releaseNote.php?id=SDUnityChan)のものを使用させていただきました。ありがとうございます！

つぎにクリックしたところにユニティちゃんを置きます

```SetUnitychanDebug.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public class SetUnitychanDebug : MonoBehaviour
{   
    [SerializeField] GameObject UnityChan;

    public bool isUnitychan;
    
    public List<GameObject> UnityChanReals;

    public Vector3 hitPosition;

    // Start is called before the first frame update

    void Start(){
        UnityChanReals = new List<GameObject>();
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetMouseButtonDown(0)) {
            Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
            RaycastHit hit_info = new RaycastHit ();

            bool is_hit = Physics.Raycast(ray, out hit_info); 

            if (is_hit)
            {
                Debug.Log("Point : " + hit_info.point);
                Debug.Log("Create Unitychan!");
                isUnitychan = true;
                var unitychan = Instantiate(UnityChan, hit_info.point, hit_info.transform.rotation);
                UnityChanReals.Add(unitychan);
                hitPosition = hit_info.point;
            }   
        }
    }
}

```

どうせ、あとで増殖させるのでユニティちゃんをUnityChanRealsというリストに保存しています。

ポイントとしては,

1. Physics.Raycast(ray, out hit_info); でRayが当たったところの情報を保存していること。<br>
ARRaycastでは、ピンチ動作とかで複数の指を考える場合があるので、hit_infoのところがListになっていますが、どうせ1つのデータしか今回のアプリケーションでは使わないです。

2. Instantiateにおいて、hit_info.pointの場所でUnitychanを生成していること。hit_info.pointはUnity上でのworld座標でのColliderをもつオブジェクトとの交差点が返ってきます。<br>
2. hitPositionに交差点の情報を保存しています。これにより、あとでUnityちゃんの移動とかにつかいます。


これをControllerにアタッチして、InspectorのUnitychanのところにSD ユニティちゃんをアタッチします。

クリック後
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1353403/a2c99a0d-8a28-822f-c664-0f419d295769.png)

### 3. ちびキャラのユニティちゃんをみんな移動させる

つぎに、ユニティちゃんをみんなまとめて移動させます。

```UnitychanControlleDebug.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class UnitychanControllerDebug : MonoBehaviour
{

    [SerializeField] float gain = 100.0f;
    private SetUnitychanDebug setUnitychan;
    // Start is called before the first frame update

    private Vector3 preHitPosition;

    void Start(){
        setUnitychan = GetComponent<SetUnitychanDebug>();
    }

    // Update is called once per frame
    void Update()
    {
        if (setUnitychan.isUnitychan){
            if (Vector3.Distance(preHitPosition, setUnitychan.hitPosition) > 0.01f){
                Debug.Log("Destination Change!");
                for(var i = 0 ; i < setUnitychan.UnityChanReals.Count; i++){
                    setUnitychan.UnityChanReals[i].transform.LookAt(setUnitychan.hitPosition);
                }
                preHitPosition = setUnitychan.hitPosition;
            }
            for(var i = 0 ; i < setUnitychan.UnityChanReals.Count; i++){
                setUnitychan.UnityChanReals[i].transform.position = Vector3.MoveTowards(setUnitychan.UnityChanReals[i].transform.position, preHitPosition, Time.deltaTime * gain);
            }
        }
    }
}
```

画面をクリックすると、クリック前のhitPositionとクリック後のhitPositionの値が変化するので、それを利用してif文で分岐しています。

クリック時にはクリックした方向に全員のユニティちゃんが向くようになっています。

また、Vector3.MoveTowardsをつかって、クリックした点に向かっていくようにしています。

ちなみに、Vector3.SmoothDampを使うと面白い挙動をするので、よかったらやってみてください。

ただし、今のままでは写真のように重なってしまうので改良していきます。

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1353403/898b8675-1b12-f0cf-034e-391923cabf11.png)


### 4. コライダーをつけて重ならないようにする

いかのようにユニティちゃんにコライダーとRigidBodyを設置してください

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1353403/a744c24c-af89-e74e-be02-4c0ec3cbed4d.png)
![キャプチャ2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1353403/88dfb8c1-c980-fc80-9195-ab87ee7a157f.png)
 
これで重ならないようになります。

ちなみに、ユニティちゃんのルートに設置しないとコライダーだけが吹っ飛んで行ってしまいます...。

これにちょっと引っかかりました...。

### 5. ちびキャラのユニティちゃんのアニメーションをつける

次にちびキャラのアニメーションを編集します。

1. Projectから「SD_unitychan_motion_humanoid」と「SD_unitychan_motion_humanoid Import Settings」を検索して、Ctrl + Dで複製し、Sceneフォルダーに移動させてください。

2. 複製したSD_unitychan_motion_humanoid Import Settingsについて、Standing, Running, Walking以外のモーションをすべて削除します
2. 複製したSD_unitychan_motion_humanoid Import SettingsのWalkingモーションについて、ダブルクリックをして選択をした後で、Inspectorのしたの方へ行くと、Eventという項目があります。ここのEventに関して、すべて消去します
2. 複製したSD_unitychan_motion_humanoidについて、編集していきます。写真のようにStanding, Running, Walking以外をすべて消してください。また、parameterについてはNest, Backを消去しInt形で新たに **state**という parameterを追加してください<br>
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1353403/73d61983-fb5f-743e-0379-94f110da0ee3.png)
2. Standing, Running, Walkingの遷移については以下のように設定してください
![キャプチャ5.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1353403/df88ab0c-2553-d8ff-ae01-34a4217d0c9f.png)
![キャプチャ3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1353403/6193e0d9-adcd-3db5-6540-82637d3739d5.png)
![キャプチャ2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1353403/66c9427f-bae4-85ad-9bcb-ee8b21566a3b.png)
![キャプチャ1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1353403/a182a4eb-0407-5170-1487-e5c53040d15f.png)
6. SD_unitychan_motion_humanoid中の各ステートマシーンの中にあるAnimation ClipをSD_unitychan_motion_humanoid Import Settingsのものに変えてください
7. SD ユニティちゃんのPrefab中のアニメーターを複製したSD_unitychan_motion_humanoidに変えます。
8. 最後にSDユニティちゃんについて、Animator, IKLookAt, BoxCollider, RigidBody以外のコンポーネントをすべて削除すれば完了です。

あと、Scriptで増殖したユニティちゃんのAnimatorを管理するので、SetUnityChanDebug.csとUnityChanControllerDebug.csを以下のように書き換えます

```SetUnitychanDebug.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public class SetUnitychanDebug : MonoBehaviour
{   
    [SerializeField] GameObject UnityChan;

    public bool isUnitychan;
    
    public List<GameObject> UnityChanReals;

    public List<Animator> UnityChanAnimators;

    public Vector3 hitPosition;

    // Start is called before the first frame update

    void Start(){
        UnityChanReals = new List<GameObject>();
        UnityChanAnimators = new List<Animator>();
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetMouseButtonDown(0)) {
            Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
            RaycastHit hit_info = new RaycastHit ();

            bool is_hit = Physics.Raycast(ray, out hit_info); 

            if (is_hit)
            {
                Debug.Log("Point : " + hit_info.point);
                Debug.Log("Create Unitychan!");
                isUnitychan = true;
                var unitychan = Instantiate(UnityChan, hit_info.point, hit_info.transform.rotation);
                UnityChanReals.Add(unitychan);
                UnityChanAnimators.Add(unitychan.GetComponent<Animator>());
                hitPosition = hit_info.point;
            }   
        }
    }
}
```

```UnitychanControllerDebug.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class UnitychanControllerDebug : MonoBehaviour
{

    [SerializeField] float gain = 100.0f;
    private SetUnitychanDebug setUnitychan;
    // Start is called before the first frame update

    private Vector3 preHitPosition;

    private List<Animator> UnitychanAnimators;

    void Start(){
        setUnitychan = GetComponent<SetUnitychanDebug>();
    }

    // Update is called once per frame
    void Update()
    {
        if (setUnitychan.isUnitychan){
            if (Vector3.Distance(preHitPosition, setUnitychan.hitPosition) > 0.01f){
                Debug.Log("Destination Change!");
                for(var i = 0 ; i < setUnitychan.UnityChanReals.Count; i++){
                    setUnitychan.UnityChanReals[i].transform.LookAt(setUnitychan.hitPosition);
                }
                preHitPosition = setUnitychan.hitPosition;
            }
            for(var i = 0 ; i < setUnitychan.UnityChanReals.Count; i++){
                setUnitychan.UnityChanReals[i].transform.position = Vector3.MoveTowards(setUnitychan.UnityChanReals[i].transform.position, preHitPosition, Time.deltaTime * gain);
                var distance = Vector3.Distance(setUnitychan.UnityChanReals[i].transform.position, preHitPosition);
                var state = 0;
                if (distance > 1.0f){
                    state = 2;
                }else if (distance > 0.1f){
                    state = 1;
                }else{
                    state = 0;
                }
                Debug.Log("state : " + state + " distance : " + distance);
                setUnitychan.UnityChanAnimators[i].SetInteger("state", state);
            }
        }
    }
}
```

### 6. 音や物理マテリアルをつける

#### 音の設定

次にユニティちゃん同士が接触したとに音が鳴るようにします。[この記事](https://dianxnao.com/unity%EF%BC%9A%E3%81%B6%E3%81%A4%E3%81%8B%E3%81%A3%E3%81%9F%E6%99%82%E3%81%AB%E9%9F%B3%E3%82%92%E9%B3%B4%E3%82%89%E3%81%99/)を参考にさせていただきました。

1. CollisionSoundMotion.csを作成し、SDユニティちゃんにアタッチします

```CollistionSoundMotion.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CollistionSoundMotion : MonoBehaviour
{
    // ぶつかった時の音
    [SerializeField] AudioClip se;

    // ぶつかった時に音を鳴らす
    void OnCollisionEnter(Collision col)
    {
        AudioSource.PlayClipAtPoint(se, transform.position);
    }

}
```

2.Inspector中のCollistionSoundMotion.csのSEを設定します。<br>ちなみにSEには[https://otologic.jp/free/se/motion-pop05.html](https://otologic.jp/free/se/motion-pop05.html)の**ポップモーション42**を、BGMには[https://otologic.jp/free/bgm/pop-music01.html](https://otologic.jp/free/bgm/pop-music01.html)の**不思議ちゃんオーラ**を使用しています。
3.ControllerにAudioSourceを設定し、BGNのAudioClipをアタッチし、Loopを設定します。

#### PhysicsMaterialの設定

最後はまあ、なくてもいいのですがPhysicsMaterialを設定します。値を以下に設定して、SDユニティちゃんのコライダーに設置します。

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1353403/ca31332b-f90f-7c0d-0e65-6f6206f5423e.png)