在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就行了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM