Unity 3D入門 #9 [ドアの開閉 ]

ドアの前で特定のボタンを押すことでドアの開閉ができるようにします。

Sun Templeというアセットの中のDoorというスクリプトをもとに作っていきます。

* namespace SunTempleを消すためにはSun Templeのスクリプト「CircularLerp.cs」の関数[public static float Clerp(float start, float end, float value)]をDoor.csにコピペする必要があります。

```
public bool IsLocked = false;  //ドアに鍵がかかっているか
public bool DoorClosed = true;  //ドアが現在閉まっているか
public float OpenRotationAmount = 90;  //ドアが何度開くか(90で直角に開く)
public float RotationSpeed = 1f;  //ドアが開く速度
public float MaxDistance = 3.0f;  //ドアからどれほど離れていても開けられるか
private Collider DoorCollider;  //ドアのコライダー(DoorCollider.enabled = falseで当たり判定消失)
private GameObject Player;  //ドアを開けるプレイヤーのGameObject
private Camera Cam;  //メインカメラ

float StartAngle = 0;
float EndAngle = 0;
float LerpTime = 1f;  //ドアを開ける際にかかる時間
float CurrentLerpTime = 0;  //ドアを開ける際の現在の移動時間
bool Rotating;

void Start(){
    DoorCollider = GetComponent<BoxCollider> ();
    Player = GameObject.FindGameObjectWithTag ("Player");
    Cam = Camera.main;
}
void Update(){
    if (Rotating) {
        Rotate ();
    }
    if(Input.GetKeyDown(KeyCode.M)){
        TryToOpen ();
    }
} 

void TryToOpen(){
    if (Mathf.Abs(Vector3.Distance(transform.position, Player.transform.position)) <= MaxDistance){	
        Ray ray = Cam.ScreenPointToRay (new Vector3 (Screen.width / 2, Screen.height / 2, 0));
        RaycastHit hit;
        if (DoorCollider.Raycast(ray, out hit, MaxDistance)){					
            if (IsLocked == false){
                if (DoorClosed)
                    Open();
                else
                    Close();
            }
        }
    }
}

void Rotate(){
    CurrentLerpTime += Time.deltaTime * RotationSpeed;
    if (CurrentLerpTime > LerpTime){
        CurrentLerpTime = LerpTime;
    }
    float _Perc = CurrentLerpTime / LerpTime;  //今全体の何割進んでいるか
    float _Angle = Clerp(StartAngle, EndAngle, _Perc);  //今現在の角度
    transform.localEulerAngles = new Vector3(transform.eulerAngles.x, _Angle, transform.eulerAngles.z);
    if (CurrentLerpTime == LerpTime) {  //回転し終えたら
        Rotating = false;
        DoorCollider.enabled = true;
    }
}

void Open(){
    DoorCollider.enabled = false;  //コライダーを無効化
    DoorClosed = false;
    StartAngle = transform.localEulerAngles.y;
    EndAngle =  transform.localEulerAngles.y + OpenRotationAmount;
    CurrentLerpTime = 0;
    Rotating = true;
}

void Close(){
    DoorCollider.enabled = false;
    DoorClosed = true;
    StartAngle = transform.localEulerAngles.y;
    EndAngle = transform.localEulerAngles.y - OpenRotationAmount;
    CurrentLerpTime = 0;
    Rotating = true;
}

public static float Clerp(float start, float end, float value){
    float min = 0.0f;
    float max = 360.0f;
    float half = Mathf.Abs((max - min) / 2.0f);//half the distance between min and max
    float retval = 0.0f;
    float diff = 0.0f;
    if ((end - start) < -half){
        diff = ((max - start) + end) * value;
        retval = start + diff;
    } else if ((end - start) > half) {
        diff = -((max - end) + start) * value;
        retval = start + diff;
    } else retval = start + (end - start) * value;
    return retval;
}
```

上の長いコードの分からなかったところ

* Cam.ScreenPointToRay(Vector3 pos) posからカメラの向いている方向に向けてRayを発生させる(https://developer.roblox.com/en-us/api-reference/function/Camera/ScreenPointToRay)

* lerpについて 二点間の線形補間 
    lerp参考URL：https://www.sejuku.net/blog/83510
    線形補間参考URL：https://qiita.com/niusounds/items/c4af702b06582590c82e

* 関数Clerpについて value : 全体の何割進んでいるか, retval : startとendの間となる線形補間された回転角度(下にイメージ図)
<img width="323" alt="スクリーンショット 2020-07-30 0.50.18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670403/04b20eee-c13d-7b24-3620-1938720c8c61.png">

![ezgif-7-a86150982043.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670403/e0ec8c6a-3f51-8728-e2bd-0d15403f2fe5.gif)