UGUI 实现屏幕外怪物的指示箭头

在玩ARPG手游的时候,我们常常会发现在战斗的过程中,如果有怪物在屏幕的外边(即玩家视野之外)的时候,会发现在玩家周围活屏幕边缘有指示怪物方位的箭头,而且这是在UI上的。我用UGUI来实现这个贴心的功能。

UGUI 实现屏幕外怪物的指示箭头_第1张图片

首先实现思路:肯定是把3D世界的坐标投射到屏幕坐标,再由屏幕坐标投射到UI坐标。有两种实现方式:

  1. 先在3D世界中把指示箭头的位置计算出来。
    UGUI 实现屏幕外怪物的指示箭头_第2张图片

然后再把计算出来的点投射到屏幕。与玩家的点也投射到屏幕。

2.把玩家的位置和怪物的世界坐标分别投射至UI坐标,然后在UI坐标里计算出指示箭头的位置。这样指示箭头与玩家的距离稳定。这种方案理论上是没问题的,但实际实现的过程中,我发现角色走到某些地方(怪物距离屏幕很远的地方)的时候发现指示箭头反向了,并不对。发现是
Camera.WorldToScreenPoint这个方法得出的屏幕坐标有问题,原因不明啊。

实现之:
涉及到的知识点:
- 把世界坐标投射到屏幕坐标,及把屏幕坐标投射到UI(用了单独的摄像机来渲染UI)坐标。
- 向量的基本计算。
- UI里设置图片旋转到指定方向(即UI的LookAt)。
- 怪物是否在屏幕里的判断

准备:
UGUI 实现屏幕外怪物的指示箭头_第3张图片

写代码:

using UnityEngine;
using System.Collections.Generic;
using UnityEngine.UI;
/// 
/// 怪物位置指示器
/// 
public class MonsterIndicator : MonoBehaviour
{
    //public List monsterList;
    public Transform player;
    public int disFromPlayer = 200;//指示箭头与玩家距离,以像素算
    public Camera uiCam;

    List indicatorList = new List();//指示箭头图片资源
    Dictionary<int, IndicatorMonster> outScreenMonsters = new Dictionary<int, IndicatorMonster>();//在屏幕外的怪物列表

    void Start()
    {
        for (int i = 0, size = transform.childCount; i < size; i++)
        {
            Transform child = transform.GetChild(i);
            indicatorList.Add(child);
            child.name = "-1";
        }
    }

    void LateUpdate()
    {
        foreach (KeyValuePair<int, IndicatorMonster> kv in outScreenMonsters)
        {
            kv.Value.Update();
        }
    }



    int GetNoUseIndicator()
    {
        for (int i = 0, size = indicatorList.Count; i < size; i++)
        {
            if (indicatorList[i].name.CompareTo("-1") == 0) return i;
        }
        Transform indicator = Instantiate(indicatorList[0]);
        indicator.SetParent(indicatorList[0].parent);
        indicator.localScale = Vector3.one;
        indicator.name = "-1";
        indicatorList.Add(indicator);
        return indicatorList.Count - 1;
    }



    public void OnScreen(bool outScreen, Transform monst)
    {
        int key = monst.GetInstanceID();
        if (outScreen)
        {
            if (!outScreenMonsters.ContainsKey(key))
            {
                IndicatorMonster monstInfo = new IndicatorMonster();
                monstInfo.monst = monst;
                Transform indicator = indicatorList[GetNoUseIndicator()];
                indicator.name = key.ToString();
                indicator.gameObject.SetActive(true);

                monstInfo.indicator = indicator;
                monstInfo.player = player;
                monstInfo.uiCam = uiCam;
                monstInfo.parent = transform as RectTransform;
                monstInfo.disFromPlayer = disFromPlayer;

                outScreenMonsters.Add(key, monstInfo);
            }
        }
        else
        {
            if (outScreenMonsters.ContainsKey(key))
            {
                outScreenMonsters[key].Dispose();
                outScreenMonsters.Remove(key);
            }
        }
    }

}

internal class IndicatorMonster
{
    public Transform indicator;
    public Transform monst;
    public Transform player;
    public Camera uiCam;
    public RectTransform parent;
    public int disFromPlayer;

    public void Update()
    {
        Vector3 indicatorPoint = (monst.position - player.position).normalized * 5 + player.position;
        Vector3 indicatorScnPoint = Camera.main.WorldToScreenPoint(indicatorPoint);
        Vector3 playerPos = Camera.main.WorldToScreenPoint(player.position);

        Vector2 indicatorUIPoint;
        Vector2 playerUIPos;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(parent, indicatorScnPoint, uiCam, out indicatorUIPoint);
        RectTransformUtility.ScreenPointToLocalPointInRectangle(parent, playerPos, uiCam, out playerUIPos);
        Vector2 indicatorPos = playerUIPos + (indicatorUIPoint - playerUIPos).normalized * 200;
        indicator.localPosition = new Vector3(indicatorPos.x, indicatorPos.y, 0);
        UILookAt(indicator, indicatorUIPoint - playerUIPos, Vector3.up);
    }
    /// 
    /// UI的LookAt
    /// 
    /// 
    /// 
    /// 
    public void UILookAt(Transform transform, Vector3 dir, Vector3 lookAxis)
    {
        Quaternion q = Quaternion.identity;
        q.SetFromToRotation(lookAxis, dir);
        transform.rotation = q;
    } 

    public void Dispose()
    {
        indicator.gameObject.SetActive(false);
        indicator.gameObject.name = "-1";
        indicator = null;
        monst = null;
        player = null;
        uiCam = null;
        parent = null;
    }
}

效果:
UGUI 实现屏幕外怪物的指示箭头_第4张图片

UGUI 实现屏幕外怪物的指示箭头_第5张图片

你可能感兴趣的:(unity之UGUI)