文章目录
前言
一、工具
二、使用步骤
1.逻辑
2.拖动物体,传入鼠标坐标
3.鼠标坐标转为rect下的坐标
内容:ui选择框随鼠标的点击,位移到移动位置,同时保证ui选择框始终全部出现在canvas下.
一、工具
unity2019.3,ugui
其中canvas的Render Mode选择Screen Space - camera模式,相机赋值为主相机,canvas Scaler选择Scale With Sceen Size模式,如下图,因为Screen Space- overlay模式下的鼠标拖动ui没记录的,直接获取屏幕坐标就行了.
1.使用射线判断是否点击到3D物体,判断点击到3D物体成功后,获取到鼠标点击的屏幕坐标.
2.通过RectTransformUtility.ScreenPointToLocalPointInRectangle方法,鼠标的屏幕坐标相对于Ui RectTransform下的坐标,会返回一个v2坐标,这个就是运算出的ui要移动到的坐标点.
3.最后为了让Ui按钮选择框和框的背景始终全部出现在Canvas里面,不会出现框显示一半的情况,通过框的宽高和canvas的宽高计算出一个坐标,赋值给UI,
最终效果
这里使用到一个信息处理机制,和拖动3D物体的代码,会标注出来.和一个修改material上shader参数的代码段,主要就是region圈起来的一行代码,获取鼠标点击坐标
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Move3DObject : MonoBehaviour
{
private bool isClick = false;
private Transform curTf = null;
private Vector3 oriMousePos;
private Vector3 oriObjectScreenPos;
///
/// 3d物体上的material,用过修改Gameobject上的shader来添加物体的一个描边效果,可以删掉引用到他的所有代码
///
private Material materShader;
///
/// 3D物体所在的layer层
///
private int layerMask;
private void Awake()
{
layerMask = 1 << 8;
}
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray , out hit , 100, layerMask))
{
curTf = hit.transform;
oriObjectScreenPos = Camera.main.WorldToScreenPoint(curTf.position);
oriMousePos = Input.mousePosition;
materShader = curTf.GetComponent().material;
#region 通过射线判断点击到3D物体,把鼠标点击坐标通过信息处理机制传到设置ui坐标的脚本,也可以用单利的方式传递
MessageCenter.PostMessage(EMessage.uiSetImagePosition , Input.mousePosition);
#endregion
materShader.SetFloat("_OutlineWidth" , 0.015f);
materShader.SetFloat("_AlphaBlend" , 0.5f);
isClick = !isClick;
}
}
if(Input.GetMouseButtonUp(0))
{
if (curTf!=null)
{
curTf = null;
materShader.SetFloat("_OutlineWidth" , 0);
materShader.SetFloat("_AlphaBlend" , 1);
isClick = !isClick;
}
}
if (isClick)
{
if (curTf != null)
{
Vector3 curMousePos = Input.mousePosition;
Vector3 mouseOffset = curMousePos - oriMousePos;
Vector3 curObjectScreenPos = oriObjectScreenPos + mouseOffset;
Vector3 curObjectWorldPos = Camera.main.ScreenToWorldPoint(curObjectScreenPos);
curTf.position = curObjectWorldPos;
}
}
}
}
1.鼠标坐标转化为rect下的坐标,其中的canvasRectTra是ui canvas的RectTransform属性,imageRectTra是设置的选择框的RectTransform属性,imagePanelV2是方法运算返回的结果.
其中选择框的锚点要设置为中间.
这样鼠标拖动ui,ui出现在鼠标点击的位置就完成了.
///
/// 参数为鼠标点击坐标
///
///
private void SetImagePosition(Vector3 v3)
{
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRectTra , v3 , mainCamera , out imagePanelV2))
{
imageRectTra.anchoredPosition = imagePanelV2;
}
}
2.我要的效果是选择出现在点击3d模型的旁边,并且始终全部出现在ui canvas视野内.
第一步:先通过ImagePanel的宽高向坐标点旁边位移,,让imaegpanel的左上或者右上边框点重现在点击位置上,就是上面运算出来的坐标点进行运算.
第二步 :判断选择框是否超出Canvas的视野范围,对超出的部分进行对应的位移,让他始终处于canvas内.
(其中的AddMessage(),RemoveMessage()是注册,删除信息处理机制的方法,可以不注意,通过单利调用SetImagePosition()方法,通过单利调用传参也是一样的,OnBindPanelElements()方法,放到Start()内调用是一样的,脚本继承的UiBasePanel,无所谓主要是代码逻辑)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UiSetImagePosition : UiBasePanel
{
private float imageWidth, imageHeight;
private RectTransform imageRectTra;
private Camera mainCamera;
private RectTransform canvasRectTra;
private Vector2 imagePanelV2;
private float halfScreenHeig = 0;
private float screenHeightHalf
{
get
{
if (halfScreenHeig == 0)
halfScreenHeig = (Screen.height / (Screen.width / 800f)) / 2;
return halfScreenHeig;
}
}
//public RectTransform panelRect;
protected override void OnBindPanelElements()
{
imageRectTra = gameObject.FindChild("Image");
mainCamera = Camera.main;
imageWidth = imageRectTra.sizeDelta.x / 2;
imageHeight = imageRectTra.sizeDelta.y / 2;
canvasRectTra = GameObject.Find("Canvas").GetComponent();
//screenHeightHalf = (Screen.height/(Screen.width/800f))/2;
base.OnBindPanelElements();
}
protected override void AddMessage()
{
MessageCenter.AddMessage(EMessage.uiSetImagePosition , SetImagePosition);
base.AddMessage();
}
protected override void RemoveMessage()
{
MessageCenter.RemoveMessage(EMessage.uiSetImagePosition , SetImagePosition);
base.RemoveMessage();
}
///
/// 参数为鼠标点击坐标
///
///
private void SetImagePosition(Vector3 v3)
{
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRectTra , v3 , mainCamera , out imagePanelV2))
{
//通过ImagePanel的宽高向坐标点旁边位移,,让imaegpanel的左上或者右上边框点重现在点击位置上
imagePanelV2 = new Vector2(imagePanelV2.x + imageWidth , imagePanelV2.y - imageHeight);
//判断ImagePanel是否超出Canvas的下边界
if (imagePanelV2.y - imageHeight < -screenHeightHalf)
{
//如果超出下边界,让imagePanel的y轴坐标按超出的向量向上位移,是ImagePanel的最下边正好与Canvas的边框对齐
imagePanelV2.y = imagePanelV2.y - ((imagePanelV2.y - imageHeight) + screenHeightHalf);
}
imageRectTra.anchoredPosition = imagePanelV2;
}
}
}
3.对上面脚本的screenHeightHalf字段的解释,
因为把canvas的Canvas Scaler的目标宽高设置为800*600,所以canvas的width和heighe会根据canvas Scaler进行限制,canvas的大小并不是我们设置的屏幕大小,
如下面这样设置canvas的宽是一直在800,高会随着我们设置的屏幕宽高进行等比例缩放,也就是宽是固定的,高不固定,(宽也会在800左右有一定的浮动值,误差不大)
so,屏幕的宽高跟Canvas的宽高有统一的比例值,可以通过屏幕的宽/Vanvas的宽计算出比例值,在计算出canvas的高.因为选择框的锚点在屏幕的中心,所以canvas视野内的最下边的点y=-canvas.height/2.这样就可以判断选择框是否超出canvas视野最下方.上下左右都可以这样判断