Unity で UI のドラッグドロップ領域をつくる
※ これは 2020/11/06 時点の Unity 2020.1.12f1 の情報です
最新版では動作が異なる可能性がありますのでご注意ください
前回の UI のマウスドラッグの続き
今度は下記のように、三色の四角 UI を二つの灰色領域間でドラッグドロップで移動させてみたい
まず前回の Item
UI をプレハブ化して3色に色分け、その後、この UI をドロップする領域として Area (A)
と Area (B)
の GameObject
を作る
Area (A)
と Area (B)
は配置位置以外全く同じもので、DropArea.cs
のスクリプトと、HorizontalLayoutGroup
、背景画像の Image
をこんな感じで設定した
DropArea.cs
は単なる目印なので下記の通り空っぽでよし
using UnityEngine; public class DropArea : MonoBehaviour { }
あとは前回の Draggable.cs
を変更
using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.EventSystems; [RequireComponent(typeof(CanvasGroup))] public class Draggable : MonoBehaviour { private Transform root; private Transform area; private Transform self; private CanvasGroup canvasGroup = null; public void Awake() { this.self = this.transform; this.area = this.self.parent; this.root = this.area.parent; this.canvasGroup = this.GetComponent<CanvasGroup>(); } public void OnBeginDrag(BaseEventData eventData) { // ドラッグできるよういったん DropArea の上位に移動する this.self.SetParent(this.root); // UI 機能を一時的無効化 this.canvasGroup.blocksRaycasts = false; } public void OnDrag(BaseEventData eventData) { this.self.localPosition = GetLocalPosition(((PointerEventData)eventData).position, this.transform); } private static Vector3 GetLocalPosition(Vector3 position, Transform transform) { // 画面上の座標 (Screen Point) を RectTransform 上のローカル座標に変換 RectTransformUtility.ScreenPointToLocalPointInRectangle( transform.parent.GetComponent<RectTransform>(), position, Camera.main, out var result); return new Vector3(result.x, result.y, 0); } public void OnEndDrag(BaseEventData eventData) { // ドロップ地点に DropAra があったらそこに入れる var dropArea = GetRaycastArea((PointerEventData)eventData); if (dropArea != null) { this.area = dropArea.transform; } this.self.SetParent(this.area); // UI 機能を復元 this.canvasGroup.blocksRaycasts = true; } /// <summary> /// イベント発生地点の DropArea を取得する /// </summary> /// <param name="eventData">イベントデータ</param> /// <returns>DropArea</returns> private static DropArea GetRaycastArea(PointerEventData eventData) { var results = new List<RaycastResult>(); EventSystem.current.RaycastAll(eventData, results); return results.Select(x => x.gameObject.GetComponent<DropArea>()) .FirstOrDefault(x => x != null); } }
主な変更点は、ドラッグ開始時にいったん DropArea
との親子関係を解消し、終了時にドロップ地点にある DropArea
に対して親子関係を設定するという感じ
EventSystem.current.RaycastAll()
を使えば、マウス位置の重なってる UI を全部取得できるみたいなので、これを利用すれば DropArea
を取得できる
さてお試し
思い通りにできたっぽい・・・何もないところにドロップしたら戻る