Unity3D 世界坐标转屏幕坐标的坑

【Unity3D】世界坐标转屏幕坐标的坑

本菜鸟是今年2019年毕业,正式走向游戏开发岗位三个月的大萌新。每天忙着和策划各种撕逼,实现各种脑残设计,其中就遇到不少值得罗缕纪存的坑,现在就开始写下了供后来的学习者跳坑。
策划之前提了一个玩家目标指示器需求,箭头始终指向玩家点击的目标在屏幕上的位置。接到需求第一个想法是直接将目标单位的世界坐标转换成ugui的屏幕坐标,使用unity自带的坐标转换函数转换就行了。
ugui以左下角为坐标原点(0,0),向上为y轴正半轴,向右为x轴正半轴。所以先计算目标的屏幕坐标和玩家摄像机屏幕中心坐标的两点之间的角度angle,通过判断目标屏幕坐标相对于摄像机屏幕中心位于哪个象限,处理之前计算的angle差值(ugui图片的角度是逆时针从0到360)。

//将目标世界坐标转换成ugui屏幕坐标
Vector3 target_ScreenPoint = Camera.main.WorldToScreenPoint(m_target.position);
//计算target_ScreenPoint和屏幕中心点的绝对值角度

float angle = Mathf.Atan(Mathf.Abs(target_ScreenPoint.y - Screen.height / 2)
    / Mathf.Abs(target_ScreenPoint.x - Screen.width / 2)) * 180 / Mathf.PI;

//通过判断target_ScreenPoint相对屏幕中心点所在象限处理angle的差值
if (target_ScreenPoint.x <= Screen.width / 2)
    angle = target_ScreenPoint.y >= Screen.height / 2 ? 90 - angle : 90 + angle;
else
    angle = target_ScreenPoint.y >= Screen.height / 2 ? 270 + angle : 270 - angle;

Vector3 euler = m_arrow.eulerAngles;
euler.z = angle;
//设置箭头的角度
m_arrow.eulerAngles = euler;

下面是效果图


箭头指示器始终指向目标在屏幕显示的位置,是不是很完美无缺。但狗策划测试发现个问题,当目标不在屏幕内,玩家转动到某些角度时候,箭头指示器会指向相反方向。如下图所示

Unity3D 世界坐标转屏幕坐标的坑_第1张图片
此时目标不在屏幕范围内,在人物左边,转换成的屏幕坐标x值为负也正常,但当人物继续向右转动后出现下图问题了。
Unity3D 世界坐标转屏幕坐标的坑_第2张图片
指向箭头刚才还指向左边,现在指向右边相反方向了,目标转换成的屏幕左边x值也变成正的了,所以这时候需要把箭头角度改成对角相反的方向。至于什么时候改了,需要通过判断当目标在摄像头左边但指示箭头角度大于180度和当目标在摄像头右边但指示箭头角度小于180时候转换即可,最后达到了真正完美的效果图如下。


这回即使旋转一周箭头位置都没任何问题,最后上完整代码

using UnityEngine;

public class GuideUICtl : MonoBehaviour
{
    public Transform m_target;                  //指向目标
    public RectTransform m_arrow;               //UI箭头
    Vector3 m_Screen_Center;

    private void Start()
    {
        m_Screen_Center = new Vector3(Screen.width / 2, Screen.height / 2, 0);
    }
    
    void Update() {
        //将目标的世界坐标转换成屏幕坐标
        Vector3 target_ScreenPoint = Camera.main.WorldToScreenPoint(m_target.position);

        //计算target_ScreenPoint和屏幕中心点的绝对值角度
        float angle = Mathf.Atan(Mathf.Abs(target_ScreenPoint.y - Screen.height / 2)
            / Mathf.Abs(target_ScreenPoint.x - Screen.width / 2)) * 180 / Mathf.PI;

        //通过判断target_ScreenPoint相对屏幕中心点所在象限处理angle的差值
        if (target_ScreenPoint.x <= Screen.width / 2)
            angle = target_ScreenPoint.y >= Screen.height / 2 ? 90 - angle : 90 + angle;
        else
            angle = target_ScreenPoint.y >= Screen.height / 2 ? 270 + angle : 270 - angle;

        #region  计算摄像机和目标的叉乘

        //以屏幕中心所在的单位向量为起始向量from,并将from的y值设成和目标y值保持一致
        Vector3 from = Camera.main.transform.forward;
        from.y = m_target.forward.y;
        //将屏幕中心坐标转换成世界坐标
        Vector3 cameraPos = Camera.main.ScreenToWorldPoint(m_Screen_Center);
        cameraPos.y = m_target.position.y;
        
        //求出目标点与摄像机之间的向量
        Vector3 to = m_target.position - cameraPos;
        //求出两向量之间的叉乘
        Vector3 cross = Vector3.Cross(from, to);
        #endregion

        //根据叉乘求出带符号的角度
        //cross.y > 0:目标向量位于起始向量右侧
        //cross.y < 0:目标向量位于起始向量左侧
        if (cross.y > 0 && angle < 180)
            angle += 180;
        else if (cross.y < 0 && angle > 180)
            angle -= 180;
        Vector3 euler = m_arrow.eulerAngles;
        euler.z = angle;
        
        //设置箭头的角度
        m_arrow.eulerAngles = euler;
    }
}

你可能感兴趣的:(Unity3D,Unity3D,ugui,屏幕坐标转换)