【Unity3D】映射3D坐标到UI上(血条、人物状态)

507 阅读2分钟

本文已参与[新人创作礼]活动,一起开启掘金创作之路。

前言:

把3D坐标映射到UI上,这种操作多用于类似血条、人物名之类的东西:

image.png

这种其实是可以整个通用的方法来解决的:通过3D坐标直接修改UI控件的位置。

正文:

需求很明了简洁,所以直接上代码:

/// <summary>
/// 通过世界坐标设置UI的位置
/// </summary>
/// <param name="rectTransform"></param>
/// <param name="worldPos"></param>
public static void Do_SetUIPositionByWorldPos(this RectTransform rectTransform, Vector3 worldPos)
{
    if (rectTransform == null)
        return;

     //这里 uiCamera 是缓存的 UI 相机,需要外部自行定义
    if (uiCamera == null)
        return;

     //这里的 worldCamera 是缓存的世界 3D 透视相机,需要外部定义;
    Vector3 screenPos = worldCamera.WorldToScreenPoint(worldPos);
    // Z小于0,代表在相机后面,此时X、Y反向;
    if (screenPos.z < 0)
    {
        screenPos.x *= -1;
        screenPos.y *= -1;
    }

    Vector3 uiWorldPos = uiCamera.ScreenToWorldPoint(screenPos);
    rectTransform.position = uiWorldPos;
    // Z轴和UI Canvas的根节点对齐(LocalPos.Z = 0  );
    rectTransform.Do_SetLocalPosZ(0);
}

public static void Do_SetLocalPosZ(this Transform t, float z)
{
    Vector3 localPos = t.localPosition;
    localPos.z = z;
    t.localPosition = localPos;
}

上文中的 worldCamera 是指照3D物体的相机。

uiCamera是指照射UI的相机。

注意区分。

另外,有时需要对坐标进行位置限定(也就是无论如何不要移出屏幕外,如果3D坐标已经超出视野,UI则停留在在屏幕边缘),可以增加如下代码:

        //限定在屏幕内;
        if (limitInScreen)
        {
            screenPos.x = Mathf.Clamp(screenPos.x, 0, Screen.width);
            screenPos.y = Mathf.Clamp(screenPos.y, 0, Screen.height);
            
            Vector3 uiWorldPos = uiCamera.ScreenToWorldPoint(screenPos);
        }

上文中对 Screen.width 和 Screen.height 的取值据说也是有性能问题的,不能实时去取。所以最好是做一个统一取值的地方进行缓存,和 uiCamera 、 worldCamera 的操作思路是一样的。

一般这种血条的HUD都是要每帧实时刷新的,所以性能还是要尽量节省。

PS:

这个方法要放在一个静态类里,如果不明白这种写法可以去搜索C#类扩展。

PS2:

我感觉这个方法并不是性能最优,后面还以再研究怎么优化。