三角関数を使って円運動の軌道を変え続ける


# 三角関数を使って Unity で円運動の軌道を変え続ける

今回使用した環境は

windows 11
Unity 2020.3.16f1
Visual Studio Community 2022 v17.1.2

です。
## 初めに
今回、三角関数を少し勉強したので、それを記事にしたいと思います。

やりたい事は、昔、どこかで見た円運動の軌道を変え続けるエフェクトの作成です。

## Mathf でサインを取得する

ご存じかも知れませんが、Unity の Mathf 関数ではサインとコサインを取得できます。

以下に UI.Image をアタッチさせたオブジェクトを揺らすコードを書きます。

```
RectTransform rt;
void Start()
{
    rt = GetComponent<RectTransform>();
}

void Update()
{
    float sin = Mathf.Sin(Time.time);
    rt.localPosition(0, sin, 0);
}
```

## コサインを取得して円運動させる
ご存じの通り、ここにコサインを加えると、円運動が出来ます。

```
void Update()
{
    float sin = Mathf.Sin(Time.time);
    float cos = Mathf.Con(Time.time);
    rt.localPosition(cos, sin, 0);
}

```

## 円運動の速さと大きさを変える
この sin やら cos に倍率を掛けるとスピードや運動の大きさを変える事が出来ます。

Unity の Mathf.Sin と Mathf.Cos は 0~1 に引数の値を変換して返します。
ですので、この周期を早めたい時は Sin 引数の中の数値に倍率を掛けます。
今回は、localPosition に sin 変数を入れているので、運動の大きさを変えるのに Sin メソッドの返り値に moveVolume を掛けています。
もちろん、Sin と Cos に別々の数値を掛けることもできます。

```

public float moveSpeed;
public float moveVolume;

void Update()
{
    float sin = Mathf.Sin(Time.time * moveSpeed) * moveVolume;
    float cos = Mathf.Con(Time.time * moveSpeed) * moveVolume);
    rt.localPosition(cos, sin, 0);
}

```

## 円運動の軌道を変える
更に、時間をずらす事で円運動の軌道を変えられます。

これは、「数学ガールの秘密ノート」79p の中で記述されている、
「点(x,y) = (cosΘ, sin(Θ + α))」
を利用して、
「点(x,y) = (cos(Θ + α), sinΘ)」と処理をした部分になります。

```
public float essence;

void Update()
{
    float sin = Mathf.Sin(Time.time * moveSpeed) * moveVolume;
    float cos = Mathf.Con((Time.time - essence) * moveSpeed) * moveVolume);
    rt.localPosition(cos, sin, 0);
}

```

## 軌道を変化させ続ける
これを、軌道を変え続けるために、essence を変化させ続けます。
essenceShift の量は 0.01 が丁度良いかと思います。
moveSpeed は 15位が分かりやすく、
moveVolume は Canvas の大きさによって違いますが、初期設定
(Canvas - Screen Space -Overlay,
 MainCamera - Projection : Orthographic, Size : 5)だと 50 位が丁度良いと思います。

```C#:CircularMotionShifter
private float essence;
public float essenceShift;
bool canShift = true;
void Update()
{
    float sin = Mathf.Sin(Time.time * moveSpeed) * moveVolume;
    float cos = Mathf.Con((Time.time - essence) * moveSpeed) * moveVolume);

    if((sin % moveVolume > (moveVolume * 0.9) && canShift){
        essence += essenceShift;
        canShift = false;
    {
    else if(sin < 0)
    {
        canShift = true;
    }


    rt.localPosition(cos, sin, 0);
}
```

## 完成したコード
完成したソースコードは以下になります。

```C#:CircularMotion
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RotationTest : MonoBehaviour
{
    RectTransform rt;
    public int speed;
    public int moveSpeed;
    public int moveVolume;
    private float essense = 0;
    public float essenseShift;
    bool canShift = true;

    // Start is called before the first frame update
    void Start()
    {
        rt = GetComponent<RectTransform>();
    }

    // Update is called once per frame
    void Update()
    {
        float sin = Mathf.Sin(Time.time * moveSpeed) * moveVolume;
        float cos = Mathf.Cos((Time.time - essense) * moveSpeed) * moveVolume;

        Debug.Log(sin);
        if(sin % sinPower > (sinPower * 0.9) && canShift)
        {
            essense += essenseShift;
            canShift = false;
        }
        else if(sin < 0){
            canShift = true;
        }

        rt.localPosition = new Vector3(cos, sin, 0);
    }
}

```

スイッチの仕方が糞仕様ですが、とりあえずこれにてメモとします。


sin 関数の使い方に関しては以下のページにお世話になりました。
[Sin関数の使い方](https://qiita.com/Nekomasu/items/f526b9392fd16f2bd243)

また、三角関数については「数学ガールの秘密ノート-丸い三角関数(結城浩著)」
にお世話になりました。

## 追記：周期について
上記（nekomasu さん）の記事において、Time.time に 2πr を掛けると一周するというのが、単位円の円周が 2πr だと言うのは分かるのですが、僕の常識では角度とはオイラー角（多分）であり、0 ~ 360°で表すのに、なぜ 2πr で一周なのか分かりませんでした。

僕が調べたところによると、プログラミングの世界では、角度は弧度法（ラジアン）と呼ばれるもので表すのが普通らしいです。

このラジアンと言うものは、

2πr * Θ / 360°

で表されるもの。

つまり、Θ(角度) = 360° （一周回った)の時の
角度の値は 2πr になるのです。

つまり、
Mathf.Sin(2πr * Time.time);

にすると、1 秒で 2πr になるので、1秒で一周することになります。

ですからその部分を修正に入れると最終的なコードは

```C#:CircularMotion

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

public class RotationTest : MonoBehaviour
{
    RectTransform rt;
    public int speed;
    public float cycle;
    public int cycleTimes;
    public int moveVolume;
    private float essense = 0;
    public float essenseShift;
    bool canShift = true;

    // Start is called before the first frame update
    void Start()
    {
        rt = GetComponent<RectTransform>();
        cycleTimes = 1.0f;
        cycle = 1.0f / cycleTimes;
    }

    // Update is called once per frame
    void Update()
    {
        float sin = Mathf.Sin(Time.time * cycle * 2 * Mathf.PI) * moveVolume;
        float cos = Mathf.Cos((Time.time - essense) * cycle * 2 * Mathf.PI) * moveVolume;

        Debug.Log(sin);
        if(sin % sinPower > (sinPower * 0.9) && canShift)
        {
            essense += essenseShift;
            canShift = false;
        }
        else if(sin < 0){
            canShift = true;
        }

        rt.localPosition = new Vector3(cos, sin, 0);
    }
}

```

という事で、追記のキモは Mathf.Sin　に渡す引数の実体は一周が 2πr のラジアンだと言う事でした。

弧度法の開設については以下のサイトにお世話になりました。
[三角関数(cosΘ, sinΘ, tanΘ)](https://cognicull.com/ja/yifrirfw)
[弧度法](https://cognicull.com/ja/yifrirfw)