游戏中很普遍的一个功能,长按道具,显示道具的信息。类似下面这样的一个功能:
分析一下这个功能需要的知识点:
1.文本框的长宽根据文本内容自适应;
2.点击位置转换成实际UI位置(弹窗位置由点击位置决定而不是点击对象的位置决定,后者更简单)
3.若道具在屏幕边界,则需要动态修改弹窗的位置以及轴点。
先说一下画布设置,博主在canvas下新加了UIcamera,相机只渲染UI,方式为Orthographic。画布渲染方式为根据ui相机渲染,ui缩放方式为根据屏幕分辨率,画布尺寸为1080*1920,如图
第一个问题:弹窗由背景父物体image以及内容子物体text组成。首先背景大小是不定的,所以背景image的填充方式应该为sliced,则需要对图片进行切割,切割图片只需要在图片的inspector视图中点击sprite editor按钮进行切割,切割成拉伸时不影响边框即可。然后在父物体中添加组件Vertical Layout Group和Content Size Fitter,其中Content Size Fitter要设置为preferred。Vertical Layout Group根据自身需要设置即可,博主设置如下。子物体不需要添加额外的组件,之后就可以自适应了。记做预设UITip,供下文使用。这里注意的是如果要改子物体text的文本对齐方式,需要改父物体的轴点。
第二个问题:博主最初的做法是讲鼠标坐标转换为屏幕坐标,然后乘以画布缩放值得到ui坐标,但是这样算的换个分辨率就不对了,不清楚为什么,这一块也不是很熟悉,找不到问题,只能换方法,直接用一个api解决了,博主依旧贴出来原来的算法,希望知道问题的能指点一下
Vector3 pos = Input.mousePosition;
Debug.Log("mousePos:"+pos);
Vector2 pos2d = RectTransformUtility.WorldToScreenPoint(uiCamera, pos);
Vector2 pos2d_1 = pos2d * canvas.transform.localScale.x;
x = pos2d_1.x - Screen.width / 2;
y = pos2d_1.y - Screen.height / 2;
Debug.Log(x+"----"+y);
其中pos2d_1为博主算的坐标,在1080*1920可以,换了其他分辨率就不行了,而且差距非常大。之后博主更换其他方式,一个api直接解决
Vector2 pos2D_01;
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.transform as RectTransform, Input.mousePosition, uiCamera, out pos2D_01))
{
Debug.Log(pos2D_01);
}
其实最初发现了这个方法,但是api写错了...后来检查才发现的,很尴尬。pos2D_01坐标即为当前鼠标点击的位置转成的UI坐标,此时只需要将上面的预设UITip 的相对坐标(localposition)设置为pos2D_01即可。
第三个问题:首先用博主自己的语言解释下轴点pivot,轴点即为物理中心点,recttransform的坐标以及旋转都是根据轴点决定的,对象的坐标即轴点的坐标。假设一个正方形,则左下为(0,0),右上为(1,1)。设置此正方形轴点为(0,1),即以左上为轴点,此时设置正方形坐标为(100,100),则左上点坐标为(100,100)。 博主默认轴点设置为(0,1),也就是UITip是由左向右铺开,即不可能超过左边框(边框指画布边框),判断是否超过右边框只需要判断UITip的x坐标与UITip的width是否大于画布width的1/2(这里建议读者画一个xy坐标系,画出来就很好理解,但是文字表达是真的麻烦,就省过了)。大于则表示超过。假设右边框超过了,则设置UITip为从右向左铺开,即设置轴心的x值为1。这样的话UITip的最右边即是鼠标点击的地方,是不可能超框的。如图
x方向解决了,y方向同理。上面说的还有一个变量是未知的,即UITip的width。有人说直接recttransform.width。博主试了,这样是不行的,因为UITip的width是由text控制的,将text赋值后,text的width是直接生效的,然后才会影响UITip的width,这中间存在一个时间延迟,也就是说,运行这句代码Text.text =“这是个例子”之后,直接获取text的width是没问题的,但是直接获取UITip的width并不正确,但是延时再获取UITip的width就没问题,博主亲测。所以博主说这里有个延时,没办法,这里只好自己算了。上面Vertical Layout Group组件的属性padding为子物体与父物体的各个方向的边距,则父物体的width只需要text的width加上text与父物体的左右边距即可,height同理。之后与画布的长宽相比即可算出是否超框。代码如下
x = pos2D_01.x;
y = pos2D_01.y;
//当到达边界时候的处理,根据不同的情况设置不同的轴点
//先处理左右边界,默认轴点为(0,1),左边界可以不用处理,上边界不用处理
Debug.Log(Screen.width + "**"+Screen.height);
if (x + width > (1080/2) )//右边界,设置轴点x为1
{
Debug.Log("超右边界");
PivotX = 1;
}
if (Mathf.Abs(-y + height) > (1920/2))//下边界,设置轴点y为0
{
Debug.Log("超下边界");
PivotY = 0;
}
注意这里为什么会跟1080/2 和1920/2比较,这是因为画布的尺寸设置为如此,而画布的尺寸是不随分辨率改变而改变的,画布只是会根据不同的分辨率进行相应的缩放显示,但是实际尺寸还是最初设定的。这是博主的理解,不清楚是否正确,但是博士实验结果是这样的。有不对的欢迎指正,谢谢。
整个过程就是这个样子,博主粘贴一下整个代码
public class UITip : MonoBehaviour
{
public Text msgTxt;
public Canvas canvas;
public Camera uiCamera;
private RectTransform recTran;
private VerticalLayoutGroup group;
private float width;//物体宽度
private float height;//物体长度
private float x;//x坐标
private float y;//y坐标
private int PivotX;//轴点x坐标
private int PivotY;//轴点y坐标
void Start()
{
recTran = transform as RectTransform;
group = transform.GetComponent();
}
public void Show(string msg)
{
PivotX = 0;
PivotY = 1;
msgTxt.text = msg;
//设置弹窗最外面渲染
transform.SetAsLastSibling();
//计算recttransform的width与height,根据子物体文本长度和vertical layout group 的padding计算,直接获取的话获取的不对,延时获取才是对的,但子物体文本的直接获取是对的
width = msgTxt.preferredWidth + group.padding.left + group.padding.right;
height = msgTxt.preferredHeight + group.padding.top + group.padding.bottom;
Debug.Log(width + "---" + height);
#region
Vector2 pos2D_01;
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.transform as RectTransform, Input.mousePosition, uiCamera, out pos2D_01))
{
Debug.Log(pos2D_01);
}
x = pos2D_01.x;
y = pos2D_01.y;
//当到达边界时候的处理,根据不同的情况设置不同的轴点
//先处理左右边界,默认轴点为(0,1),左边界可以不用处理,上边界不用处理
Debug.Log(Screen.width + "**"+Screen.height);
if (x + width > (1080/2) )//右边界,设置轴点x为1
{
Debug.Log("超右边界");
PivotX = 1;
}
if (Mathf.Abs(-y + height) > (1920/2))//下边界,设置轴点y为0
{
Debug.Log("超下边界");
PivotY = 0;
}
recTran.pivot = new Vector2(PivotX, PivotY);
transform.localPosition = pos2D_01;
return;
#endregion
}
}
总感觉上面有些地方的解释不正确,只是自己看到的,所以希望发现问题的不吝赐教,在此谢过。