前言
如题的需求,让UI的窗体跟随场景中模型进行移动,现在很多游戏的血条就是按这个进行;如果我们直接使用世界中的Canvas,每个窗体都需要一个Canvas,消耗很大,我们一般还是在屏幕内同步显示UI的方式。
效果
如上就是同步的效果。
实现
UI搭建
按如图搭建UI
重点是pivot是UI的同步点,如果调整不准可以用偏移值修正。
定义变量
[Header("跟随的物体")]
public Transform FollowTran;
[Header("偏移值")]
public Vector2 Offset;
RectTransform ParentTran, Rtran;
同步位置
if (FollowTran != null)
{
Vector2 mScreenPos = Camera.main.WorldToScreenPoint(FollowTran.transform.position);
Vector2 mRectPos;
RectTransformUtility.ScreenPointToLocalPointInRectangle(ParentTran, mScreenPos, null, out mRectPos);
Rtran.localPosition = mRectPos + Offset;
}
思路就是世界坐标转屏幕点;通过屏幕点再转为UI位置即可。
源码工程
https://download.csdn.net/download/qq_33789001/33465355
\---------------------------------------------华丽分割线---------------------------------------------\
以下2022.3.9更新
问题
感谢kindred_joe网友的反馈,在摄像头背面会出现标签问题:
该问题的原因是在世界位置映射屏幕位置(WorldToScreenPoint)时,在摄像机背面的世界位置也会映射到屏幕内位置造成了该问题。
我这边的解决思路是判断物体是否在在摄像机前面,在前面时进行标注,否则不标注。
同步位置
if (FollowTran != null)
{
if (isInFront())
{
Vector2 mScreenPos = Camera.main.WorldToScreenPoint(FollowTran.transform.position);
Vector2 mRectPos;
RectTransformUtility.ScreenPointToLocalPointInRectangle(ParentTran, mScreenPos, null, out mRectPos);
Rtran.localPosition = mRectPos + Offset;
Rtran.localScale = Vector3.one;
}
else
Rtran.localScale = Vector3.zero;
}
位置判断
//判定在摄像头前面
public bool isInFront() {
Vector2 viewPos = Camera.main.WorldToViewportPoint(FollowTran.position);
Vector3 dir = (FollowTran.position - Camera.main.transform.position).normalized;
float dot = Vector3.Dot(Camera.main.transform.forward, dir);
if (dot > 0)
return true;
else
return false;
}