-
通過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); } }
然后新建一個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); } }
-
通過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); } }
這時候會發現一個問題,就是Cube不會被Mask遮擋住,因為Mask遮罩遮不住模型,其實只要在Mask周圍添加上UI圖片,擋住Cube就行了。