2015年8月6日木曜日

【Unity】 マウスでクリックした方向になめらかに向いてほしい

タイトルの通りです。バージョンはUnity5で言語はC#。



想定している状況

図1のように、画面の真ん中に対象の物体がくる状況を想定しています。位置はどこでもいいのですが、カメラに対してRotation(x, y, z)が(0, 0, 0)となることが条件なので汎用性は無いです。


図1 想定している状況


例1 改良前

マウスの左ボタンを押している間マウスカーソルの方を向いてくれて、ボタンを離すと元の姿勢に戻るような動作を目指します。

今回は Transform.LookAt() を使わないで、Transform.Rotate() で物体の向きを変えることで、あたかもマウスカーソルを追っているような感じを出したいと思います。

なお、スクリプト内の変数horizontalRangeverticalRangeはそれぞれ水平・垂直方向にどれくらい首をふるかの程度を決めるためのもので、イメージとしては図2に示す感じです。(単位は [°] で、あんまり大きな値を入れるとよくない)


図2 各範囲の変数について


以下のスクリプトを対象物体にアタッチします。

Test.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
using UnityEngine;
using System.Collections;
 
public class Test : MonoBehaviour {
 
    // 元の姿勢
    private Vector3 defaultAngles;
 
    // 水平・垂直方向の首ふりをする範囲
    public float horizontalRange = 120f;
    public float verticalRange = 90f;
 
    void Start () {
        // 開始時の姿勢を元の姿勢として保存
        defaultAngles = transform.eulerAngles;
    }
     
    void Update () {
        // マウス左ボタンを押している間、マウスカーソルの方を向く
        if (Input.GetMouseButton(0)) {
            Vector3 targetAngles = new Vector3(
                (Input.mousePosition.y / Screen.height) * verticalRange - (verticalRange / 2f),
                (Input.mousePosition.x / Screen.width) * -horizontalRange + (horizontalRange / 2f),
                0f
            );
            transform.eulerAngles = targetAngles;
        }
        // 左ボタンを離したら元の姿勢に戻る
        if (Input.GetMouseButtonUp(0)) {
            transform.eulerAngles = defaultAngles;
        }
    }
}

実行結果

 



オレンジの丸は別のスクリプトで動かしているもので、マウスの左ボタンを押している間表示されます。

ちゃんとマウスカーソルを追ってくれていますが、最初に向くときと元に戻るときにパッと切り替わってしまうのが気になります。そこで、切り替えがなめらかになるようにちょっと手を加えてみました。


例2 改良後

Test.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
using UnityEngine;
using System.Collections;
 
public class Test : MonoBehaviour {
 
    // 元の姿勢
    private Vector3 defaultAngles;
 
    // 水平・垂直方向の首ふりをする範囲
    public float horizontalRange = 120f;
    public float verticalRange = 90f;
 
    void Start () {
        // 開始時の姿勢を元の姿勢として保存
        defaultAngles = transform.eulerAngles;
    }
     
    void Update () {
        // マウス左ボタンを押している間マウスカーソルの方を向く
        if (Input.GetMouseButton(0)) {
            Vector3 targetAngles = new Vector3(
                (Input.mousePosition.x / Screen.width) * -horizontalRange + (horizontalRange / 2f),
                (Input.mousePosition.y / Screen.height) * verticalRange - (verticalRange / 2f),
                0f
            );
            transform.Rotate(new Vector3(
                (targetAngles.y - CorrectAngle(transform.eulerAngles.x)) * 10f * Time.deltaTime,
                (targetAngles.x - CorrectAngle(transform.eulerAngles.y)) * 10f * Time.deltaTime,
                0f
            ));
        }
        // 左ボタンを押していない時は元の姿勢に戻ろうとする
        else {
            transform.Rotate(new Vector3(
                (defaultAngles.x - CorrectAngle(transform.eulerAngles.x)) * 5f * Time.deltaTime,
                (defaultAngles.y - CorrectAngle(transform.eulerAngles.y)) * 5f * Time.deltaTime,
                (defaultAngles.z - CorrectAngle(transform.eulerAngles.z)) * 5f * Time.deltaTime
            ));
        }
    }
 
    // 0°~360°を-180°~180°にする
    static float CorrectAngle(float angle) {
        return angle > 180f ? angle - 360f : angle;
    }
}

実行結果

 


向き始めと元の姿勢に戻る動作がなめらかになりました。

目標となる姿勢と現在の姿勢の差をとって、その差の分を操作量とするような比例制御っぽい処理を行っています。その処理をするにあたって、例えば0°を10°下回ると350°になるのは都合が悪いので、0°~360°を-180°~180°に変換する関数を追加しました。

おわりに

状況が大きく異なる場合、ちょっと書き直す必要がでてくるコードですが参考までに。

0 件のコメント:

コメントを投稿