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

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

ただいまの
回答率

88.03%

カメラをマウスで移動しすぎると反転する

解決済

回答 1

投稿

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

score 17

カメラを主人公のまわりをマウスで移動させるスクリプトを作ったのですがY軸に激しく動かすと反転します。
反転させないようにしたいです!
このスクリプトは画像に乗っているCameraControllerに張り付けたスクリプトです。
public class CameraController : MonoBehaviour {

public GameObject player;
public GameObject mainCamera;
public float rotate_speed;
private const int ROTATE_BUTTON = 1;
private const float ANGLE_LIMIT_UP = 60f;
private const float ANGLE_LIMIT_DOWN = -60f;

void Start() {
mainCamera = Camera.main.gameObject;
player = GameObject.FindGameObjectWithTag("Player");
}

void Update() {
transform.position = player.transform.position;

if (Input.GetMouseButton(ROTATE_BUTTON)) {
rotateCmaeraAngle();
}

float angle_x = 180f <= transform.eulerAngles.x ? transform.eulerAngles.x - 360 :   transform.eulerAngles.x;
transform.eulerAngles = new Vector3(
Mathf.Clamp(angle_x, ANGLE_LIMIT_DOWN, ANGLE_LIMIT_UP),
transform.eulerAngles.y,
transform.eulerAngles.z
);
}
private void rotateCmaeraAngle() {
Vector3 angle = new Vector3(
Input.GetAxis("Mouse X") * rotate_speed,
Input.GetAxis("Mouse Y") * rotate_speed,
0
);

transform.eulerAngles += new Vector3(angle.y, angle.x);
}
}
イメージ説明
イメージ説明
回答くださると幸いです。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

+1

transform.eulerAnglesからオイラー角を取得するというのはちょっと注意が必要ですね。内部的にはQuaternion型からの変換が起こっていますので、予期した角度とは違う角度(具体的に言うと、YやZが180°反転してXの増減方向が逆転した角度)が返ってくることがあるかと思います。縦にすばやくマウスを動かしてX角度が±90°を超えてしまうとこの現象が発生し、異常動作の原因となるでしょう。
以前見かけました他の方のご質問に「UnityC# TPSカメラの角度が上手に求められず反転してしまう」や「[Unity] オブジェクトのRotationの値を変化させたときの挙動について質問」といったものがあり、ご参考になるかもしれません。

現在のコードをなるべく維持したまま上記の問題を回避する一案として、下記のような形はどうでしょうか。

using UnityEngine;

public class CameraController : MonoBehaviour {

    public GameObject player;
    public GameObject mainCamera;
    public float rotate_speed;
    private const int ROTATE_BUTTON = 1;
    private const float ANGLE_LIMIT_UP = 60f;
    private const float ANGLE_LIMIT_DOWN = -60f;

    // クォータニオンとオイラー角の相互変換をせずにすむように、
    // まずオイラー角保存用のフィールドを作り...
    private Vector3 eulerAngles;

    void Start() {
        mainCamera = Camera.main.gameObject;
        player = GameObject.FindGameObjectWithTag("Player");

        // Start時点のtransform.eulerAnglesを保存し、これ以降は
        // transform.eulerAnglesからの値の取得は行わないようにする
        eulerAngles = transform.eulerAngles;
    }

    void Update() {
        transform.position = player.transform.position;

        if (Input.GetMouseButton(ROTATE_BUTTON)) {
            rotateCmaeraAngle();
        }

        float angle_x = 180f <= eulerAngles.x ? eulerAngles.x - 360 : eulerAngles.x;
        eulerAngles = new Vector3(
            Mathf.Clamp(angle_x, ANGLE_LIMIT_DOWN, ANGLE_LIMIT_UP),
            eulerAngles.y,
            eulerAngles.z
        );

        // ここまでの回転制御でtransform.eulerAnglesを使っていた部分は
        // 代わりにeulerAnglesを使うよう変更し、最後にeulerAnglesを
        // transform.eulerAnglesにセットする
        transform.eulerAngles = eulerAngles;
    }

    private void rotateCmaeraAngle() {
        Vector3 angle = new Vector3(
            Input.GetAxis("Mouse X") * rotate_speed,
            Input.GetAxis("Mouse Y") * rotate_speed,
            0
        );

        eulerAngles += new Vector3(angle.y, angle.x);
    }
}

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/12/24 18:53

    なるほど(よくわかってない)
    ただ少しづつ理解できるように頑張りたいと思います!
    丁寧に教えていただきありがとうございます!

    キャンセル

  • 2019/12/24 20:11 編集

    なんども質問してすみません!
    元々カメラでターゲット機能をつけていたので上記のようにスクリプトをつけたらそれらが機能しなくなりました。
    どうすればよいでしょうか?
    private LockOnTargetDetector lockOnTargetDetector;
    [SerializeField]
    public GameObject lockOnTarget;

    void Start() {
    mainCamera = Camera.main.gameObject;
    player = GameObject.FindGameObjectWithTag("Player");
    lockOnTargetDetector = player.GetComponentInChildren<LockOnTargetDetector>();
    }

    void Update() {
    transform.position = player.transform.position;

    if (Input.GetKeyDown(KeyCode.R)) {
    GameObject target = lockOnTargetDetector.getTarget();

    if (target != null) {
    lockOnTarget = target;
    } else {
    lockOnTarget = null;
    }
    }

    if (lockOnTarget) {
    lockOnTargetObject(lockOnTarget);
    } else {
    if (Input.GetMouseButton(ROTATE_BUTTON)) {
    rotateCmaeraAngle();
    }
    }

    float angle_x = 180f <= transform.eulerAngles.x ? transform.eulerAngles.x - 360 : transform.eulerAngles.x;
    transform.eulerAngles = new Vector3(
    Mathf.Clamp(angle_x, ANGLE_LIMIT_DOWN, ANGLE_LIMIT_UP),
    transform.eulerAngles.y,
    transform.eulerAngles.z
    );
    }

    キャンセル

  • 2019/12/25 06:01

    なるほど、ロックオン機能もあったのですね。
    当初投稿したコードでは、Start内のコメントに「これ以降はtransform.eulerAnglesからの値の取得は行わないようにする」などと書いておきましたが、それを曲げてロックオン直後(transform.LookAt使用直後)にも...

    private void lockOnTargetObject(GameObject target) {
    transform.LookAt(target.transform, Vector3.up);
    eulerAngles = transform.eulerAngles;
    }

    といった具合にtransform.eulerAnglesから角度を再取得してeulerAnglesを更新してみるとどうでしょうか。

    実際のところスクリプト全域でtransform.eulerAnglesの再取得を禁止しなくても、カメラのフリー回転処理の過程で「transform.eulerAnglesに大きく飛んだ角度が設定される」→「transform.eulerAnglesから意図しない角度が取得され、それを使って角度制限される」という現象が起こらないようにすれば十分なように思います。
    もしたくさんの場所でtransform.LookAtのようなtransformの回転に影響を与える操作をしている場合、フリー回転処理だけを単独のメソッドにまとめてしまい(現状だと「rotateCmaeraAngle」と「X回転制限部分」の2カ所に分散していますよね)、そのメソッドの先頭部分でtransform.eulerAnglesの再取得をしてもいいでしょう。

    キャンセル

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

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

関連した質問

同じタグがついた質問を見る