服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - Unity 从UI中拖拽对象放置并拖动效果 附demo

Unity 从UI中拖拽对象放置并拖动效果 附demo

2022-11-21 14:37Sun.ME C#

最近新接了个需求,要求模拟场景并生成3D对象,对象可以跟随鼠标移动效果,今天小编把我实现的demo分享到脚本之家平台,对Unity UI拖拽相关知识感兴趣的朋友跟随小编一起学习吧

需求:点击UI,在场景中生成3D对象,对象跟随鼠标移动,放置后可再次拖拽对象,改变其位置。做了一个小Demo,如下图所示:

Unity 从UI中拖拽对象放置并拖动效果 附demo

实现大致思路:

  • 射线碰撞检测
  • 对象空间坐标变换(世界坐标->屏幕坐标、屏幕坐标->世界坐标)

首先为要生成3D对象的UI添加一个鼠标监听事件,脚本如下:

SelectImage.cs

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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拖拽内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/JianZuoGuang/article/details/81304796

延伸 · 阅读

精彩推荐
  • C#c# BackgroundWorker使用方法

    c# BackgroundWorker使用方法

    这篇文章主要介绍了c# BackgroundWorker使用方法,文中代码非常详细,帮助大家更好的参考学习,感兴趣的朋友可以了解下...

    风情单车5942022-09-09
  • C#C#中加载dll并调用其函数的实现方法

    C#中加载dll并调用其函数的实现方法

    下面小编就为大家带来一篇C#中加载dll并调用其函数的实现方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    C#教程网5572021-12-23
  • C#Unity3D制作序列帧动画的方法

    Unity3D制作序列帧动画的方法

    这篇文章主要为大家详细介绍了Unity3D制作序列帧动画的方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    qq_339945665302022-03-10
  • C#C#使用Selenium+PhantomJS抓取数据

    C#使用Selenium+PhantomJS抓取数据

    本文主要介绍了C#使用Selenium+PhantomJS抓取数据的方法步骤,具有很好的参考价值,下面跟着小编一起来看下吧...

    挖宝居士5922021-12-27
  • C#解决C#中Linq GroupBy 和OrderBy失效的方法

    解决C#中Linq GroupBy 和OrderBy失效的方法

    最近发现了一个问题,在服务器端的Linq GroupBy 和OrderBy居然不管用,后来终于解决了所以现在分享给大家,有需要的朋友们可以参考借鉴。...

    garfieldzf4342021-12-07
  • C#C# 根据字符串生成二维码的实例代码

    C# 根据字符串生成二维码的实例代码

    这篇文章主要介绍了C# 根据字符串生成二维码的实例,文中示例代码非常详细,帮助大家更好的理解和学习,感兴趣的朋友可以了解下...

    江北3912022-09-24
  • C#轻松学习C#的ArrayList类

    轻松学习C#的ArrayList类

    轻松学习C#的ArrayList类,对C#的ArrayList类感兴趣的朋友可以参考本篇文章,帮助大家更灵活的运用C#的ArrayList类。...

    丿木呈广予口贝5542021-11-04
  • C#C#用timer实现背单词小程序

    C#用timer实现背单词小程序

    这篇文章主要为大家详细介绍了C#用timer实现背单词小程序,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    、你我山前没相见7472022-07-31