zoukankan      html  css  js  c++  java
  • 在UGUI上显示3D模型,并限制范围的拖拽

    • 通过RenderTexture实现

    显示图片

    • 记得在用NGUI的时候,就是用这种方式实现在UI上显示模型的。
    • 首先新建一张RenderTexture,右键点击Project窗口,Create->Render Texture;
    • 新建一个Shader ,将刚刚新建的RenderTexture 拖入Shader中。

      

    • 新建一个Cube 和一个相机名为ModelCamera,这个相机专门用来对准模型,并将刚刚新建的RenderTexture拖入相机的TargetTexture中。

      

    • 新建UI,将Canvas 的 RenderMode改为Camera,将Main Camera拖入Render Camera中。
    • 新建一个Image命名为MaskImg,并添加脚本Mask,用于遮罩,并设置Image的SourceImage为UIMask

      

    • 在MaskImg新建子物体Image,用于展示模型。然后将新建的 Shader拖入Image上。如下图,此时就已经看到模型的一面在Image上显示出来了。

      

    • 但是会发现,颜色不对,此时将Shader改为”UI/Default“就可以了。旋转模型会发现Image上的模型会同步旋转。

      

    有限制的拖拽

       拖拽图片,通过UGUI的OnDrag函数来获取鼠标的世界坐标,赋值给图片即可。

      限制,要求Image必须在Mask中显示,拖出视野外后自动回到视野内。这就需要在Mask的上下左右设置限制位置,当图片超过限制并且松开拖拽后,计算位置自动返回到视野内。

      添加限制,在MaskImg上新建子物体 Limit ,并设置Anchor

      

      在Limit下新建四个子物体,作为上下左右的限制。Left的Anchor为Left-middle,POS为(0,0,0),其他同理,Right为Right-midle,Top为Center-Top,Bottom为center-Bottom

      

      缩放,通过获取滚轮的滑动实现缩放,添加上下限,(注意:1.移动限制里面需要将缩放的系数也计算其中;2.如果放大后显示的模型出现模糊,把RenderTexture的Size属性调大即可

      旋转,计算鼠标滑动的X轴和Y轴,给Cube赋值旋转。

      代码如下

      首先添加一个UIEventListener脚本,用于监听UI的OnDrag事件

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.EventSystems;
    using UnityEngine.Events;
    
    public class UIEventListener : UnityEngine.EventSystems.EventTrigger
    {
        public delegate void VoidDelegate(GameObject go);
        public delegate void VoidEventDelegate(PointerEventData eventData);
        public VoidDelegate onClick;
    
        public VoidEventDelegate onDrag;
        public VoidEventDelegate onBeginDrag;
        public VoidEventDelegate onEndDrag;
      
    
        static public UIEventListener Get(GameObject go)
        {
            UIEventListener listener = go.GetComponent<UIEventListener>();
            if (listener == null) listener = go.AddComponent<UIEventListener>();
            return listener;
        }
        public override void OnPointerClick(PointerEventData eventData)
        {
                if (onClick != null) onClick(gameObject);
           
        }
    
    
        public override void OnDrag(PointerEventData eventData)
        {
            //if (eventData.button == PointerEventData.InputButton.Left)
                if (onDrag != null) onDrag(eventData);
        }
    
        public override void OnBeginDrag(PointerEventData eventData)
        {
            if (eventData.button == PointerEventData.InputButton.Left)
                if (onBeginDrag != null)
                    onBeginDrag(eventData);
        }
        public override void OnEndDrag(PointerEventData eventData)
        {
            if (eventData.button == PointerEventData.InputButton.Left)
                if (onEndDrag != null) onEndDrag(eventData);
        }
     
    }
    View Code

      然后新建一个MoveTest代码,挂在Canvas上

      

    using UnityEngine;
    using System.Collections;
    using UnityEngine.EventSystems;
    
    public class MoveTest : MonoBehaviour
    {
        public float ScaleMax = 4;
        public float ScaleMin = 1;
        public Transform Limit;
        public GameObject Img;
        public GameObject goModel;
        public float RotSpeed = 10f;
    
        Vector3 posOffset;
        bool isDrag;
        float fScale;
        float LimitLeft;
        float LimitRight;
        float LimitTop;
        float LimitBottom;
        float fRotX;
        float fRotY;
        float fRotYMaxLimt = 360;
        float fRotYMinLimt = -360;
        // Use this for initialization
        void Start()
        {
            UIEventListener.Get(Img).onBeginDrag = OnBeginDrag;
            UIEventListener.Get(Img).onDrag = OnDrag;
            UIEventListener.Get(Img).onEndDrag = OnEndDrag;
    
            if (Limit != null)
            {
                LimitLeft = Limit.Find("Left").localPosition.x;
                LimitRight = Limit.Find("Right").localPosition.x;
                LimitTop = Limit.Find("Top").localPosition.y;
                LimitBottom = Limit.Find("Bottom").localPosition.y;
            }
            //计算图片的宽度,因为模型的RenderTexture必须显示在Image上,所以Image的长宽必须相等,否则会拉伸模型的显示,此处计算取宽度和高度最大值作为图片的宽高。
            float Width = (LimitRight - LimitLeft) > (LimitTop - LimitBottom) ? LimitRight - LimitLeft : LimitTop - LimitBottom;
            Img.GetComponent<RectTransform>().sizeDelta = new Vector2(Width, Width);
            fScale = Img.transform.localScale.x;
            fRotX = goModel.transform.eulerAngles.x;
            fRotY = goModel.transform.eulerAngles.y;
        }
    
        // Update is called once per frame
        void Update()
        {
            float fMouseScale = Input.GetAxis("Mouse ScrollWheel");
            if (!isDrag)
            {
                
                if (fMouseScale != 0)
                    Scale(fMouseScale);
                MoveBack(Img);
            }
         
        }
    
        void OnBeginDrag(PointerEventData eventData)
        {
            Vector3 vPos = new Vector3();
            if (RectTransformUtility.ScreenPointToWorldPointInRectangle(Img.GetComponent<RectTransform>(), eventData.position, Camera.main, out vPos))
            {
                posOffset = Img.transform.position - vPos;
            }
            isDrag = true;
        }
        void OnDrag(PointerEventData eventData)
        {
            if (isDrag)
            {
                Vector3 pos;
                if (RectTransformUtility.ScreenPointToWorldPointInRectangle(Img.GetComponent<RectTransform>(), eventData.position, Camera.main, out pos))
                {
                    Img.transform.position = pos + posOffset;
                }
            }
            else if (eventData.button == PointerEventData.InputButton.Right)
            {
                Rotation();
            }
        }
        void OnEndDrag(PointerEventData eventData)
        {
            posOffset = Vector3.zero;
            isDrag = false;
        }
    
        //回到视野内
        void MoveBack(GameObject objImg)
        {
            float fOffsetX;
            float fOffsetY;
            {
                if (objImg.transform.localPosition.x < (LimitRight - objImg.GetComponent<RectTransform>().rect.width / 2 * fScale))
                {
                    fOffsetX = LimitRight - (objImg.transform.localPosition.x + objImg.GetComponent<RectTransform>().rect.width / 2 * fScale);
    
                }
                else if (objImg.transform.localPosition.x > (LimitLeft + objImg.GetComponent<RectTransform>().rect.width / 2 * fScale))
                {
                    fOffsetX = LimitLeft - (objImg.transform.localPosition.x - objImg.GetComponent<RectTransform>().rect.width / 2 * fScale);
                }
                else
                {
                    fOffsetX = 0;
                }
            }
            {
                if (objImg.transform.localPosition.y > (LimitBottom + objImg.GetComponent<RectTransform>().rect.height / 2 * fScale))
                {
                    fOffsetY = LimitBottom - (objImg.transform.localPosition.y - objImg.GetComponent<RectTransform>().rect.height / 2 * fScale);
                }
                else if (objImg.transform.localPosition.y < (LimitTop - objImg.GetComponent<RectTransform>().rect.height / 2 * fScale))
                {
                    fOffsetY = LimitTop - (objImg.transform.localPosition.y + objImg.GetComponent<RectTransform>().rect.height / 2 * fScale);
                }
                else
                {
                    fOffsetY = 0;
                }
            }
            objImg.transform.Translate(new Vector3(fOffsetX, fOffsetY, 0) * Time.deltaTime / 2, Space.Self);
        }
    
        //缩放
        void Scale(float fDetla)
        {
            Debug.Log(fDetla);
            Img.transform.localScale = new Vector3(Img.transform.localScale.x + fDetla, Img.transform.localScale.y + fDetla, 1);
            if (Img.transform.localScale.x < ScaleMin)
                Img.transform.localScale = Vector3.one;
            else if (Img.transform.localScale.x > ScaleMax)
                Img.transform.localScale = new Vector3(ScaleMax, ScaleMax, 1);
            fScale = Img.transform.localScale.x;
        }
    
        void Rotation()
        {
    
            float fx = Input.GetAxis("Mouse X");
            float fy = Input.GetAxis("Mouse Y");
    
            #region//第一种方法,无法实现世界坐标的X轴旋转
            //if (Mathf.Abs(fx) >= Mathf.Abs(fy))
            //{
            //    fRotX -= fx * RotSpeed;
            //}
            //else if (Mathf.Abs(fy) > Mathf.Abs(fx))
            //{
            //    fRotY += fy * RotSpeed;
            //}
            //fRotY = ClampAngle(fRotY, fRotYMinLimt, fRotYMaxLimt);
            //goModel.transform.rotation = Quaternion.Euler(fRotY, fRotX, 0);
            #endregion
            if (Mathf.Abs(fx) >= Mathf.Abs(fy))
            {
                fRotX += fx * RotSpeed;
                fRotY = 0;
            }
            else if (Mathf.Abs(fy) > Mathf.Abs(fx))
            {
                fRotY += fy * RotSpeed;
                fRotX = 0;
            }
            goModel.transform.Rotate(Vector3.up, -fx * Time.deltaTime * 500, Space.World);
            goModel.transform.Rotate(Vector3.right, fy * Time.deltaTime * 500, Space.World);
        }
        float ClampAngle(float angle, float min, float max)
        {
            if (angle < -360)
                angle += 360;
            if (angle > 360)
                angle -= 360;
            return Mathf.Clamp(angle, min, max);
        }
    }
    View Code
    • 通过UI和模型的位置实现

      上述方法存在一个问题,就是无法直接和模型进行点击交互,因为你点击的是RenderTexture,那就需要 另外一种方法,调整UI和模型的位置,可以直接将模型放入主摄像机的视野中。

      为了方便计算,先将MainCamera坐标变为(0,0,0),将Canvas的PlaneDistance变成1,把Cube放到(0,0,2)的位置。现在在Game视图中就看到了对象Cube,并不是在RenderTexture上中显示的。

      

      其实原理就是等比,UI和Cube距离摄像机的距离等比于Img在UI上移动的距离和Cube在World上移动的距离。

      所以在移动的方法里把Img移动的距离X这个比例,就可以计算Cube的移动距离并赋值了。

      

      直接上修改后的代码。

      

    using UnityEngine;
    using System.Collections;
    using UnityEngine.EventSystems;
    
    public class MoveTest : MonoBehaviour
    {
        public float ScaleMax = 4;
        public float ScaleMin = 1;
        public Transform Limit;
        public GameObject Img;
        public GameObject goModel;
        public float RotSpeed = 10f;
        public Canvas canvas;
        Vector3 posOffset;
        bool isDrag;
        float fScale;
        float LimitLeft;
        float LimitRight;
        float LimitTop;
        float LimitBottom;
        float fRotX;
        float fRotY;
        float fRotYMaxLimt = 360;
        float fRotYMinLimt = -360;
        float fDistance;
        Vector3 StartPos;
        // Use this for initialization
        void Start()
        {
            UIEventListener.Get(Img).onBeginDrag = OnBeginDrag;
            UIEventListener.Get(Img).onDrag = OnDrag;
            UIEventListener.Get(Img).onEndDrag = OnEndDrag;
    
            if (Limit != null)
            {
                LimitLeft = Limit.Find("Left").localPosition.x;
                LimitRight = Limit.Find("Right").localPosition.x;
                LimitTop = Limit.Find("Top").localPosition.y;
                LimitBottom = Limit.Find("Bottom").localPosition.y;
            }
            //计算图片的宽度,因为模型的RenderTexture必须显示在Image上,所以Image的长宽必须相等,否则会拉伸模型的显示,此处计算取宽度和高度最大值作为图片的宽高。
            float Width = (LimitRight - LimitLeft) > (LimitTop - LimitBottom) ? LimitRight - LimitLeft : LimitTop - LimitBottom;
            Img.GetComponent<RectTransform>().sizeDelta = new Vector2(Width, Width);
            fScale = Img.transform.localScale.x;
            fRotX = goModel.transform.eulerAngles.x;
            fRotY = goModel.transform.eulerAngles.y;
            StartPos = goModel.transform.localPosition;
        }
    
        // Update is called once per frame
        void Update()
        {
            float fMouseScale = Input.GetAxis("Mouse ScrollWheel");
            if (!isDrag)
            {
                
                if (fMouseScale != 0)
                    Scale(fMouseScale);
                MoveBack(Img);
            }
         
        }
    
        void OnBeginDrag(PointerEventData eventData)
        {
            Vector3 vPos = new Vector3();
            if (RectTransformUtility.ScreenPointToWorldPointInRectangle(Img.GetComponent<RectTransform>(), eventData.position, Camera.main, out vPos))
            {
                posOffset = Img.transform.position - vPos;
            }
            isDrag = true;
        }
        void OnDrag(PointerEventData eventData)
        {
            if (isDrag)
            {
                Vector3 pos;
                if (RectTransformUtility.ScreenPointToWorldPointInRectangle(Img.GetComponent<RectTransform>(), eventData.position, Camera.main, out pos))
                {
                    Img.transform.position = pos + posOffset;
    
                    //计算比例,然后再Img移动的基础上乘以比例系数,就算出模型移动的距离了。
                    fDistance = goModel.transform.position.z / canvas.planeDistance;
                    Debug.Log(fDistance);
                    float fx = Img.transform.position.x * fDistance;
                    float fy = Img.transform.position.y * fDistance;
    
                    goModel.transform.localPosition = StartPos + new Vector3(fx, fy, 0);
                }
            }
            else if (eventData.button == PointerEventData.InputButton.Right)
            {
                Rotation();
            }
        }
        void OnEndDrag(PointerEventData eventData)
        {
            posOffset = Vector3.zero;
            isDrag = false;
        }
    
        //回到视野内
        void MoveBack(GameObject objImg)
        {
            float fOffsetX;
            float fOffsetY;
            {
                if (objImg.transform.localPosition.x < (LimitRight - objImg.GetComponent<RectTransform>().rect.width / 2 * fScale))
                {
                    fOffsetX = LimitRight - (objImg.transform.localPosition.x + objImg.GetComponent<RectTransform>().rect.width / 2 * fScale);
    
                }
                else if (objImg.transform.localPosition.x > (LimitLeft + objImg.GetComponent<RectTransform>().rect.width / 2 * fScale))
                {
                    fOffsetX = LimitLeft - (objImg.transform.localPosition.x - objImg.GetComponent<RectTransform>().rect.width / 2 * fScale);
                }
                else
                {
                    fOffsetX = 0;
                }
            }
            {
                if (objImg.transform.localPosition.y > (LimitBottom + objImg.GetComponent<RectTransform>().rect.height / 2 * fScale))
                {
                    fOffsetY = LimitBottom - (objImg.transform.localPosition.y - objImg.GetComponent<RectTransform>().rect.height / 2 * fScale);
                }
                else if (objImg.transform.localPosition.y < (LimitTop - objImg.GetComponent<RectTransform>().rect.height / 2 * fScale))
                {
                    fOffsetY = LimitTop - (objImg.transform.localPosition.y + objImg.GetComponent<RectTransform>().rect.height / 2 * fScale);
                }
                else
                {
                    fOffsetY = 0;
                }
            }
            //注意此处,(60/canvas.planeDistance)必须加上,否则会因为移动过,导致Postion的值过大而报错,因为一开始Canvas的值是60,现在改成1,所以需要60/1
            objImg.transform.Translate(new Vector3(fOffsetX, fOffsetY, 0) * Time.deltaTime / (60 / canvas.planeDistance), Space.Self);
    
            goModel.transform.localPosition=StartPos+ new Vector3(objImg.transform.position.x * fDistance, objImg.transform.position.y * fDistance, 0);
        }
    
        //缩放
        void Scale(float fDetla)
        {
            Img.transform.localScale = new Vector3(Img.transform.localScale.x + fDetla, Img.transform.localScale.y + fDetla, 1);
            if (Img.transform.localScale.x < ScaleMin)
                Img.transform.localScale = Vector3.one;
            else if (Img.transform.localScale.x > ScaleMax)
                Img.transform.localScale = new Vector3(ScaleMax, ScaleMax, 1);
            //fScale = Img.transform.localScale.x;
            //同样的代码,Cube再来一遍
            goModel.transform.localScale = new Vector3(goModel.transform.localScale.x + fDetla, goModel.transform.localScale.y + fDetla, 1);
            if (goModel.transform.localScale.x < ScaleMin)
                goModel.transform.localScale = Vector3.one;
            else if (goModel.transform.localScale.x > ScaleMax)
                goModel.transform.localScale = new Vector3(ScaleMax, ScaleMax, 1);
            fScale = goModel.transform.localScale.x;
        }
    
        void Rotation()
        {
    
            float fx = Input.GetAxis("Mouse X");
            float fy = Input.GetAxis("Mouse Y");
    
            #region//第一种方法,无法实现世界坐标的X轴旋转
            //if (Mathf.Abs(fx) >= Mathf.Abs(fy))
            //{
            //    fRotX -= fx * RotSpeed;
            //}
            //else if (Mathf.Abs(fy) > Mathf.Abs(fx))
            //{
            //    fRotY += fy * RotSpeed;
            //}
            //fRotY = ClampAngle(fRotY, fRotYMinLimt, fRotYMaxLimt);
            //goModel.transform.rotation = Quaternion.Euler(fRotY, fRotX, 0);
            #endregion
            if (Mathf.Abs(fx) >= Mathf.Abs(fy))
            {
                fRotX += fx * RotSpeed;
                fRotY = 0;
            }
            else if (Mathf.Abs(fy) > Mathf.Abs(fx))
            {
                fRotY += fy * RotSpeed;
                fRotX = 0;
            }
            goModel.transform.Rotate(Vector3.up, -fx * Time.deltaTime * 500, Space.World);
            goModel.transform.Rotate(Vector3.right, fy * Time.deltaTime * 500, Space.World);
        }
        float ClampAngle(float angle, float min, float max)
        {
            if (angle < -360)
                angle += 360;
            if (angle > 360)
                angle -= 360;
            return Mathf.Clamp(angle, min, max);
        }
    }
    View Code

      这时候会发现一个问题,就是Cube不会被Mask遮挡住,因为Mask遮罩遮不住模型,其实只要在Mask周围添加上UI图片,挡住Cube就行了。

  • 相关阅读:
    构建之法阅读笔记
    人月神话阅读笔记
    人月神话阅读笔记2
    人月神话阅读笔记
    第十周总结
    第九周总结
    进度日报10
    进度日报8
    进度日报7
    进度日报6
  • 原文地址:https://www.cnblogs.com/YDoubleC/p/9777411.html
Copyright © 2011-2022 走看看