【Unity】マリオギャラクシーのように惑星の表面を歩こう！

・この記事ではUnityでマリオギャラクシーのように惑星の表面を歩くシステムを実装します。

・アセットは使用せず、初めからUnityに搭載されている機能のみを使って実装します。

・動けば良い！の精神でおねがいします。

・筆者の環境：Windows10 Unity 2020.1.3f1 Personal

# はじめに
みなさんが子どもの頃に遊んだ好きなゲームってなんですか？

私は小さいころWiiで遊んだマリオギャラクシーが特に大好きです。

マリオギャラクシーのステージは惑星（平面でないステージ）が多く、印象的ですよね。

このシステム、Unityで再現できれば面白いと思いませんか？

というわけで、今回はこの「惑星の表面を歩くシステム」をUnityで実装してみましょう。

# 今回のゴール

画像では伝わりにくいので動画↓でどうぞ。※クリックすると新規タブでYouTubeが開きます。
[![Qiita記事_03用動画_01](http://img.youtube.com/vi/sVBTHZyIFOg/0.jpg)](http://www.youtube.com/watch?v=sVBTHZyIFOg)

## 目標
１：惑星オブジェクトのポリゴンに沿って運動できるようにする
２：惑星を複数配置し、惑星間を飛び回る

# 実装・その１
1. Unityを起動し、3Dでプロジェクトを新規作成する。（名前はなんでもよい）

1. `` Hierarchy +タブ`` -> ``3D Object`` -> ``Capsule``

1. ``Capsule``の名前を「 Player 」に変更する。

1. ``Player``のポジションを``[ x = 0, y = 13, z = 0 ]``に変更する。

1. ``Player`` -> ``Inspector`` -> ``Add Component`` -> ``Physics`` -> ``Rigidbody``を追加する。 

1. ``Player`` -> ``Rigidbody`` の ``Use Gravity``のチェックを外す。

1. ``Player`` -> ``Rigidbody`` -> ``Constraints`` -> ``Freeze Rotaion``の``x, y, z``にチェックを入れる。

1. このままだと正面がどこかわからないので、Cubeを子オブジェクトにして鼻を付けると分かりやすいかも。（お好みでどうぞ）

1. `` Hierarchy +タブ`` -> ``3D Object`` -> ``Sphere`` 

1. ``Sphere``の名前を「 Planet_Sphere 」に変更する。

1. ``Planet_Sphere``のスケールを``[ x = 20, y = 20, z = 20 ]``程度に変更する。

1. ``Planet_Sphere`` -> ``Inspector`` -> ``Tag`` -> ``Add Tag...``から「 Planet 」というタグを新しく設定する。

1. ``Planet_Sphere``のタグを「 ``Planet`` 」に変更する。

1. ``Project +タブ`` -> ``C# Script`` から「 Player_Logic 」という名前でスクリプトを新規作成する。

1. `` Player_Logic`` を　``Player``　にアタッチする。

1. `` Player_Logic``に以下のコードをコピペする。

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

public class Player_Logic : MonoBehaviour
{
    //プレイヤーの移動する速さ
    public float move_speed = 15;

    //プレイヤーの回転する速さ
    public float rotate_speed = 5;

    //プレイヤーの回転する向き
    //1 -> （プレイヤーから見て）時計回り
    //-1 -> （プレイヤーから見て）反時計回り
    private int rotate_direction = 0;

    //プレイヤーのRigidbody
    private Rigidbody Rig = null;

    //地面に着地しているか判定する変数
    public bool Grounded;

    //ジャンプ力
    public float Jumppower;


    void Start()
    {
        Rig = this.GetComponent<Rigidbody>();
    }

    void Update()
    {
        Jump();
    }

    private void FixedUpdate()
    {
        Horizontal_Rotate();

        Vector3 move_direction = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical")).normalized;
        
        Rig.MovePosition(Rig.position + transform.TransformDirection(move_direction) * move_speed * Time.deltaTime);
    }

    void Jump()
    {
        if (Grounded == true)//  もし、Groundedがtrueなら、
        {
            if (Input.GetKeyDown(KeyCode.Space))//  もし、スペースキーがおされたなら、  
            {
                Grounded = false;//  Groundedをfalseにする
                Rig.AddForce(transform.up * Jumppower * 100);//  上にJumpPower分力をかける
            }
        }
    }

    void OnCollisionEnter(Collision other)//  他オブジェクトに触れた時の処理
    {
        if (other.gameObject.tag == "Planet")//  もしPlanetというタグがついたオブジェクトに触れたら、
        {
            Grounded = true;//  Groundedをtrueにする
        }
    }

    void Horizontal_Rotate()
    {
        if (Input.GetKey(KeyCode.Q))
        {
            rotate_direction = -1;
        }
        else if (Input.GetKey(KeyCode.E))
        {
            rotate_direction = 1;
        }
        else
        {
            rotate_direction = 0;
        }

        // オブジェクトからみて垂直方向を軸として回転させるQuaternionを作成
        Quaternion rot = Quaternion.AngleAxis(rotate_direction * rotate_speed, transform.up);
        // 現在の自信の回転の情報を取得する。
        Quaternion q = this.transform.rotation;
        // 合成して、自身に設定
        this.transform.rotation = rot * q;
    }
}
```
# Player_Logic.C#の解説
・宣言部分

```C#:Player_Logic.C#
//プレイヤーの移動する速さ
    public float move_speed = 15;

    //プレイヤーの回転する速さ
    public float rotate_speed = 5;

    //プレイヤーの回転する向き
    //1 -> （プレイヤーから見て）時計回り
    //-1 -> （プレイヤーから見て）反時計回り
    private int rotate_direction = 0;

    //プレイヤーのRigidbody
    private Rigidbody Rig = null;

    //地面に着地しているか判定する変数
    public bool Grounded;

    //ジャンプ力
    public float Jumppower;
```

コメントアウトそのままです。特に説明しなくても大丈夫でしょう。

---
・Start関数部分

```C#:Player_Logic.C#
    void Start()
    {
        Rig = this.GetComponent<Rigidbody>();
    }
```

スクリプトがアタッチされているオブジェクトのコンポーネントを参照し、Rigidbodyを取得しています。

---

・Update関数部分

```C#:Player_Logic.C#
    void Update()
    {
        Jump();
    }
```

Jump関数を呼び出しています。Jump関数の中身は↓に

---

・Jump関数部分
順番が入れ替わりますが、先にJump関数の説明です。

```C#:Player_Logic.C#
    void Jump()
    {
        if (Grounded == true)//  もし、Groundedがtrueなら、
        {
            if (Input.GetKeyDown(KeyCode.Space))//  もし、スペースキーがおされたなら、  
            {
                Grounded = false;//  Groundedをfalseにする
                Rig.AddForce(transform.up * Jumppower * 100);//  上にJumpPower分力をかける
            }
        }
    }
```

ジャンプ機能が重複しない（無限ジャンプができない）ようにしています。
ここのポイントはAddForceで力を加える際に、上方向を「``Player``オブジェクトからみて垂直方向``(transform.up)``」にしてあるところです。
惑星単位で重力を操るので、ワールド基準の垂直方向では意味がなくなってしまうためです。

```C#
Rig.AddForce(transform.up * Jumppower * 100);
```

---

・OnCollisionEnter関数部分
順番が入れ替わりますが、ジャンプ機能に関連するので先に説明します。

```C#:Player_Logic.C#
    void OnCollisionEnter(Collision other)//  他オブジェクトに触れた時の処理
    {
        if (other.gameObject.tag == "Planet")//  もしPlanetというタグがついたオブジェクトに触れたら、
        {
            Grounded = true;//  Groundedをtrueにする
        }
    }
```

ジャンプ機能の回復について制御しています。
Jump関数では Spaceキー を押すと Grounded変数が false に設定されます。このままだと二度とジャンプすることができません (´;ω;｀)
OnCollisionEnter関数では他のオブジェクトに接触した際、そのオブジェクトのタグが``Planet``であった場合に Grounded変数を true に設定します。
これで再びジャンプすることができます。＾＾ ﾔｯﾀﾈ!

---

・FixedUpdate関数部分
順番が前後しましたがFixedUpdate関数の説明です。

```C#:Player_Logic.C#
    private void FixedUpdate()
    {
        Horizontal_Rotate();

        Vector3 move_direction = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical")).normalized;
        
        Rig.MovePosition(Rig.position + transform.TransformDirection(move_direction) * move_speed * Time.deltaTime);
    }
```

一行目では``Horizontal_Rotate関数``を呼び出しています。

二行目ではキーボードの入力に対して、``Player``の進行方向を設定しています。
また、「 方向 」なので .normalized を用いて正規化しています。

三行目では``Rigidbody.MovePosition``を用いて``Player``の位置を変更しています。
``Rigidbody.MovePosition``は移動したい先の座標（ワールド座標・絶対座標）を引数とすることで、オブジェクトを指定した座標に移動させます。
``Time.deltaTime``をかける（補間する）ことでスムーズ遷移となります。（本当は違いますが...）

より詳しい説明や考察は以下の記事からどうぞ...

> 
> Unity DOCUMENTION Rigidbody.MovePosition
> https://docs.unity3d.com/ja/2018.4/ScriptReference/Rigidbody.MovePosition.html
> 

> 
> Qiita [Unity初心者Tips]どれが良いかわかる！ものを動かす方法はこうして決める
> https://qiita.com/JunShimura/items/ab243cbd29e63e4f27c5
> 

> 
> 弱火でじっくり [Unity] positionとMovePositionの違いを比べた
> https://yowabi.blogspot.com/2017/12/unity-positionmoveposition-rigidbody.html?m=1
> 

また、``transform.TransformDirection``は「ローカルな方向・ベクトル・座標」を「ワールドな方向・ベクトル・座標」に変換します。
より詳しい説明は以下の記事を参考にしてください,,,

> 
> Unity DOCUMENTION Transform.TransformDirection
> https://docs.unity3d.com/ja/current/ScriptReference/Transform.TransformDirection.html
> 

> 
> TECH Pjin 【Unity】Transformコンポーネントの便利な関数まとめ
> https://tech.pjin.jp/blog/2016/03/29/unity_transform_compo/
> 

---

・Horizontal_Rotate関数部分

```C#:Player_Logic.C#
    void Horizontal_Rotate()
    {
        if (Input.GetKey(KeyCode.Q))
        {
            rotate_direction = -1;
        }
        else if (Input.GetKey(KeyCode.E))
        {
            rotate_direction = 1;
        }
        else
        {
            rotate_direction = 0;
        }

        // オブジェクトからみて垂直方向を軸として回転させるQuaternionを作成
        Quaternion rot = Quaternion.AngleAxis(rotate_direction * rotate_speed, transform.up);
        // 現在の自信の回転の情報を取得する。
        Quaternion q = this.transform.rotation;
        // 合成して、自身に設定
        this.transform.rotation = rot * q;
    }
```

ここではプレイヤーからみた水平方向の回転について制御しています。

また、Quaternionの合成は積で行うのですが、

```C#
Quaternion.AngleAxis(rotate_direction * rotate_speed, transform.up);
```

この部分で「``Player``オブジェクトからみた上方向（``transform.up``）」を軸として回転させるQuaternionを生成しています。

回転量は``rotate_speed``で指定し、回転方向は``rotate_direction``で指定しています。

``rotate_direction`` はキーボード入力（Ｑキーで反時計回り（-1）、Ｅキーで時計回り（+1））を設定します。何も入力がないときは 0 を設定します。これがポイントです。

``感覚としてはオブジェクトに長い棒をぶっさしてクルクルまわしている感じです。``

また、Quaternionは線形代数などで学ぶ回転行列と強い関係を持ちます。

そのため交換法則が必ず成り立つとは限りません。

もし、Quaternionの回転で意図しない動きをしてしまったら、積の順番を入れ替えてみるのもおすすめです。

行列の交換法則についてのわかりやすい説明です。よかったらどうぞ↓

> 
> 高校数学の基本問題　高卒～大学数学　行列の乗法の性質
> http://www.geisya.or.jp/~mwm48961/kou2/matrix3.html
> 

## Quaternionについて
Quaternion（クォータニオン）ってなんだ？と思う方もいるかもしれません。

QuaternionはUnityやゲームエンジン、その他の分野で「回転」を扱う際に非常に重要な概念です。

Unityでは回転にかかわることを全てこのQuaternionで制御しています。

「全てだって？！インスペクターではオブジェクトの回転はオイラー角で制御しているじゃないか！(°д°) 」と思う人もいるでしょう。

これはUnityが気を利かせて、内部処理でオイラー角をQuaternionに変形してくれているのです。Unityちゃんありがとう！(≧▽≦)

さて、Quaternionとは何かを語ると非常に~~面倒くさい~~大変なので、Quaternionの本質を考えたい方は以下の記事を参考にしてください。

``特にセガ公式の記事はおすすめです。大企業の資料を見ることなんてなかなかできません！公開されているうちにぜひ見ましょう！``

> 
> Twitter セガ公式アカウント クォータニオンとは何ぞや？
> https://twitter.com/SEGA_OFFICIAL/status/1404648102477262849
> 

> 
> XR-HU3 【Unity】Quaternionでオブジェクトを回転させる方法
> https://xr-hub.com/archives/11515
> 

> 
> SAMURAIENGINEER 【Unity入門】必ず分かる!一番簡単なQuaternionの使い方入門!
> https://www.sejuku.net/blog/55596
> 

> 
> クォータニオン (Quaternion) を総整理！ ～ 三次元物体の回転と姿勢を鮮やかに扱う ～
> https://qiita.com/drken/items/0639cf34cce14e8d58a5
> 

# 途中経過

さて、ここで一度再生ボタンを押して動作を確認してみましょう。

！注意！　ジャンプ力を表す変数``Jumppower``は初期値では０に設定されています。

重力の概念を追加していないので、同じ高さで動き回ることが出来れば成功です。

これでプレイヤーの動作を実装できました。

次は重力とその他もろもろについて実装しましょう。

# 実装・その2

1. ``Project +タブ`` -> ``C# Script`` から「 Gravity_Logic 」という名前でスクリプトを新規作成する。

1. `` Gravity_Logic`` を　``Player``　にアタッチする。

1. `` Gravity_Logic``に以下のコードをコピペする。

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

public class Gravity_Logic : MonoBehaviour
{
    //PlayerのTransform
    private Transform myTransform;

    //PlayerのRigidbody
    private Rigidbody rig = null;

    //重力減となる惑星
    private GameObject Planet;

    //「Planet」タグがついているオブジェクトを格納する配列
    private GameObject[] Planets;

    //重力の強さ
    public float Gravity;

    //惑星に対するPlayerの向き
    private Vector3 Direction;

    //Rayが接触した惑星のポリゴンの法線
    private Vector3 Normal_vec = new Vector3(0,0,0);

    void Start()
    {
        rig = this.GetComponent<Rigidbody>();
        rig.constraints = RigidbodyConstraints.FreezeRotation;
        rig.useGravity = false;
        myTransform = transform;
    }

    void Update()
    {
        Attract();
        RayTest();
    }

    public void Attract()
    {
        Vector3 gravityUp = Normal_vec;

        Vector3 bodyUp = myTransform.up;

        myTransform.GetComponent<Rigidbody>().AddForce(gravityUp * Gravity);

        Quaternion targetRotation = Quaternion.FromToRotation(bodyUp, gravityUp) * myTransform.rotation;

        myTransform.rotation = Quaternion.Lerp(myTransform.rotation, targetRotation, 120 * Time.deltaTime);

    }

    GameObject Choose_Planet()
    {
        Planets = GameObject.FindGameObjectsWithTag("Planet");

        double[] Planet_distance = new double[Planets.Length];

        for (int i = 0; i < Planets.Length; i++)
        {
            Planet_distance[i] = Vector3.Distance(this.transform.position, Planets[i].transform.position);
        }

        int min_index = 0;
        double min_distance = Mathf.Infinity;

        for (int j = 0; j < Planets.Length; j++)
        {
            if (Planet_distance[j] < min_distance)
            {
                min_distance = Planet_distance[j];
                min_index = j;
            }
        }

        return Planets[min_index];
    }

    void RayTest()
    {
        Planet = Choose_Planet();

        Direction = Planet.transform.position - this.transform.position;

        Ray ray = new Ray(this.transform.position, Direction);

        //Rayが当たったオブジェクトの情報を入れる箱
        RaycastHit hit;

        //もしRayにオブジェクトが衝突したら
        if (Physics.Raycast(ray, out hit, Mathf.Infinity))
        {
            //Rayが当たったオブジェクトのtagがPlanetだったら
            if (hit.collider.tag == "Planet")
            {
                Normal_vec = hit.normal;
            }
        }
    }
}
```

# Gravity_Logicの解説
・宣言部分

```C#:Gravity_Logic.C#
    //PlayerのTransform
    private Transform myTransform;

    //PlayerのRigidbody
    private Rigidbody rig = null;

    //重力減となる惑星
    private GameObject Planet;

    //「Planet」タグがついているオブジェクトを格納する配列
    private GameObject[] Planets;

    //重力の強さ
    public float Gravity;

    //惑星に対するPlayerの向き
    private Vector3 Direction;

    //Rayが接触した惑星のポリゴンの法線
    private Vector3 Normal_vec = new Vector3(0,0,0);
```

コメントアウトしている説明そのままです。特に解説する必要はないでしょう。
``Normal_vec``にゼロベクトルを代入し、初期化しています。（開始後数フレームの間Normal_vecに何も入っていない（わけのわからない数）が代入されてしまうのを防ぐためです。）

---

・Start関数部分

```C#:Gravity_Logic.C#
    void Start()
    {
        rig = this.GetComponent<Rigidbody>();
        rig.constraints = RigidbodyConstraints.FreezeRotation;
        rig.useGravity = false;
        myTransform = this.transform;
    }
```

一行目では Player の Rigidbody である``rig``にコンポーネント経由でPlayerのRigidbodyを格納しています。

二行目・三行目では``実装・その１の6番目7番目``で行った設定を念のためスクリプト側からも設定しています。

四行目では、このスクリプトがアタッチされているオブジェクトのTransformを``myTransform``に格納しています。

---

・Update関数部分

```C#:Gravity_Logic.C#
    void Update()
    {
        Attract();
        RayTest();
    }
```

一行目では ``Attract関数`` を呼び出しています。``Attract関数`` は重力とPlayerの回転について制御する関数です。

二行目では ``RayTest関数`` を呼び出しています。``RayTest関数`` は ``Attract関数`` 内で使用される``Normal_vec`` にRayが接触したポリゴンの法線を格納する役割を持ちます。

---

・Attract関数内部

```C#:Gravity_Logic.C#
    public void Attract()
    {
        Vector3 gravityUp = Normal_vec;

        Vector3 bodyUp = myTransform.up;

        myTransform.GetComponent<Rigidbody>().AddForce(gravityUp * Gravity);

        Quaternion targetRotation = Quaternion.FromToRotation(bodyUp, gravityUp) * myTransform.rotation;

        myTransform.rotation = Quaternion.Lerp(myTransform.rotation, targetRotation, 120 * Time.deltaTime);

    }
```

一行目では、``gravityUp``というVector3型の変数に``Normal_vec``を代入しています。GravityUpという分かりやすい名前にしたかったので代入しました。意味としては重力の逆ベクトルを表しています。

二行目では、``bodyUp``というVector3型の変数に``myTransform.up``を代入しています。
こちらも分かりやすい名前にしたかったので代入しました。意味としては``Player``オブジェクトからみた頭上のベクトルを表しています。

三行目では、コンポーネント経由で``myTransform``からRigidbodyを参照し、AddForceで力を加えています。
力を加える向きは先ほど指定した``gravityUp``で、力の大きさは``Gravity``です。この引数からわかる通り、``Gravity``は負の数で指定する必要があります。

四行目では、Quaternion.FromToRotationを用いてQuaternionを生成しています。
自身がどれだけ回転しているかという情報を``myTransform.rotation``で表し加算しています。つまり現在の姿勢からどれだけ回転させるかを計算し``targetRotation``に格納しています。

五行目では、Quaternion.Lerpを用いて第一引数と第二引数の間の角度を第三引数で指定した秒数で「線形補間」しながら回転させています。

## .Lerp と .Slerp について

五行目でQuaternion.Lerpを用いて回転させていましたが、「線形補間」のほかに「球面補間」というものがあります。

「線形補間」は直線を意識した回転を。
「球面補間」は球面に沿うような回転をします。

より詳しい解説はこちらの記事↓からどうぞ！

> 
> 【Unity入門】LerpとSlerpの使い方と違い!自在に補間をかけよう
>  https://www.sejuku.net/blog/83510
> 


今回のプログラムではどちらを使っても特に違いは出ないと思います。ステージの形状の傾向などを考慮してお好みで変更してあげてください。

---

順番が前後しますが先にRayTest関数の解説です。
・RayTest関数部分

```C#:Gravity_Logic.C#
    void RayTest()
    {
        Planet = Choose_Planet();

        Direction = Planet.transform.position - this.transform.position;

        Ray ray = new Ray(this.transform.position, Direction);

        //Rayが当たったオブジェクトの情報を入れる箱
        RaycastHit hit;

        //もしRayにオブジェクトが衝突したら
        if (Physics.Raycast(ray, out hit, Mathf.Infinity))
        {
            //Rayが当たったオブジェクトのtagがPlanetだったら
            if (hit.collider.tag == "Planet")
            {
                Normal_vec = hit.normal;
            }
        }
    }
```

一行目では、``Choose_Planet関数`` の戻り値を ``Planet`` に代入しています。
``Choose_Planet関数`` をざっくり説明すると、``Planetタグ`` を持っているオブジェクトで一番 ``Player`` に近いオブジェクトを返す関数です。

二行目では、``Direction`` にプレイヤーから見た惑星中心のベクトルを代入しています。

三行目では、Rayを宣言しています。Rayの発射地点は``Player``の座標で、Rayを飛ばす方向は``Direction``です。

四行目では、``Ray``が当たったオブジェクトの情報を入れる``hit``を宣言しています。

五行目以降では、Rayがオブジェクトに接触した際の処理を書いています。
Rayが接触したオブジェクトのタグが``Planet``である場合にRayが接触した惑星のポリゴンの法線（``hit.normal``）を``Normal_vec``に代入します。
この代入をすることで、惑星の表面に沿って``Player``が回転するようになります。
なにげに重要な部分です笑

---

順番が前後しましたがChoose_Planet関数の解説です。
・Choose_Planet関数部分

```C#:Gravity_Logic.C#
    GameObject Choose_Planet()
    {
        Planets = GameObject.FindGameObjectsWithTag("Planet");

        double[] Planet_distance = new double[Planets.Length];

        for (int i = 0; i < Planets.Length; i++)
        {
            Planet_distance[i] = Vector3.Distance(this.transform.position, Planets[i].transform.position);
        }

        int min_index = 0;
        double min_distance = Mathf.Infinity;

        for (int j = 0; j < Planets.Length; j++)
        {
            if (Planet_distance[j] < min_distance)
            {
                min_distance = Planet_distance[j];
                min_index = j;
            }
        }

        return Planets[min_index];
    }
```

Choose_Planet関数は``Planetタグ`` を持っているオブジェクトで一番 ``Player`` に近いオブジェクトを返す関数です。

アルゴリズムは有名なので解説は不要でしょう。
C#には配列の最大値を返す便利な機能もありますが今回は使っていません。その機能を使えばコードはもう少し短くなると思います。

#実行・おわりに

さて、ここまできたら再生ボタンを押して実行してみましょう！

``Player`` が惑星( ``Planet`` )の側面に沿って歩くことが出来れば実装成功です！

``！注意！``　ジャンプ力を表す変数 ``Jumppower`` は初期値では０に設定されています。お好みで変更してください。

今回はUnityでマリオギャラクシーのように惑星の表面を歩くシステムを実装しました。

これを機にゲーム技術への関心を持っていただけたら嬉しいです。

長い記事になりましたがここまで読んでくれてありがとうございました。

それではまた次の記事でお会いしましょう！お疲れさまでした。(´∇｀)

# 参考文献

>
> Unity Tutorial: Planet Spherical Gravity (Multiple Planets!) - Super Mario Galaxy Movement
> https://www.youtube.com/watch?v=UeqfHkfPNh4&t=164s
>

>
> How Did They Do That - Mario Galaxy's Gravity
> https://www.youtube.com/watch?v=vALtyrp87mI
>

> 
> Unityでのジャンプ【Rigidbody,CharacterControllerどちらも対応】
> https://getabakoclub.com/2019/12/01/unity%E3%81%A7%E3%81%AE%E3%82%B8%E3%83%A3%E3%83%B3%E3%83%97%E3%80%90rigidbodycharactercontroller%E3%81%A9%E3%81%A1%E3%82%89%E3%82%82%E5%AF%BE%E5%BF%9C%E3%80%91/#Rigidbody
> 

> 
> 【Unity】Time.deltaTimeの正しい使い方わかってる？適当に掛ければいいてもんじゃない！
> https://qiita.com/toRisouP/items/930100e25e666494fcd6
> 

> 
> [Unity初心者Tips]どれが良いかわかる！ものを動かす方法はこうして決める
> https://qiita.com/JunShimura/items/ab243cbd29e63e4f27c5#%E6%96%B9%E6%B3%952iskinematic%E3%81%A8moveposition%E3%81%A7%E3%82%B4%E3%83%AA%E6%8A%BC%E3%81%97
> 

> 
> Twitter セガ公式アカウント クォータニオンとは何ぞや？
> https://twitter.com/SEGA_OFFICIAL/status/1404648102477262849
> 

> 
> XR-HU3 【Unity】Quaternionでオブジェクトを回転させる方法
> https://xr-hub.com/archives/11515
> 

> 
> SAMURAIENGINEER 【Unity入門】必ず分かる!一番簡単なQuaternionの使い方入門!
> https://www.sejuku.net/blog/55596
> 

> 
> クォータニオン (Quaternion) を総整理！ ～ 三次元物体の回転と姿勢を鮮やかに扱う ～
> https://qiita.com/drken/items/0639cf34cce14e8d58a5
> 

> 
> 基礎線形代数講座 - 線形代数・回転の表現 - 株式会社 セガ 開発技術部
> https://www.slideshare.net/SEGADevTech/ss-249343092
> 

> 
> 【Unity入門】LerpとSlerpの使い方と違い!自在に補間をかけよう
>  https://www.sejuku.net/blog/83510
> 

> 
> 高校数学の基本問題　高卒～大学数学　行列の乗法の性質
> http://www.geisya.or.jp/~mwm48961/kou2/matrix3.html
> 

> 
> Unity DOCUMENTION Rigidbody.MovePosition
> https://docs.unity3d.com/ja/2018.4/ScriptReference/Rigidbody.MovePosition.html
> 

> 
> Qiita [Unity初心者Tips]どれが良いかわかる！ものを動かす方法はこうして決める
> https://qiita.com/JunShimura/items/ab243cbd29e63e4f27c5
> 

> 
> 弱火でじっくり [Unity] positionとMovePositionの違いを比べた
> https://yowabi.blogspot.com/2017/12/unity-positionmoveposition-rigidbody.html?m=1
>