Unity 從UI中拖拽對(duì)象放置并拖動(dòng)效果 附demo
需求:點(diǎn)擊UI,在場(chǎng)景中生成3D對(duì)象,對(duì)象跟隨鼠標(biāo)移動(dòng),放置后可再次拖拽對(duì)象,改變其位置。做了一個(gè)小Demo,如下圖所示:

實(shí)現(xiàn)大致思路:
- 射線碰撞檢測(cè)
- 對(duì)象空間坐標(biāo)變換(世界坐標(biāo)->屏幕坐標(biāo)、屏幕坐標(biāo)->世界坐標(biāo))
首先為要生成3D對(duì)象的UI添加一個(gè)鼠標(biāo)監(jiān)聽事件,腳本如下:
SelectImage.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class SelectImage : MonoBehaviour,IPointerDownHandler{
//需要被實(shí)例化的預(yù)制
public GameObject inistatePrefab;
//實(shí)例化后的對(duì)象
private GameObject inistateObj;
// Use this for initialization
void Start () {
if (inistatePrefab==null)return;
//實(shí)例化預(yù)制
inistateObj=Instantiate(inistatePrefab) as GameObject;
inistateObj.SetActive(false);
}
//實(shí)現(xiàn)鼠標(biāo)按下的接口
public void OnPointerDown(PointerEventData eventData)
{
inistateObj.SetActive(true);
//將當(dāng)前需要被實(shí)例化的對(duì)象傳遞到管理器中
SelectObjManager.Instance.AttachNewObject(inistateObj);
}
}
將腳本掛載到UI對(duì)象上。
創(chuàng)建一個(gè)對(duì)象放置管理器,用于處理拖動(dòng)的放置的邏輯:
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軸距攝像機(jī)的長(zhǎng)度
public float _zDistance = 50f;
//對(duì)象的縮放系數(shù)
public float _scaleFactor=1.2f;
//地面層級(jí)
public LayerMask _groundLayerMask;
int touchID;
bool isDragging = false;
bool isTouchInput = false;
//是否是有效的放置(如果放置在地面上返回True,否則為False)
bool isPlaceSuccess = false;
//當(dāng)前要被放置的對(duì)象
public GameObject currentPlaceObj = null;
//坐標(biāo)在Y軸上的偏移量
public float _YOffset=0.5F;
void Awake () {
_instance = this;
}
void Update () {
if (currentPlaceObj == null) return;
if (CheckUserInput()){
MoveCurrentPlaceObj();
}else if (isDragging){
CheckIfPlaceSuccess();
}
}
/// <summary>
///檢測(cè)用戶當(dāng)前輸入
/// </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>
///讓當(dāng)前對(duì)象跟隨鼠標(biāo)移動(dòng)
/// </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>
///在指定位置化一個(gè)對(duì)象
/// </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;
//改變這個(gè)對(duì)象的Layer為Drag,以便后續(xù)拖動(dòng)檢測(cè)
obj.layer=LayerMask.NameToLayer("Drag");
}
/// <summary>
///檢測(cè)是否放置成功
/// </summary>
void CheckIfPlaceSuccess(){
if (isPlaceSuccess){
CreatePlaceObj();
}
isDragging=false;
currentPlaceObj.SetActive(false);
currentPlaceObj=null;
}
/// <summary>
/// 將要?jiǎng)?chuàng)建的對(duì)象傳遞給當(dāng)前對(duì)象管理器
/// </summary>
/// <param name="newObject"></param>
public void AttachNewObject(GameObject newObject){
if (currentPlaceObj){
currentPlaceObj.SetActive(false);
}
currentPlaceObj=newObject;
}
}
腳本中都有詳細(xì)注釋,我就不多解釋了。
創(chuàng)建一個(gè)腳本,用于處理放置成功后,再次改變位置的邏輯:
DragObject.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DragObject : MonoBehaviour {
//只針對(duì)指定的層級(jí)進(jìn)行拖動(dòng)
public LayerMask _dragLayerMask;
//指定當(dāng)前要拖動(dòng)的對(duì)象
public Transform currentTransform;
//是否可以拖動(dòng)當(dāng)前對(duì)象
public bool isDrag = false;
//用于存儲(chǔ)當(dāng)前需要拖動(dòng)的對(duì)象在屏幕空間中的坐標(biāo)
Vector3 screenPos = Vector3.zero;
//當(dāng)前需要拖動(dòng)對(duì)象的坐標(biāo)相對(duì)于鼠標(biāo)在世界空間坐標(biāo)中的偏移量
Vector3 offset = Vector3.zero;
void Update () {
if (Input.GetMouseButtonDown (0)) {
//將鼠標(biāo)輸入點(diǎn)轉(zhuǎn)化為一條射線
Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
RaycastHit hitinfo;
//如果當(dāng)前對(duì)象與指定的層級(jí)發(fā)生碰撞,表示當(dāng)前對(duì)象可以被拖動(dòng)
if (Physics.Raycast (ray, out hitinfo, 1000f, _dragLayerMask)) {
isDrag = true;
//將當(dāng)前需要拖動(dòng)的對(duì)象賦值為射線碰撞到的對(duì)象
currentTransform = hitinfo.transform;
//將當(dāng)前對(duì)象的世界坐標(biāo)轉(zhuǎn)化為屏幕坐標(biāo)
screenPos = Camera.main.WorldToScreenPoint (currentTransform.position);
//將鼠標(biāo)的屏幕坐標(biāo)轉(zhuǎn)換為世界空間坐標(biāo),再與當(dāng)前要拖動(dòng)的對(duì)象計(jì)算兩者的偏移量
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);
//鼠標(biāo)的屏幕空間坐標(biāo)轉(zhuǎn)化為世界坐標(biāo),并加上偏移量
var currentPos = Camera.main.ScreenToWorldPoint (currentScreenPos) + offset;
currentTransform.position = currentPos;
}
}
if (Input.GetMouseButtonUp (0)) {
isDrag = false;
currentTransform = null;
}
}
}
主要是一些坐標(biāo)空間的變換和計(jì)算。
多余的我就不說了,腳本中都有很詳細(xì)的注釋,Demo地址掃碼后當(dāng)前文章末尾獲取。
到此這篇關(guān)于Unity 從UI中拖拽對(duì)象放置并拖動(dòng)的文章就介紹到這了,更多相關(guān)Unity UI拖拽內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Unity UGUI實(shí)現(xiàn)簡(jiǎn)單拖拽圖片功能
- Unity工具類ScrollView實(shí)現(xiàn)拖拽滑動(dòng)翻頁
- Unity UI拖拽模型選擇功能
- Unity3D實(shí)現(xiàn)物體旋轉(zhuǎn)縮放移動(dòng)效果
- Unity向量按照某一點(diǎn)進(jìn)行旋轉(zhuǎn)
- Unity實(shí)現(xiàn)旋轉(zhuǎn)扭曲圖像特效
- Unity實(shí)現(xiàn)物體沿自身的任意軸向旋轉(zhuǎn)
- Unity實(shí)現(xiàn)人物旋轉(zhuǎn)和移動(dòng)效果
- Unity實(shí)現(xiàn)繞任意軸任意角度旋轉(zhuǎn)向量
- Unity UI實(shí)現(xiàn)拖拽旋轉(zhuǎn)
相關(guān)文章
C#開發(fā)之int與string轉(zhuǎn)化操作
這篇文章主要介紹了C#開發(fā)之int與string轉(zhuǎn)化操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-12-12
C#中結(jié)構(gòu)(struct)的部分初始化和完全初始化實(shí)例分析
這篇文章主要介紹了C#中結(jié)構(gòu)(struct)的部分初始化和完全初始化,通過實(shí)例分析了結(jié)構(gòu)初始化中常見的錯(cuò)誤及技巧,有助于加深對(duì)C#結(jié)構(gòu)(struct)的認(rèn)識(shí),需要的朋友可以參考下2014-09-09
DevExpress實(shí)現(xiàn)GridControl單元格編輯驗(yàn)證的方法
這篇文章主要介紹了DevExpress實(shí)現(xiàn)GridControl單元格編輯驗(yàn)證的方法,很實(shí)用的功能,需要的朋友可以參考下2014-08-08
winform 使用Anchor屬性進(jìn)行界面布局的方法詳解
這篇文章主要介紹了winform 使用Anchor屬性進(jìn)行界面布局的方法,有需要的朋友可以參考一下2013-12-12
C#中前臺(tái)線程和后臺(tái)線程的區(qū)別與聯(lián)系
這篇文章主要介紹了C#中前臺(tái)線程和后臺(tái)線程的區(qū)別與聯(lián)系,本文先講解了它們的區(qū)別,然后給出了一個(gè)例子來驗(yàn)證這些區(qū)別,需要的朋友可以參考下2015-06-06

