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

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

ただいまの
回答率

89.65%

UnityC# TPSカメラの角度が上手に求められず反転してしまう

解決済

回答 2

投稿

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

DIST_

score 13

tpsのゲームのカメラ制御を作っている際に、このようなプログラムを書いていたら

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

public class PlayerCam : MonoBehaviour
{
    public GameObject targetObj;
    public float camRotateHorizontalSpeed;
    public float camRotateVerticalSpeed;
    public float camJoyStickRotateHorizontalSpeed;
    public float camJoyStickRotateVerticntalSpeed;

    public float minVerticalLimit;
    public float maxVerticalLimit;

    public Vector3 defaulltCamPos;
    bool afterSetFirstFrame;
    Vector3 prevTargetPos;

    public bool cursorLocking;

    [SerializeField] GameObject pauseUI;

    // Start is called before the first frame update
    void Start()
    {
        targetObj = null;
        afterSetFirstFrame = true;
        //カーソルをロック
        cursorLocking = true;
        Cursor.lockState = CursorLockMode.Locked;
        Cursor.visible = false;
        pauseUI.SetActive(false);
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            //カーソルロック解除
            Cursor.lockState = CursorLockMode.None;
            Cursor.visible = true;
            cursorLocking = false;
            pauseUI.SetActive(true);
        }

        if (targetObj != null)
        {
            if (afterSetFirstFrame)
            {
                afterSetFirstFrame = false;
                prevTargetPos = targetObj.transform.position;
                transform.position = prevTargetPos + defaulltCamPos;
            }
            else
            {
                //↓ProgressOfCam↓
                //move
                transform.position += targetObj.transform.position - prevTargetPos;
                prevTargetPos = targetObj.transform.position;

                float mouseMoveX = Input.GetAxis("Mouse X");
                float mouseMoveY = Input.GetAxis("Mouse Y");
                float rightStickX = Input.GetAxis("RightStickHorizontal");
                float rightStickY = Input.GetAxis("RightStickVertical");

                if (cursorLocking)
                {
                    //カメラの回転
                    transform.RotateAround(prevTargetPos, Vector3.up, (mouseMoveX + Input.GetAxis("RightStickHorizontal") * camJoyStickRotateHorizontalSpeed) * Time.deltaTime * camRotateHorizontalSpeed);
                    transform.RotateAround(prevTargetPos, transform.right, (mouseMoveY + Input.GetAxis("RightStickVertical") * camJoyStickRotateVerticntalSpeed) * Time.deltaTime * camRotateVerticalSpeed);
                    float fLEAX = fixedLocalEulerAngleX();

                    Debug.Log("MouseX:" + mouseMoveX.ToString() + "MouseY:" + mouseMoveY.ToString() + "StickX:" + rightStickX.ToString() + "StickY:" + rightStickY.ToString() + "RotateX:" + transform.eulerAngles.x.ToString() + "FixedRotateX:" + fLEAX);
                    //!!!!問題点!!!!上限・下限を超えた場合にもとに戻す処理がうまくいかない
                    if (fLEAX < minVerticalLimit || fLEAX > maxVerticalLimit)
                        transform.RotateAround(prevTargetPos, transform.right, 0 - (mouseMoveY + Input.GetAxis("RightStickVertical") * camJoyStickRotateVerticntalSpeed) * Time.deltaTime * camRotateVerticalSpeed);
                }
            }
        }


    }

    //localEulerAngle.xを-90<x<=270に修正
    float fixedLocalEulerAngleX()
    {
        float lEAX = transform.localEulerAngles.x;

        if (lEAX <= 270) return lEAX; else return lEAX - 360;
    }
}


マウスを縦に動かす大きさが大きすぎるとx軸の回転が大きくなりすぎて上下逆になってしまいます。
上限・下限を超した場合の計算がうまくいかないようです。
360°でx軸の角度を求められないのでしょうか?
または反転しないように他の工夫が出来るのでしょうか?

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

+1

Unity の回転と向き - Unity マニュアルの中に...

ただし、スクリプトでオイラー角を使用したほうがよい場合があります。そのような場合は、角度を変数に代入し、オイラー角として回転に適用するためにだけ使用することが重要です。オイラー角をクォータニオンから取得することは可能ですが、取得、変更、再適用すると、問題が発生します。

という記述があります。ご質問者さんのコードは一見するとオイラー角の取得のみ行っており再適用していないため、マニュアルの警告にあるケースには当てはまらないように思われるかもしれませんが、似たような問題が発生しうるでしょう。
角度の範囲制限をなくした状態でカメラを垂直に回転させていきコンソールのRotateXを観察すると、おそらく-90°や90°を越えたところで角度の増減方向が逆になるかと思います。この意図しない角度をもとに範囲制限が行われてしまうと、制限結果も意図しないものになってしまうはずです。

マニュアルに例示されている...

以下は、スクリプトでオイラー角を使う時の正しい使用法です。

         // 正しくオイラー角を用いた回転スクリプト。
         // ここでは、オイラー角をクラス変数として格納します。
         //それをオイラー角として使用しますが、決してオイラー角として読み込みには使いません。
         
         float x;
         void Update () {
         
             x += Time.deltaTime * 10;
             transform.rotation = Quaternion.Euler(x,0,0);
 
         }

に似せた形に変えて、下記のように制限を加えてみるのはいかがでしょうか?

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

public class PlayerCam : MonoBehaviour
{
    public GameObject targetObj;
    public float camRotateHorizontalSpeed;
    public float camRotateVerticalSpeed;
    public float camJoyStickRotateHorizontalSpeed;
    public float camJoyStickRotateVerticntalSpeed;

    public float minVerticalLimit;
    public float maxVerticalLimit;

    public Vector3 defaulltCamPos;
    bool afterSetFirstFrame;
    Vector3 prevTargetPos;

    public bool cursorLocking;

    [SerializeField] GameObject pauseUI;

    // X軸回転角を積算するためのフィールドを追加
    float verticalAngle;

    // Start is called before the first frame update
    void Start()
    {
        targetObj = null;
        afterSetFirstFrame = true;
        //カーソルをロック
        cursorLocking = true;
        Cursor.lockState = CursorLockMode.Locked;
        Cursor.visible = false;
        pauseUI.SetActive(false);
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            //カーソルロック解除
            Cursor.lockState = CursorLockMode.None;
            Cursor.visible = true;
            cursorLocking = false;
            pauseUI.SetActive(true);
        }

        if (targetObj != null)
        {
            if (afterSetFirstFrame)
            {
                afterSetFirstFrame = false;
                prevTargetPos = targetObj.transform.position;
                transform.position = prevTargetPos + defaulltCamPos;

                // 初期X軸周り角度としてカメラのオイラー角Xを代入し
                // これ以降はtransform.localEulerAngles.xは見ないことにする
                verticalAngle = transform.localEulerAngles.x;
            }
            else
            {
                //↓ProgressOfCam↓
                //move
                transform.position += targetObj.transform.position - prevTargetPos;
                prevTargetPos = targetObj.transform.position;

                float mouseMoveX = Input.GetAxis("Mouse X");
                float mouseMoveY = Input.GetAxis("Mouse Y");
                float rightStickX = Input.GetAxis("RightStickHorizontal");
                float rightStickY = Input.GetAxis("RightStickVertical");

                if (cursorLocking)
                {
                    //カメラの回転
                    // 水平操作はそのままでいいとして...
                    transform.RotateAround(prevTargetPos, Vector3.up, (mouseMoveX + Input.GetAxis("RightStickHorizontal") * camJoyStickRotateHorizontalSpeed) * Time.deltaTime * camRotateHorizontalSpeed);

                    // 垂直操作を少し修正
                    // 入力された垂直回転角を...
                    float verticalAngleDelta = (mouseMoveY + Input.GetAxis("RightStickVertical") * camJoyStickRotateVerticntalSpeed) * Time.deltaTime * camRotateVerticalSpeed;

                    // verticalAngleと足し合わせ、nextVerticalAngleとし...
                    float nextVerticalAngle = verticalAngle + verticalAngleDelta;

                    // nextVerticalAngleを制限範囲内にクランプし...
                    nextVerticalAngle = Mathf.Clamp(nextVerticalAngle, minVerticalLimit, maxVerticalLimit);

                    // クランプ後の角度からverticalAngleDeltaを逆算し...
                    verticalAngleDelta = nextVerticalAngle - verticalAngle;

                    // カメラを垂直回転する
                    transform.RotateAround(prevTargetPos, transform.right, verticalAngleDelta);

                    // verticalAngleを新しい角度に更新する
                    verticalAngle = nextVerticalAngle;

                    Debug.Log("MouseX:" + mouseMoveX.ToString() + "MouseY:" + mouseMoveY.ToString() + "StickX:" + rightStickX.ToString() + "StickY:" + rightStickY.ToString() + "RotateX:" + transform.localEulerAngles.x.ToString());
                }
            }
        }
    }

    // fixedLocalEulerAngleXは廃止
    /*
    //localEulerAngle.xを-90<x<=270に修正
    float fixedLocalEulerAngleX()
    {
        float lEAX = transform.localEulerAngles.x;

        if (lEAX <= 270) return lEAX; else return lEAX - 360;
    }
    */
}

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/01/24 17:24

    確かに、自分で計算して用意すれば正確ですね…
    適用してみると確かに反転しなくなりました!
    詳しい解説ありがとうございました!

    キャンセル

+1

あまり正攻法ではありませんが、コードが少なくなる以下の方法はどうでしょう。

using UnityEngine;

public class CameraScript : MonoBehaviour 
{

    public Transform Center;
    // Update is called once per frame
    void Update()
    {
        transform.LookAt(Center);
    }
}
using UnityEngine;

public class RotateCamera : MonoBehaviour
{

    public float Speed=1;
    // Update is called once per frame
    void Update()
    {
        var mouseMoveX = Input.GetAxis("Mouse X");
        var mouseMoveY = Input.GetAxis("Mouse Y");

        transform.Rotate(new Vector3(mouseMoveY, mouseMoveX) * Speed);
    }
}

この2つのスクリプトを作成し、シーン上に以下のように貼り付けます。
イメージ説明
Positionのxが少しずれているのは、その方がゆっくりとした天地反転になってくれるからです。

イメージ説明

これで恐らく上下反転せずに回転するはずです。
キャラクターの位置を動かしたい場合は、PlayerCenterの方を動かしてください

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/01/24 17:22

    なるほど…そんな方法もあったんですね…
    今回はシステムそのままで行きたかったのでベストアンサーにはしませんでしたがとても勉強になりました!
    ありがとうございました!

    キャンセル

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

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