[Unity] プレイヤーを基準に、ドアを常に開き戸にする

## はじめに
今回はプレイヤーがドアを開ける際、どの方向からでも常にドアが開き戸となるようにする方法を説明します。
開き戸にするために、ドア本体を回転させて実装する方法になります。
![Videotogif (2).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1349017/1abccfa6-899a-b7c2-e28e-e0dee0f4c121.gif)






# 実装
プレイヤーがドアを開ける際の向きを求める方法は主に以下の2つが考えられます。

1.プレイヤーの位置とドアの位置を比較する。
2.ドアと特定のオブジェクト間に線を引き、その線を基準にプレイヤーの角度を求める。
今回は、2の方法を採用しました。

## 2つのベクトル間の角度を求める
これを実装する関数として、**[SignedAngle()](https://docs.unity3d.com/ja/current/ScriptReference/Vector3.SignedAngle.html)** が利用できます。

**SignedAngle**
書式: `public static float SignedAngle (Vector3 from, Vector3 to, Vector3 axis);`
`from`には基準となるベクトル、`to`にはもう一方のベクトルを指定します。`axis`には、向きの基準を決める軸を指定します。(後に詳細記述してます)
戻り値: axisベクトルの方向に対して時計回りの角度は正の値、反時計回りの角度は負の値となります。角度は-180度から180度の範囲で表されます。


それぞれのベクトルを始点から終点への「方向」を示すものとして計算します。具体的には、「fromObjからdoorへの方向」を示すベクトルaと、「playerからdoorへの方向」を示すベクトルbを計算します。
fromに設定するオブジェクトは、ドア横の左側に配置します。toには、doorを設定します。axisには、回転の軸にしたいy軸を設定します。ここではVector3.upを使います。。
```c#
Vector3 a = fromObj.position - door.position;
Vector3 b = player.position - door.position;
float angle = Vector3.SignedAngle(a, b, Vector3.up);
```

図にすると次のようなイメージです。
<img width="400" alt="Game Idea-35.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1349017/5c6ba1c3-afc2-4ef4-90d2-f3be51ddac44.jpeg">
<img width="400" alt="Game Idea-34.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1349017/1abe6a81-dbc3-94e0-081e-ec55a79564ad.jpeg">
これで、ドアを中心にfromオブジェクトとPlayerの角度を求められました。

## fromオブジェクトの配置
縦と横にドアを設置した場合、fromオブジェクトを適切な位置に配置するための条件式を次のように書きました。

c#
```c#
if ((45 <= startDoor && startDoor < 135) || (225 <= startDoor && startDoor < 315))
    fromObj.localPosition = new Vector3(door.localPosition.x,door.localPosition.y,door.localPosition.z - 5);
else
    fromObj.localPosition = new Vector3(door.localPosition.x + 5, door.localPosition.y, door.localPosition.z);
```
## 初期値のドアの向きで回転度を変更する
```c#
if ((45 <= startDoor && startDoor < 135) || (225 <= startDoor && startDoor < 315))
{
    //doorの向きを変更
    if (angle < 0)
        door.rotation = Quaternion.Euler(0, 90f, 0);
    else
        door.rotation = Quaternion.Euler(0, 270f, 0);
}
else
{
    if (angle < 0)
        door.rotation = Quaternion.Euler(0, 0f, 0);
    else
        door.rotation = Quaternion.Euler(0, 180f, 0);
```
Quaternion.Euler()は特定のオブジェクトを指定した角度で回転させる関数です。ここではy軸を中心にドアを回転させています。



## スクリプト
```c#		

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

public class BackRoomsDoor : MonoBehaviour
{
	[SerializeField] private Transform door;
	[SerializeField] private Transform fromObj;
    [SerializeField] private Transform player;
    private float angle = 0f;
    private float startDoor;
    Vector3 a = Vector3.zero;
    Vector3 b = Vector3.zero;

    void Start()
    {
        //doorのy軸のlocal rotationを取得
        startDoor = door.localEulerAngles.y;
        if ((45 <= startDoor && startDoor < 135) || (225 <= startDoor && startDoor < 315))
            fromObj.localPosition = new Vector3(door.localPosition.x, door.localPosition.y, door.localPosition.z - 5);
        else
            fromObj.localPosition = new Vector3(door.localPosition.x + 5, door.localPosition.y, door.localPosition.z);
    }

    private void Update()
    {
        a = fromObj.position - door.position;
        b = player.position - door.position;
        //aとbの2つのベクトル間の角度を-180度から180度の範囲で返します。
        angle = Vector3.SignedAngle(a, b, Vector3.up);
        if ((45 <= startDoor && startDoor < 135) || (225 <= startDoor && startDoor < 315))
        {
    		//doorの向きを変更
            if (angle < 0)
                door.rotation = Quaternion.Euler(0, 90f, 0);
            else
                door.rotation = Quaternion.Euler(0, 270f, 0);
        }
        else
        {
            if (angle < 0)
                door.rotation = Quaternion.Euler(0, 0f, 0);
            else
                door.rotation = Quaternion.Euler(0, 180f, 0);
         }
    }
}
```



## 参考記事
https://gametukurikata.com/basic/calculateangle

https://nekojara.city/unity-vector-angle

## GitHub

https://github.com/subarunrun0812/Hybrid