【Unity】ドラックアンドドロップでゲームオブジェクトを動かす

当サイトで紹介する商品・サービス等の外部リンクは、アフィリエイト広告を含む場合があります。
スポンサーリンク
本記事を読むと以下の実行ができます

ドラックアンドドロップ(drag and drop)でゲームオブジェクトを動かします。

実装例

ゲーム内でユーザーがドラッグ&ドロップ機能を使う場面は、プレイヤーがアイテムを移動・装備させる際や、UI要素をカスタマイズして配置し直す際、ゲーム内の建設やレベルデザインでオブジェクトを配置する際などがあります。

ドラッグ&ドロップは実際にトランプやカードゲームなどで使用されてユーザーが扱いやすく、ゲーム開発の幅も広がるので習得したい機能の一つです。

本記事では、ゲームオブジェクトやCanvas全体、各UIの場合に分けてドラッグ&ドロップを実装します。

本記事は次の人におすすめ
  • Unityでドラッグアンドドロップを使用したい。
  • UIを画面に表示したい。
Udemyで学習する
スポンサーリンク

ドラッグアンドドロップ

Unityではドラッグ&ドロップする方法として2種類あります。それは、「Camera.main.ScreenToWorldPoint()」を使う方法とイベントシステムを使って、ポインターのアクションでオブジェクトを呼び出す方法です。

Camera.main.ScreenToWorldPoint()は画面上の座標(スクリーン座標)を、ワールド空間の座標に変換するために使います。Unityで2D・3Dゲームを作るとき、マウスの位置やUIの位置などを、ゲーム空間内の位置に対応させるのによく使われます。

イベントシステムの一部で例えば「IPointerDownHandler」を使えば、ポインターがオブジェクトを押下したときに処理が呼び出されます。
参考:「サポートされているイベント

Camera.main.ScreenToWorldPoint()

初めにCamera.main.ScreenToWorldPoint()を使ったドラッグ&ドロップを紹介します。

ゲームオブジェクトをユーザーのマウス入力から稼働できるようにします。
Hierarchyウィンドウから「2DObject>Sprites」で1つ選択します。

サンプル

Gameオブジェクトが画面上に表示されない場合は、
CanvasとMain Cameraを調節する必要があります。

Render Modeについて解説しています。

ソースコード

using UnityEngine;

public class DragObject : MonoBehaviour
{
    private Vector3 offset;
    private bool isDragging = false;

    void OnMouseDown()
    {
        Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
        offset = transform.position - mousePosition;
        isDragging = true;
    }

    void OnMouseDrag()
    {
        if (isDragging)
        {
            Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
            transform.position = mousePosition + offset;
        }
    }

    void OnMouseUp()
    {
        isDragging = false;
    }
}

解説

  • 名前空間

UnityEngine:Unityエンジンのクラスやメソッドにアクセスするために必要です。
ゲームオブジェクトの操作やユーザー入力の処理など、Unityの基本機能を利用できます。

  • フィールド(メンバ変数)

「offset」:オブジェクトの位置とマウスの位置の差分を保持します。差分を用いて、ドラッグ中のオブジェクトがマウスの位置に対して正しく動くようにします。
「isDragging」:オブジェクトが現在ドラッグ中であるかどうかを判定するフラグです。ドラッグ開始時にtrueに設定され、ドラッグ終了時にfalseに設定されます。

  • OnMouseDownメソッド

マウスボタンが押されたときに呼び出されます。
マウスのスクリーン座標をワールド座標に変換し、その位置とオブジェクトの現在の位置との差分(オフセット)を計算して保存します。また、isDraggingフラグをtrueに設定してドラッグを開始します。

  • OnMouseDragメソッド

マウスがドラッグされている間に毎フレーム呼び出されます。
isDraggingフラグがtrueの場合、現在のマウスのスクリーン座標をワールド座標に変換し、オフセットを加算してオブジェクトの新しい位置を計算し、その位置にオブジェクトを移動させます。

  • OnMouseUpメソッド

マウスボタンが離されたときに呼び出されます。
isDraggingフラグをfalseに設定してドラッグを終了します。

実装

スクリプトを対象のゲームオブジェクトにアタッチします。
さらに、「Add Component」からCollider2Dと入力して、いずれか1つを追加します。
本製作では、「Box Collider2D」を採用しました。

Box Collider2D

プロジェクトを再生すると、Gameオブジェクトが動きます。

UIやCanvasをドラッグ&ドロップ

次にイベントシステムを使ったドラッグ&ドロップを紹介します。

Canvasは、ボタンやテキストなどUIを使用するときに表示されます。それをユーザーのマウス入力から稼働できるようにします。

Hierarchyウィンドウから「UI」を選択します。

ソースコード

using UnityEngine;
using UnityEngine.EventSystems;

public class CanvasDragAndDrop : MonoBehaviour, IPointerDownHandler, IDragHandler, IPointerUpHandler
{
    private RectTransform canvasRectTransform;
    private bool isDragging = false;
    private Vector2 offset;
    private RectTransform draggedObject;

    void Start()
    {
        canvasRectTransform = GetComponentInParent<Canvas>().GetComponent<RectTransform>();
    }

    public void OnPointerDown(PointerEventData eventData)
    {
        draggedObject = eventData.pointerCurrentRaycast.gameObject.GetComponent<RectTransform>();
        if (draggedObject != null)
        {
            Vector2 localPointerPosition;
            if (RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRectTransform, eventData.position, eventData.pressEventCamera, out localPointerPosition))
            {
                offset = draggedObject.anchoredPosition - localPointerPosition;
                isDragging = true;
            }
        }
    }

    public void OnDrag(PointerEventData eventData)
    {
        if (isDragging && draggedObject != null)
        {
            Vector2 localPointerPosition;
            if (RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRectTransform, eventData.position, eventData.pressEventCamera, out localPointerPosition))
            {
                draggedObject.anchoredPosition = localPointerPosition + offset;
            }
            Debug.Log(localPointerPosition);
        }
    }

    public void OnPointerUp(PointerEventData eventData)
    {
        isDragging = false;
        draggedObject = null;
    }
}

解説

  • 名前空間

「UnityEngine」:Unityエンジンの基本機能にアクセスするために必要です。RectTransform、Canvasなど、UI要素を操作するために使用されます。
「UnityEngine.EventSystems」:UnityのUIイベントシステムを構成するインターフェースやクラスが含まれていて、ユーザーの操作(ドラッグ、クリックなど)に応答するためのインターフェース(IPointerDownHandler, IDragHandler, IPointerUpHandler)が対象です。
インターフェースを実装しており、UI要素のドラッグ&ドロップ機能を実現しています。

  • フィールド(メンバ変数)

「canvasRectTransform」:このスクリプトがアタッチされているCanvasのRectTransformを保持します。UI要素の位置計算に使用されます。
「isDragging」:現在オブジェクトがドラッグされているかどうかを示すフラグです。
「offset」:ドラッグ開始時のオブジェクトのローカル座標とマウスの位置との差分を保持します。これにより、オブジェクトが正確にマウスの移動に追従します。
「draggedObject」:現在ドラッグされているUI要素のRectTransformを保持します。

  • Startメソッド

親のCanvasからRectTransformを取得して、canvasRectTransformに保存します。

  • OnPointerDownメソッド

マウスボタンが押されたときに呼び出されるメソッド(IPointerDownHandlerインターフェースの実装)。
ドラッグされたUI要素のRectTransformを取得し、ドラッグ開始時のオフセットを計算して保存します。

  • OnDragメソッド

マウスがドラッグされている間に呼び出されるメソッド(IDragHandlerインターフェースの実装)。
現在のマウスの位置をCanvas内のローカル座標に変換し、オフセットを加算してUI要素を移動させます。

  • OnPointerUpメソッド

マウスボタンが離されたときに呼び出されるメソッド(IPointerUpHandlerインターフェースの実装)。
ドラッグフラグをfalseに設定し、ドラッグされているUI要素の参照をクリアします。

実装

スクリプトをCanvasにアタッチします。
これにより、子オブジェクトのUIはドラッグ&ドロップの対象になります。

UIのドラッグ&ドロップ可能

各UIをドラッグ&ドロップで移動

Canvas(UI全体)を動かせるようにしてしまうと、UIボタンやテキストなど全てのオブジェクトが対象になってしまいます。

例えば、文字のUIまで動かせてしまうと、ゲームとして機能が崩壊してしまうので動かせるUIと動かないUIを分けるようにします。

動かさないUIも動いてしまう。

したがって、動かしたいオブジェクトのみに処理が適用されるスクリプトが必要です。

ソースコード

using UnityEngine;
using UnityEngine.EventSystems;

public class UIDragAndDrop : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
    private RectTransform rectTransform;
    private Canvas canvas;
    private CanvasGroup canvasGroup;

    private void Awake()
    {
        rectTransform = GetComponent<RectTransform>();
        canvas = GetComponentInParent<Canvas>();
        canvasGroup = GetComponent<CanvasGroup>();
    }

    public void OnBeginDrag(PointerEventData eventData)
    {
        if (canvasGroup != null)
        {
            canvasGroup.alpha = 0.6f; // 半透明にする
            canvasGroup.blocksRaycasts = false; // ドラッグ中にレイキャストを無効にする
        }
    }

    public void OnDrag(PointerEventData eventData)
    {
        if (canvas != null)
        {
            rectTransform.anchoredPosition += eventData.delta / canvas.scaleFactor;
        }
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        if (canvasGroup != null)
        {
            canvasGroup.alpha = 1f; // 元に戻す
            canvasGroup.blocksRaycasts = true; // レイキャストを有効に戻す
        }
    }
}

解説

  • フィールド(メンバ変数)

「rectTransform」:UI要素のRectTransformを保持します。RectTransformは、UI要素の位置やサイズを管理するために使用されます。
「canvas」:UI要素が含まれているCanvasを保持します。
CanvasはUI要素の表示とレイアウトを管理するためのコンテナです。
「canvasGroup」:UI要素にアタッチされているCanvasGroupを保持します。
CanvasGroupは、UI要素の透明度やレイキャストのブロックなどの設定を管理します。

  • OnBeginDragメソッド

ドラッグ操作が開始されたときに呼び出されます(IBeginDragHandlerインターフェースの実装)。
もしCanvasGroupがアタッチされている場合、そのCanvasGroupの透明度を0.6に設定して半透明にし、レイキャストを無効にします。
ドラッグ中に他のUI要素によるクリックなどが干渉しないようにします。

  • OnDragメソッド

ドラッグ中に毎フレーム呼び出されます(IDragHandlerインターフェースの実装)。
ドラッグされているUI要素のRectTransformの位置を、マウスの移動に追従させます。
canvasのscaleFactorを使って、UIの拡大縮小に対応させています。

  • OnEndDragメソッド

ドラッグ操作が終了したときに呼び出されます(IEndDragHandlerインターフェースの実装)。
CanvasGroupがアタッチされている場合、そのCanvasGroupの透明度を元に戻して(1に設定)、レイキャストを再度有効にします。
ドラッグが終了した後のUI要素の挙動を正常に戻します。

実装

動かしたいUI(この記事では「Image」)にスクリプトをアタッチします。
これにより、正方形のオブジェクトはマウスで稼働しますが、テキストは操作されません。

まとめ

Unityでドラッグアンドドロップするには、Camera.main.ScreenToWorldPoint()を使う方法とイベントシステムを使う方法があります。

特定のUIを動かしたい場合は、IPointerDownHandler, IDragHandler, IPointerUpHandlerなどを使用すると、マウスを入力しているとき、離したときなどの処理を実装できます。

Udemyで学習する

タイトルとURLをコピーしました