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

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

ただいまの
回答率

88.80%

Unityエディタのゲーム画面で、マウスの移動量とスクリーンのサイズの関係が合わない。

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,086

tkmnusr

score 319

 前提・実現したいこと

こちらのサイトこちらのサイトを参考に、
Unityエディタのゲーム画面で端から端までのマウス移動で90度回転するように実装したつもりですが、
実際にゲームを実行してみると、かなり少ないマウス移動で何回転もしてしまいます。
該当のソースにおいて、何がおかしいのでしょうか?
ご教示お願い致します。

 該当のソースコード

    Vector2 beganPos;
    float mouseDeltaPositionX, mouseDeltaPositionY;
    float xAngle, yAngle, zAngle = 0;

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            beganPos = Input.mousePosition;
        }
        else if (Input.GetMouseButton(0))
        {

            mouseDeltaPositionX = (Input.mousePosition.x - beganPos.x)/Screen.width;
            mouseDeltaPositionY = (Input.mousePosition.y - beganPos.y)/Screen.height;

            float xAngle = mouseDeltaPositionY*90;
            float yAngle = -mouseDeltaPositionX*90;

            //回転
            this.transform.Rotate(xAngle, yAngle, zAngle, Space.World);
        }
    }

 追記。

タッチの場合は、なぜRotateで実装できたのでしょうか?
可能ならば、なるべくこれに近いコード(Rotateを使ったコード)で、エディタの方も実装したいです。

            int touchCount = Input.touches.Count(t => t.phase != TouchPhase.Ended && t.phase != TouchPhase.Canceled);

            if (touchCount == 1)
            {
                Touch t = Input.touches.First();
                switch (t.phase)
                {
                    case TouchPhase.Moved:

                        xAngle = (t.deltaPosition.y/Screen.height)*90;
                        yAngle = -(t.deltaPosition.x/Screen.width)*90;
                        break;
                }
                this.transform.Rotate(xAngle, yAngle, zAngle, Space.World);
            }

 MEMO

タッチのdeltaPositionを、マウスで表現すると下記のようなmouseDeltaPositionになる。

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            prevPos = Input.mousePosition;
        }
        else if (Input.GetMouseButton(0))
        {
            mouseDeltaPosition = Input.mousePosition - prevPos;

            //ポインタ位置差分を求めた後で、現在のポインタ位置をprevPosに代入し、次回のUpdate実行に備える。
            //この「現在のポインタ位置」が、次フレームでは「前フレームのポインタ位置」となる。
            prevPos = Input.mousePosition;

            (省略)
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

+1

Rotateは、現在のtransformの回転状態に対してさらに回転を加えるような動作をしますので、例えば下記のような状況になってしまうでしょう。

  • 画面上端中央でマウスボタンを押し下げる。
  • そのまま真下にドラッグし、画面中央でポインタを止める。ボタンは押しっぱなしにしておく。
  • このとき、mouseDeltaPositionY-0.5を示し、xAngle-45.0を示す。
    つまり、毎フレームthis.transform.Rotate(-45, 0, 0, Space.World);が実行される。
    マウスポインタを画面中央から動かさなくてもxAngle-45.0を示し続けるので、オブジェクトは-45°/フレームの速度で回り続ける。

対処方法の一例として、マウスボタンを押し下げたときに、初期マウス座標に加えてオブジェクトの初期回転を覚えておくというのはどうでしょうか。そして、オブジェクトにセットする新しい回転は「初期回転に対して(xAngle, yAngle, zAngle)回転したもの」としてみます。

    Vector2 beganPos;
    Quaternion beganRot; // オブジェクトの初期姿勢を覚えておくフィールドを追加
    float mouseDeltaPositionX, mouseDeltaPositionY;
    float xAngle, yAngle, zAngle = 0;

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            beganPos = Input.mousePosition;
            beganRot = this.transform.rotation; // ボタン押し下げ時の回転を初期姿勢として保存しておく
        }
        else if (Input.GetMouseButton(0))
        {
            mouseDeltaPositionX = (Input.mousePosition.x - beganPos.x) / Screen.width;
            mouseDeltaPositionY = (Input.mousePosition.y - beganPos.y) / Screen.height;

            float xAngle = mouseDeltaPositionY * 90;
            float yAngle = -mouseDeltaPositionX * 90;

            //回転
            // Rotateだと基準姿勢が現在の姿勢となってしまう...そこで回転方法を変える
            // 基準となる姿勢は初期姿勢として、そこからさらに(xAngle, yAngle, zAngle)回転し現在姿勢とする
            this.transform.rotation = Quaternion.Euler(xAngle, yAngle, zAngle) * beganRot;
        }
    }

追記

タッチ用コードではdeltaPositionを使っているのがポイントですね。
上で申しあげたように、Rotateは現在の姿勢を基準に回転を追加する動作となります。deltaPositionは「前フレームの指の位置からの差分」(「タッチを開始した位置からの差分」ではない点にご注意ください)を示しており、これをもとに求めた角度を使ってRotateで回しているのでうまくいっていると考えられます。
最初に挙げた例と同様に考えると...

  • 画面上端中央にタッチする。
  • そのまま真下にすばやくスワイプし(1フレームで一気に動かしたものとご想像ください)、画面中央で指を止める。
    deltaPositionには指の移動量が入っている。画面高さの半分の距離を移動したので、求めたmouseDeltaPositionY-0.5xAngle-45.0となる。
    this.transform.Rotate(-45, 0, 0, Space.World);が実行され、45°回転する。
  • このまま画面中央から指を動かさなかった場合、次のフレームのdeltaPositionは移動なしのためゼロベクトルを示す。
    求められる回転角も0°なので、そのまま指を動かさず触れ続けていても、先の例のように回転し続けてしまうことはない。

マウス版コードの書き換え案ですが、こちらもタッチ版と同じように、角度算出の基となるポインタ座標差分を「ドラッグ開始時の位置からの差分」ではなく「前フレームの位置からの差分」に変更すればRotateを使用できるはずです。
下記のような形ではいかがでしょうか?

    Vector2 prevPos; // 覚えておく座標を「マウスボタン押し下げ時の座標」から「前回の座標」に変更...フィールド名を使用目的に沿った名前に変えた
    float mouseDeltaPositionX, mouseDeltaPositionY;
    float xAngle, yAngle, zAngle = 0;

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            // フィールド名が「prevPos」に変わっただけで、ボタン押し下げ時に最初の座標を覚えておくのは変わらない
            prevPos = Input.mousePosition;
        }
        else if (Input.GetMouseButton(0))
        {
            // ここも同じく名前が「prevPos」に変わっただけだが、意味合いとしては「前フレームとのポインタ位置差分を求める」となる
            mouseDeltaPositionX = (Input.mousePosition.x - prevPos.x) / Screen.width;
            mouseDeltaPositionY = (Input.mousePosition.y - prevPos.y) / Screen.height;

            // ポインタ位置差分を求めた後で、現在のポインタ位置をprevPosに代入し、次回のUpdate実行に備える
            // この「現在のポインタ位置」が、次フレームでは「前フレームのポインタ位置」となる
            prevPos = Input.mousePosition;

            // 以降の処理は変更なし

            float xAngle = mouseDeltaPositionY * 90;
            float yAngle = -mouseDeltaPositionX * 90;

            //回転
            this.transform.Rotate(xAngle, yAngle, zAngle, Space.World);
        }
    }

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/11/04 21:09

    ご回答ありがとうございます。
    タッチのdeltaPositionは、「タッチを開始した位置からの差分」ではなく、「前フレームの指の位置からの差分」だったのですね。
    勘違いしていました。
    ご回答いただけた方法で実装できました。
    ありがとうございます。
    メモとして追記したのですが、
    タッチのdeltaPositionを、マウスのmouseDeltaPositionで表すコードを書いてみましたが、合っているかご確認いただけませんか?

    キャンセル

  • 2018/11/04 21:35

    はい、ご提示のコードで問題ないかと思います。
    今回のような、前回の値を覚えておいて差分をとって1フレームの変化量を調べるという手は、座標に限らず色々な場面で使い道がありそうです。

    キャンセル

  • 2018/11/04 21:47

    ご回答ありがとうございます。
    とても勉強になりました。
    ありがとうございます。

    キャンセル

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

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

関連した質問

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