質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.52%

SmoothFollow.csの分からない箇所に関して。

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 431

tkmnusr

score 300

前提・実現したいこと

Unity標準アセットのSmoothFollow.csのコードについて、
処理が分からない箇所があるのでご教示お願いします。

該当のソースコード

メインカメラにアタッチしました。

    public class SmoothFollow : MonoBehaviour
    {

        // The target we are following
        [SerializeField]
        private Transform target;
        // The distance in the x-z plane to the target
        [SerializeField]
        private float distance = 10.0f;
        // the height we want the camera to be above the target
        [SerializeField]
        private float height = 5.0f;

        [SerializeField]
        private float rotationDamping;

        [SerializeField]
        private float heightDamping;

        // Use this for initialization
        void Start() { 
        }

        // Update is called once per frame
        void LateUpdate()
        {            
            // Early out if we don't have a target
            if (!target)
                return;

            // Calculate the current rotation angles
            var wantedRotationAngle = target.eulerAngles.y;
            var wantedHeight = target.position.y + height;

            var currentRotationAngle = transform.eulerAngles.y;
            var currentHeight = transform.position.y;

            // Damp the rotation around the y-axis
            currentRotationAngle = Mathf.LerpAngle(currentRotationAngle, wantedRotationAngle, rotationDamping * Time.deltaTime);

            // Damp the height
            currentHeight = Mathf.Lerp(currentHeight, wantedHeight, heightDamping * Time.deltaTime);

            // Convert the angle into a rotation
            var currentRotation = Quaternion.Euler(0, currentRotationAngle, 0);

            // Set the position of the camera on the x-z plane to:
            // distance meters behind the target
            transform.position = target.position;
            transform.position -= currentRotation * Vector3.forward * distance;

            // Set the height of the camera
            transform.position = new Vector3(transform.position.x ,currentHeight , transform.position.z);

            // Always look at the target
            transform.LookAt(target);


        }
    }

試したこと

ソースコードを上から読んでいき、下記コード辺りまではおそらく理解できました。

var currentRotation = Quaternion.Euler(0, currentRotationAngle, 0);


currentRotationは、Y軸周りの角度に関して、
thisのY軸の角度から、targetのY軸の角度に変化していくものかと思います。
また、Heightに関する処理のコードも理解できていると思います。

質問

・質問1。
理解しているつもりですが、念のため質問です。
currentRotationのコードについて、
transform.eulerAngles.yをtarget.eulerAngles.yに変えていると思いますが、
これは、thisのZ軸の向きを、targetのZ軸の向きに合わせているという認識で合っていますか?

・質問2。
こちらのコードがわかりません。

  transform.position = target.position;
  transform.position -= currentRotation * Vector3.forward * distance;


まず、1行目は、thisをtargetの位置に持ってきているかと思いますが、
ここから、2行目で、「currentRotation * Vector3.forward * distance」の分だけ
移動させているかと思うのですが、
Vector3.forward * distanceに、左からcurrentRotation * を掛けているのが、
どのようなベクトルになるかがわかりません。
仮にコードが下記であれば、イメージは付きます。

  transform.position = target.position;
  transform.position -= Vector3.forward * distance;


追記。
クォータニオンとベクトルの掛け算について考えました。
・質問2-1。
this.transform.rotationにベクトルdirを掛けたものは、

 this.transform.rotation * dir


「thisのローカル軸を基準に伸びるベクトルdir」という認識で合っていますか?
そう考えると、
・質問2-2。
「『Rotation(クォータニオン) * dir(ベクトル)』は、『Rotationの回転軸を基準に伸びるベクトルdir』」
という認識で合っていますか?

・質問3。
質問2の時点のコードが理解できていないので、さらにその先もわからないのですが、

 transform.LookAt(target);


これのコードをコメントアウトするのとしないのとでは、カメラの動きが大きく変わる理由がわかりません。
「transform.LookAt(target);」これ自体のコードは、
thisのZ方向の向きをtargetに向かせるだけの処理かと思うのですが、
これをコメントアウトするのとしないのとでは、なぜこんなにも処理が大きく変わってしまうのでしょうか?

・ゲーム実行前。
targetは球です。
球のZ軸の向きはワールドのZ軸の向きと同じです。
イメージ説明
 
・LookAtをコメントアウトしてゲームを実行した場合。
その場で、targetの方向に向きました。
currentRotationでは、thisのZ軸の向きを、targetのZ軸の向きに合わせていたかと思うのですが、
thisのZ軸の向きがtargetのZ軸の向きと合っていないというのも、
質問2に関係することかと思いますが、わかりません。
イメージ説明

・LookAtを有効にした場合。
図のオレンジ色の矢印の軌跡をゆっくり描いて最終的に図のようになりました。
今度はthisのZ軸の向きがtargetのZ軸の向きに合っているようです。
ただ、thisのZ方向の向きをtargetに向かせるだけの「transform.LookAt(target);」の処理を
有効にしただけのはずなのに、なぜ急にオレンジ色の矢印の軌跡移動をするようになったのかわかりません。
イメージ説明

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

質問1について
そうですね、真上から見たときの自身の向きを次第にターゲットの向いている方向へ向けていく動きになるでしょう。

質問2について
質問2-1はおおむねそんな感じのとらえ方でいいように思います(以前「ローカル座標とワールド座標のベクトルの変換」で申し上げたような珍妙な座標系を使っていない限りは...)。
質問2-2も質問2-1からの類推ということでおっしゃりたいことは何となくイメージできますが、ちょっともやっとした表現ではありますね(「回転軸を基準に」といいましても回転軸は3本ではなく1本ですし、その1本も回転後の基底ベクトル3本のどれかと必ずしも一致するわけではないですし...)。もっと単純に「あるベクトルをあるクォータニオンで回した結果」とか、あるいはベクトルを位置ベクトルと見なして「ある座標系上の点を、その座標系の原点を中心に、その座標系上での回転を表現するクォータニオンを使って回転移動した移動先の位置」とかではどうでしょうか。
currentRotation * Vector3.forwardは要するに「向きを自身の前方向からターゲットの前方向に少しだけ近づけた方向」を指していますので、それに沿ってdistanceの分だけカメラを後退させることになるでしょう。

質問3について
LookAtなしの場合ですが、おそらく画面に映るターゲットの水平位置はごくわずかに画面の水平中心からずれているんじゃないでしょうか?

図1

LookAtによって、このズレの分だけカメラの向きがごくわずかに回転するはずです。これによりカメラの向きが徐々に目標方向に近づいていくでしょう。また、カメラの向きが少しずつ回ることによって次回のtransform.position -= currentRotation * Vector3.forward * distance;でカメラが移動する位置もずれるはずです。

図2

一方、LookAtがないとカメラの向きはいつまで経っても元の向きのままになります。結果としてcurrentRotationも変化せず、transform.position -= currentRotation * Vector3.forward * distance;でカメラが移動する位置も毎回同じになってしまう...ということになりそうです。

図3

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/09/30 22:46 編集

    ベストアンサーを付けさせていただいた後に、また質問で申し訳ないのですが、
    いろいろ弄ってたら気になってしまったのと、自分の理解が正しいか確認させていただきたくなりまして、すみません。
    お時間のあるときにご回答いただけたら幸いです。

    今回のコードに限っては、下記のように
    LerpAngleの戻り値を別の変数に代入しても同じ処理となりますか?

    float currentRotationAngle2 = Mathf.LerpAngle(currentRotationAngle, wantedRotationAngle, rotationDamping * Time.deltaTime);
    Debug.Log(currentRotationAngle2);

    (省略)

    // Convert the angle into a rotation
    var currentRotation = Quaternion.Euler(0, currentRotationAngle2, 0);

    つまり、
    「currentRotationAngle = Mathf.LerpAngle(currentRotationAngle, wantedRotationAngle, rotationDamping * Time.deltaTime);
    は、この戻り値のcurrentRotationAngleをtransform.rotaionに反映させるのならば、
    回転に影響を与えるので(次回のcurrentRotationAngleに反映されるので)、
    イージング処理となるけれども、今回は、transform.positionで位置の反映として使っているので、
    次回のcurrentRotationAngleに反映されないので、
    上記Mathf.LerpAngleはイージング処理になっていない。
    これより、今回は新しい変数currentRotationAngle2に書き換えたものでも、同じ結果の処理になる
    (回転に影響を与えているのはLookAtだけ)」
    という認識で合っていますか?
    コードの形としてはイージング処理っぽいけど、今回のMathf.LerpAngleだけの処理に限っては違うのかなと気になってしまいました。

    ただ、それでも(currentRotationAngle2に書き換えても)イージング処理っぽい動きに見えるのは、
    transform.position -= currentRotation * Vector3.forward * distance;
    の繰り返し処理の位置更新によって、だんだんLookAt(target)で向く角度がtargetに近づいて行って、これにより、transform.eulerAngles.yも、target.eulerAngles.yに近づいていくので、コード全体の結果として回転もイージング処理になっているということでしょうか?

    何度もすみません。

    キャンセル

  • 2019/10/01 04:51

    はい、今回の場合はLerpAngleの結果を受け取るのを別の変数に変えても問題ないはずです。
    後半部分の解釈も妥当だと思います。たとえば「transform.position = Vector3.Lerp(transform.position, targetTransform.position, damping * Time.deltaTime);」のようなシンプルなものでしたら、その一行だけでtransform.positionのイージング処理を表していると言えるでしょうが、今回のケースでは「新しい向きを求める」「新しい向きに基づいてカメラの位置を変更する」「新しい位置に基づいてカメラの回転を変更する(新しい位置からターゲットを注視する)」のステップが複合されることによって、全体としてカメラの位置・回転のイージング処理になっているということですね。

    キャンセル

  • 2019/10/01 21:22

    ご回答ありがとうございます。
    なるほど、ご提示いただいたようなシンプルなコードだとイージング処理ですね。
    理解がより一層深まりました。
    大変ありがとうございます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.52%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる