Unity(2D) C#でドラッグで画面を動かす、視点移動する、スライド・スクロールさせる方法

figure-skating IT

イベントトリガーを使ったドラッグ操作で画面をスライド、スクロールさせる方法です。ちょっとよくわかってない部分もあり、自己記録気味で書いてますが、どなたかの参考になれば幸いです。

スポンサーリンク

成果物

ドラッグするとカメラが画面内で動きます。

下記のコードをイベントトリガー(Drag)をつけたテキストにアタッチしています。
また、キャンバスのレンダーモードスクリーンスペース-カメラである必要があります。

//Unity C#

public class mouseDrag : MonoBehaviour
{

    Vector3 localPosition;
    Vector3 position;
    float movex;
    float movey;

    void Start()
    {
        localPosition = this.transform.position;
        movex = this.transform.position.x;
        movey = this.transform.position.y;
    }

    public void DragTest()
    {
        
        //マウスの座標を取得してスクリーン座標を更新
        Vector3 thisPosition = Input.mousePosition;
        //スクリーン座標→ワールド座標
        Vector3 worldPosition = Camera.main.ScreenToWorldPoint(thisPosition);
        //ワールド座標→ローカル座標
        Vector3 localPosition = this.transform.InverseTransformPoint(worldPosition);
        
        //動きをマイルドに
        movex += (localPosition.x - position.x) / 10;
        movey += (localPosition.y - position.y) / 10;

        //ローカル座標をカメラの位置に代入
    //z軸だけ-10に設定する
        Camera.main.gameObject.transform.position = new Vector3(movex,movey,-10f);
        position = localPosition;
    }

}

以下解説となります。

前準備

初期状態はこうなっており、この真ん中のテキストにイベントトリガーを設定し、スクリプトをアタッチして、コライダーを設置します。

マウスの現在地を取得してカメラの位置に代入(失敗)

//Unity C#
public void DragTest()
{
//マウスの座標を取得
Vector3 thisPosition = Input.mousePosition;
}
マウスの現在地はInput.mousePositionで取得できます。
//Unity C#
public void DragTest()
{
//マウスの座標を取得
Vector3 thisPosition = Input.mousePosition;
Camera.main.gameObject.transform.position = new Vector3(thisPosition.x,thisPosition.y,-10f);
}
取得したマウスの座標はVector3構造体なので、カメラの座標にそのまま代入することができます。
しかしこのコードを実行すると、
動くことは動くんですが、ドラッグした瞬間カメラが全然ちがう位置に吹き飛び、左下までドラッグするとようやく元の位置に戻れる、という状態になります。

スクリーン座標とワールド座標

なぜかというと、イベントトリガーで取得できるのはスクリーン座標だからです。
スクリーン座標はカメラのサイズを基準とし、画面の左下を0,0、サイズが640×480の画面なら画面右上を640,480として位置を指定します。
よってマウスで画面中央をドラッグすると、取得できる座標は300,200とかになります。
ところがこの座標を代入しようとしたカメラでは、座標はカメラ範囲中央を0,0とするワールド座標で位置を指定しています(Transformはワールド座標を指定できるコンポーネント)。
この場合もともと0,0の位置を映していたカメラに200とか300とかの座標を代入することになるので、カメラが右上に吹っ飛ばされてしまいます。

マウスの現在地をスクリーン座標に変換(失敗)

//Unity C#
public void DragTest()
{
//マウスの座標を取得
Vector3 thisPosition = Input.mousePosition;
//スクリーン座標→ワールド座標
Vector3 worldPosition = Camera.main.ScreenToWorldPoint(thisPosition);
Camera.main.gameObject.transform.position = new Vector3(worldPosition.x,worldPosition.y,-10f);
}
であれば、取得したスクリーン座標をワールド座標に変換してやればちゃんと動きそうです。
調べるとCamera.main.ScreenToWorldPoint(スクリーン座標)という命令でスクリーン座標→ワールド座標への変換が可能なようなので、さっそく試してみることにします。これはいけるはず。
いけません。初期位置こそ正しいものの、まったくドラッグと合わない幻惑的な動きをします。
一応z軸が原因っぽいんですが、直せなかったので次の手に移ります。

スクリーン座標をローカル座標に変換(成功?)

ローカル座標

調べていくと、Unityにはさらにローカル座標というものもあるようです。
ローカル座標は親に対しての相対的な位置を示す座標で、Rect Transformはローカル座標を指定できるコンポーネントです。どうやら取得した座標は、このローカル座標に変換してやるのが良いようです。
//Unity C#
public void DragTest()
{
//マウスの座標を取得
Vector3 thisPosition = Input.mousePosition;
//スクリーン座標→ワールド座標
Vector3 worldPosition = Camera.main.ScreenToWorldPoint(thisPosition);
//ワールド座標→ローカル座標(Canvasのレンダーモードがスクリーンスペース-カメラの場合)
Vector3 localPosition = this.transform.InverseTransformPoint(worldPosition);
Camera.main.gameObject.transform.position = new Vector3(localPosition.x, localPosition.y, -10f);
}
ワールド座標からローカル座標へは、ローカル座標.InverseTransformPoint(ワールド座標)という命令で変換できます(※Canvasのレンダーモードがスクリーンスペース-カメラの場合)。
今回はアタッチしたテキスト自身、this.transformがローカル座標となります。
変換したワールド座標をさらにローカル座標に変換してやり、改めてカメラに代入します。
実行結果です。若干素早いですが、一応目的の状態は達成できたようです。

動きをマイルドにする(成功)

//Unity C#
public class mouseDrag : MonoBehaviour
{
Vector3 localPosition;
Vector3 position;
float movex;
float movey;
ここで終わりたいのはやまやまなんですが、動きをゆるやかにするスクリプトも実装します。
クラス頭でいくつか変数を用意しておきます。localPositionがカメラへの位置の代入に使う変数で、positionが位置を変更する前の座標を記録しておく変数です。
//Unity C#
void Start()
{
localPosition = this.transform.position;
movex = this.transform.position.x;
movey = this.transform.position.y;
}
アタッチしたオブジェクト=テキストの現在地を、初期値として一度カメラの位置に使う変数に代入しておきます。
//Unity C#
//ワールド座標→ローカル座標
Vector3 localPosition = this.transform.InverseTransformPoint(worldPosition);
//ローカル座標をカメラの位置に代入
//動きをマイルドに
movex += (localPosition.x - position.x) / 10;
movey += (localPosition.y - position.y) / 10;
Camera.main.gameObject.transform.position = new Vector3(movex,movey,-10f);
position = localPosition;
ローカル座標を取得した後、移動したい位置と元の位置の差を1/10にして、現在地に加算した値をそれぞれmovex、moveyという変数に代入します。
この変数をカメラの現在地に使い、最後に更新された現在地を次回使う「元の現在地」に代入します。
……というコードを追加すると冒頭のコードが完成し、大体期待通りの挙動が達成できました。以上です。
やりたいことから逆引きするUNITYの使い方まとめ
Unityをやりたいことから学習していけるよう、機能・用途別にまとめたページです。C#の命令別の逆引きは現時点で作っていません。2019の時期に書き始めているので、それより前のバージョンについては言及しません。

コメント

モバイルバージョンを終了