Unity 從UI中拖拽對象放置並拖動效果 附demo
需求:點擊UI,在場景中生成3D對象,對象跟隨鼠標移動,放置後可再次拖拽對象,改變其位置。做瞭一個小Demo,如下圖所示:
實現大致思路:
- 射線碰撞檢測
- 對象空間坐標變換(世界坐標->屏幕坐標、屏幕坐標->世界坐標)
首先為要生成3D對象的UI添加一個鼠標監聽事件,腳本如下:
SelectImage.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.EventSystems; public class SelectImage : MonoBehaviour,IPointerDownHandler{ //需要被實例化的預制 public GameObject inistatePrefab; //實例化後的對象 private GameObject inistateObj; // Use this for initialization void Start () { if (inistatePrefab==null)return; //實例化預制 inistateObj=Instantiate(inistatePrefab) as GameObject; inistateObj.SetActive(false); } //實現鼠標按下的接口 public void OnPointerDown(PointerEventData eventData) { inistateObj.SetActive(true); //將當前需要被實例化的對象傳遞到管理器中 SelectObjManager.Instance.AttachNewObject(inistateObj); } }
將腳本掛載到UI對象上。
創建一個對象放置管理器,用於處理拖動的放置的邏輯:
SelectObjManager.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; public class SelectObjManager : MonoBehaviour { private static SelectObjManager _instance; public static SelectObjManager Instance { get { return _instance; } } //物體z軸距攝像機的長度 public float _zDistance = 50f; //對象的縮放系數 public float _scaleFactor=1.2f; //地面層級 public LayerMask _groundLayerMask; int touchID; bool isDragging = false; bool isTouchInput = false; //是否是有效的放置(如果放置在地面上返回True,否則為False) bool isPlaceSuccess = false; //當前要被放置的對象 public GameObject currentPlaceObj = null; //坐標在Y軸上的偏移量 public float _YOffset=0.5F; void Awake () { _instance = this; } void Update () { if (currentPlaceObj == null) return; if (CheckUserInput()){ MoveCurrentPlaceObj(); }else if (isDragging){ CheckIfPlaceSuccess(); } } /// <summary> ///檢測用戶當前輸入 /// </summary> /// <returns></returns> bool CheckUserInput () { #if !UNITY_EDITOR&&(UNITY_ANDROID||UNITY_IOS) if (Input.touches.Length > 0) { if (!isTouchInput) { isTouchInput = true; touchID = Input.touches[0].fingerId; return true; } else if (Input.GetTouch (touchID).phase == TouchPhase.Ended) { isTouchInput = false; return false; } else { return true; } } return false; #else return Input.GetMouseButton (0); #endif } /// <summary> ///讓當前對象跟隨鼠標移動 /// </summary> void MoveCurrentPlaceObj () { isDragging = true; Vector3 point; Vector3 screenPosition; #if !UNITY_EDITOR&&(UNITY_ANDROID||UNITY_IOS) Touch touch = Input.GetTouch (touchID); screenPosition = new Vector3 (touch.position.x, touch.position.y, 0); #else screenPosition = Input.mousePosition; #endif Ray ray = Camera.main.ScreenPointToRay (screenPosition); RaycastHit hitInfo; if (Physics.Raycast (ray, out hitInfo, 1000, _groundLayerMask)) { point = hitInfo.point; isPlaceSuccess = true; } else { point = ray.GetPoint (_zDistance); isPlaceSuccess = false; } currentPlaceObj.transform.position = point+new Vector3(0,_YOffset,0); currentPlaceObj.transform.localEulerAngles = new Vector3 (0, 60, 0); } /// <summary> ///在指定位置化一個對象 /// </summary> void CreatePlaceObj(){ GameObject obj=Instantiate(currentPlaceObj) as GameObject; obj.transform.position=currentPlaceObj.transform.position; obj.transform.localEulerAngles=currentPlaceObj.transform.localEulerAngles; obj.transform.localScale*=_scaleFactor; //改變這個對象的Layer為Drag,以便後續拖動檢測 obj.layer=LayerMask.NameToLayer("Drag"); } /// <summary> ///檢測是否放置成功 /// </summary> void CheckIfPlaceSuccess(){ if (isPlaceSuccess){ CreatePlaceObj(); } isDragging=false; currentPlaceObj.SetActive(false); currentPlaceObj=null; } /// <summary> /// 將要創建的對象傳遞給當前對象管理器 /// </summary> /// <param name="newObject"></param> public void AttachNewObject(GameObject newObject){ if (currentPlaceObj){ currentPlaceObj.SetActive(false); } currentPlaceObj=newObject; } }
腳本中都有詳細註釋,我就不多解釋瞭。
創建一個腳本,用於處理放置成功後,再次改變位置的邏輯:
DragObject.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; public class DragObject : MonoBehaviour { //隻針對指定的層級進行拖動 public LayerMask _dragLayerMask; //指定當前要拖動的對象 public Transform currentTransform; //是否可以拖動當前對象 public bool isDrag = false; //用於存儲當前需要拖動的對象在屏幕空間中的坐標 Vector3 screenPos = Vector3.zero; //當前需要拖動對象的坐標相對於鼠標在世界空間坐標中的偏移量 Vector3 offset = Vector3.zero; void Update () { if (Input.GetMouseButtonDown (0)) { //將鼠標輸入點轉化為一條射線 Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition); RaycastHit hitinfo; //如果當前對象與指定的層級發生碰撞,表示當前對象可以被拖動 if (Physics.Raycast (ray, out hitinfo, 1000f, _dragLayerMask)) { isDrag = true; //將當前需要拖動的對象賦值為射線碰撞到的對象 currentTransform = hitinfo.transform; //將當前對象的世界坐標轉化為屏幕坐標 screenPos = Camera.main.WorldToScreenPoint (currentTransform.position); //將鼠標的屏幕坐標轉換為世界空間坐標,再與當前要拖動的對象計算兩者的偏移量 offset = currentTransform.position - Camera.main.ScreenToWorldPoint (new Vector3 (Input.mousePosition.x, Input.mousePosition.y, screenPos.z)); } else { isDrag = false; } } if (Input.GetMouseButton (0)) { if (isDrag == true) { var currentScreenPos = new Vector3 (Input.mousePosition.x, Input.mousePosition.y, screenPos.z); //鼠標的屏幕空間坐標轉化為世界坐標,並加上偏移量 var currentPos = Camera.main.ScreenToWorldPoint (currentScreenPos) + offset; currentTransform.position = currentPos; } } if (Input.GetMouseButtonUp (0)) { isDrag = false; currentTransform = null; } } }
主要是一些坐標空間的變換和計算。
多餘的我就不說瞭,腳本中都有很詳細的註釋,Demo地址掃碼後當前文章末尾獲取。
到此這篇關於Unity 從UI中拖拽對象放置並拖動的文章就介紹到這瞭,更多相關Unity UI拖拽內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!