iCMS

在UGUI上显示3D模型,并限制范围的拖拽

iCMS http://www.hlike.cn 2018-10-12 20:00 出处:网络 编辑:@iCMS
好喜欢专业转载优质博客文章的地方哦~

通过RenderTexture实现

显示图片

记得在用NGUI的时候,就是用这种方式实现在UI上显示模型的。

首先新建一张RenderTexture,右键点击Project窗口,Create->Render Texture;

新建一个Shader,将刚刚新建的RenderTexture 拖入Shader中。

在UGUI上显示3D模型,并限制范围的拖拽

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

在UGUI上显示3D模型,并限制范围的拖拽

新建UI,将Canvas 的 RenderMode改为Camera,将Main Camera拖入Render Camera中。

新建一个Image命名为MaskImg,并添加脚本Mask,用于遮罩,并设置Image的SourceImage为UIMask

在UGUI上显示3D模型,并限制范围的拖拽

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

在UGUI上显示3D模型,并限制范围的拖拽

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

在UGUI上显示3D模型,并限制范围的拖拽

有限制的拖拽

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

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

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

在UGUI上显示3D模型,并限制范围的拖拽

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

在UGUI上显示3D模型,并限制范围的拖拽

缩放,通过获取滚轮的滑动实现缩放,添加上下限,并且注意移动限制里面需要将缩放的系数也计算其中。

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

代码如下

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

在UGUI上显示3D模型,并限制范围的拖拽

在UGUI上显示3D模型,并限制范围的拖拽

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上

在UGUI上显示3D模型,并限制范围的拖拽

在UGUI上显示3D模型,并限制范围的拖拽

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>(),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上中显示的。

在UGUI上显示3D模型,并限制范围的拖拽

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

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

在UGUI上显示3D模型,并限制范围的拖拽

直接上修改后的代码。

在UGUI上显示3D模型,并限制范围的拖拽

在UGUI上显示3D模型,并限制范围的拖拽

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>(),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,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,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,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,max);

}

}

View Code

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

0

精彩评论

暂无评论...
验证码 换一张
取 消

热门标签