Unity超初心者が引き出し開閉アクションをつくるその2

#はじめに
こちらの勉強メモの内容のつづきです
https://qiita.com/okapy0922/items/7f2de09d0f2d8f4c1c60

今回もこちらのチュートリアルを参考にざっくり手順を残したいと思います。
https://www.youtube.com/watch?v=a5WXiMN3APk

引き出しに触れた状態で引き出しを開閉するアクションと、
開けた引き出しの中に球体が入っている状態を再現しました。

#再現したもの
![20200517_162037.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234065/d9bfd4c6-d3ca-a2c4-cf9c-1f80afb4bd98.gif)

Raycastというみえないレーザー状の光線をとばして、引き出しのコライダに接触していたら開閉を行います。

#スクリプト追加
FPSControllerに新しくAdd Componentでスクリプトを追加します。

```c#:Interactive.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//gameObjectにカーソルが接触していたらRaycastで触れ、開閉を行う

public class Interactive : MonoBehaviour{
    // Float型 Rayの有効範囲を値で入力
    [SerializeField] private float interactRange;

    //InteractiveObject.csを呼び出すため
    private InteractiveObject interactiveObject;

    /* カメラ型の変数cam 
    (FPSコントローラのインスペクター内を確認する、
    メインカメラとしてタグ付けされたCameraコンポーネントがある場合にRaycastが機能するようにしている)
    メインカメラがタグ付けされているオブジェクトはシーン内で1つだけにすること*/
    private Camera cam;

    // Raycast型の変数hit
    private RaycastHit hit;

    void Start(){
        // シーン開始時にメインカメラのCameraコンポーネントを変数camに取得する
        cam = Camera.main;
    }

    // Eキーを押した瞬間にRaycastを実行する
    void Update(){
        if (Input.GetKeyDown(KeyCode.E)) {
            /* Raycastの判定
             レーザービームが伸びた先までに何かにぶつかるものがあるかどうか、
             Rayの有効距離はメインカメラ(Player)の座標位置からinteractRangeに格納されている位置情報の値まで*/ 
            Physics.Raycast(cam.transform.position, cam.transform.forward, out hit, interactRange);
            // Raycastがゲームオブジェクトにhitしたら
            if (hit.transform) {
                // InteractiveObject.cs内の開閉処理をよびだす
                interactiveObject = hit.transform.GetComponent<InteractiveObject>();
            }else{
            // Raycastで触れていないときは開閉処理しない、nullを指定する
            //（これがないと引き出しに触れ開閉した後、触れてない状態でも引き出しの開閉ができてしまう）
                interactiveObject = null;
            }
            // nullであるかどうか判定処理(NullRefarenceExeptionを回避)
            if (interactiveObject) {
            // InteractiveObject.csのPerformAtionメソッドを呼び出す
            interactiveObject.PerformAction();
            }
        }
    }
}
```

#レティクル(照準器)追加

Unity画面の左上[Gameobject]-[UI]-[Image]を押下後、
インスペクター内の[Image(Script)]-[Source Image]の右側の丸を選択し、
レティクルのデザインを選びます。[GUIReticle]がちょうどよさげだったので
これを使いました。
ゲーム画面を見ながら照準器が真ん中にくるように置いてあげます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234065/c3cbede0-a217-eb94-afb4-e9b38a172cef.png)

#オーディオソース追加
こちらから引き出しの開閉音をダウンロードして使用しました。
フリー素材の効果音をダウンロードできます、種類が豊富です。
http://soundbible.com/
ダウンロードしたファイルは保存先のフォルダからUnity上のAseets内に適宜保存先を用意して
ドラッグドロップします。
[AudioClip]の丸を押下後、保存したオーディオファイルを選択し、
下側にある[play on Awake]のチェックボックスは外します。
（チェックを入れてるとゲーム起動後と同時に設定した効果音が鳴ります）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234065/ee69073d-38f8-06c4-8d3b-0aa329a8e150.png)


#スクリプト編集
前回追加したInteractiveObject.csを編集します。

```c#:InteractiveObject.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class InteractiveObject : MonoBehaviour{
    // Vector3型 引き出しが開いた時(openPositon)と閉じたとき(closePosition)の座標位置をインスペクタ表示、数値入力で設定
    [SerializeField] private Vector3 openPositon, closedPosition;

    // Float型 引き出しの開閉スピードをインスペクタ表示、数値入力で設定
    [SerializeField] private float animationTime;

    /* ブーリアン型 開いているか閉じているかを「isOpen」(真偽)の値で調べている
     (これがないとEキーで開いたとき、引き出しが閉まらなくなる)
     ブール値はデフォルトの状態でfalseにする*/
    [SerializeField] private bool isOpen = false;

    // God_P.234 ハッシュテーブル：キーと値の組み合わせの情報で、
    // キーにより値を出し入れすることができる連想配列と呼ばれる配列の一種のこと
    // (これがないと引き出しを開けたとき空間に飛び出たままになった)
    private Hashtable iTweenArgs;

    // audioSource作成(オーディオクリップを再生するための変数)
    private AudioSource audioSource;

    void Start(){
        /* P.234 ハッシュテーブル作成_渡す引数（arguments）は2個ペア
        Property NameとTypeをカンマ区切りで追加する*/
        iTweenArgs = iTween.Hash();
        // GameObjectがアニメーション化する空間内のポイント。
        iTweenArgs.Add("position", openPositon);
        // アニメーションが完了するまでの秒数
        iTweenArgs.Add("time", animationTime);
        // 「islocal」ワールド空間でアニメ化するか、親(引き出し)を基準にしてアニメ化するか。
        // （デフォルト値はfalseでtrueにすることで親オブジェクトを基準としている）
        iTweenArgs.Add("islocal", true);

        // オーディオソースのコンポーネント取得(GetComponentでAudioSourceを取得)
        audioSource = GetComponent<AudioSource>();
    }

    // 他のスクリプトからアクセスが可能となるようにpublic宣言
    public void PerformAction(){
        // 引き出しが開閉されたら開閉音が再生される
        if (audioSource) {
            // 与えたオーディオクリップを取得して自動的に再生
            audioSource.Play();
        }
        // （isOpenの値が開いているか閉じているかの状態を保持している）
        if (isOpen){
        // 引き出しが開いていたら閉じる
       iTweenArgs["position"] = closedPosition;
       }else{
       // 引き出しが閉じていたら開く
       iTweenArgs["position"] = openPositon;
       }
       //  (真偽を交互に置き換えする、ここにこれがないとEボタン連打したときに引き出しの開け閉めがうまく動作しなかった)
       isOpen = !isOpen;
       // インスペクタで設定した座標位置にアニメーション移動（引き出し開閉）
       iTween.MoveTo(gameObject, iTweenArgs);
    }
}
```
#FPSコントローラの設定変更
シーン上のFPSコントローラはカプセルコライダを設定していますが、Raycastを使用するときは
このコライダが邪魔をしてしまい開閉のアクションがうまくいかないことが起きてしまうようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234065/abc9dcc1-1553-398f-bd1a-74f19829f5a9.png)

そこでFPSコントローラの[Layer]プルダウンから[Ignore Raycast]を選択して設定しておくと
コライダの判定を無視したRaycast判定が利用できるようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234065/a0513beb-a81d-def0-7178-d63fa7ad4899.png)

#引き出しに物体を入れる
引き出しなので、中に何かが入っている状態を再現します。
動画内の内容に沿って引き出しの中に球体をいれました。
球体にはコンポネント追加で[Rigidbody]を設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234065/a16d0715-f8fd-30f7-ddcb-5ceba8c9e267.png)

物体をいれる引き出しにはRigidBody内の[Is Kinematic]にチェックをいれます。
Is Kinematicを有効化すると物理演算の影響が有効化されるため
引き出しの中にモノが入っている状態が再現できるのと、
引き出しを開いた勢いと同時に球体がコロコロと転がってきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234065/49ca9ef4-9ac2-19de-7bc0-bf4c1ef8b4c5.png)

#次回
動画チュートリアルの内容のつづきとなりますが、
引き出しのスライドの動きに加え、宝箱の開閉、ドア開閉の軸回転の動き（Rotate）を同シーン状に再現していきたいと思います。


#参考にしたもの
https://qiita.com/4_mio_11/items/4b10c6fe37fd7a856350
http://megumisoft.hatenablog.com/entry/2015/08/13/172136
https://ekulabo.com/rigidbody-is-kinematic