※ これは 2020/11/20 時点の Unity 2020.1.14f1 の情報です
最新版では動作が異なる可能性がありますのでご注意ください
前回の UI のドラッグ移動の続き
2つのドロップ位置によって挿入位置が入れ替わるようにしたが、どのあたりに挿入されるかわかりにくいので、ドラッグ中にもプレースホルダーとして挿入位置にスペースが入るようにしたい
スポンサードリンク
やったことはコード変更のみ
using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; [RequireComponent(typeof(CanvasGroup))] public class Draggable : MonoBehaviour { private Transform root; private Transform area; private Transform self; private CanvasGroup canvasGroup = null; private GameObject placeholder = 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) { // プレースホルダー生成 this.placeholder = new GameObject(); var element = this.placeholder.AddComponent<LayoutElement>(); element.preferredWidth = 100f; element.preferredHeight = 100f; // 元の場所にプレースホルダーを残す this.placeholder.transform.SetParent(this.area); this.placeholder.transform.SetSiblingIndex(this.self.GetSiblingIndex()); // ドラッグできるよういったん 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); AddToArea(this.placeholder.transform, this.self.position.x, (PointerEventData)eventData); } 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); } private static void AddToArea(Transform item, float x, PointerEventData eventData) { // 座標位置に DropArea があったら挿入対象にする var dropArea = GetRaycastArea(eventData); if (dropArea == null) { return; } // 挿入位置を x 座標から計算 var area = dropArea.transform; var index = area.childCount; for (var i = 0; i < area.childCount; i++) { if (x < area.GetChild(i).position.x) { index = i; if (item.GetSiblingIndex() < index) index--; break; } } item.SetParent(area); item.SetSiblingIndex(index); } public void OnEndDrag(BaseEventData eventData) { // プレースホルダーを破棄 Destroy(this.placeholder); // DropArea 移動 AddToArea(this.self, this.self.position.x, (PointerEventData)eventData); this.area = this.self.parent; // 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); } }
ドラッグ開始時に UI と同じサイズになるよう LayoutElement
を張り付けた GameObject
を生成して、挿入予想位置に差し込むようにして、ドラッグ終了時にプレースホルダーは破棄して UI と入れ替えるだけ
お試し
モーセの葦の海の奇跡のように、挿入位置がパカッと割れました!