UI指示屏幕外目标方向的功能

有时后敌人或者传送门等位置超出屏幕外,为了指示这些物体的位置,需要有UI标识这些物体的位置。

这里采用怪物离屏幕边缘最近的点。另一种方式是怪物与玩家的位置连线与屏幕边框的交点,这里暂时不考虑。

using JetBrains.Annotations;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Security.AccessControl;
using UnityEngine;
using UnityEngine.Animations;

/// <summary>
/// 图标类型
/// </summary>
public enum IconType
{
    OpendDoor,   //正在打开的门
    EliteDoor,   //精英怪门
    NpcDoor,     //NPC门
    BossDoor,     //Boss门
    RandomDoor,  //问号门
    HomeDoor
}

/// <summary>
/// 预制物体和图标类型的对应
/// </summary>
[System.Serializable]
public class PointIconTypePair
{
    public IconType iconType;
    public GameObject prefab;
}

/// <summary>
/// 图标与目标物体
/// </summary>
public class PointIconPair
{
    public Transform icon;
    public Transform target;
    public Transform cycleFigure;//圆圈指向,用于指向目标位置

    public PointIconPair(Transform icon,Transform target)
    {
        this.icon = icon;
        this.target = target;
        cycleFigure = icon.Find("CycleFigure_Image");
    }

    /// <summary>
    /// 按照固定屏幕空间计算设置图标的位置
    /// </summary>
    /// <param name="distanceToEdge"></param>
    public void UpdateWithScreenSpaceOverlay(float distanceToEdge)
    {
        if (!icon || !target) return;

        Vector3 iPos;
        Vector3 tPos;

        bool isAlive = SetAlive(out iPos,  distanceToEdge,out tPos);

        if (!isAlive) return;

        icon.position = iPos;

        CycleFigureTarget(tPos);
    }

    /// <summary>
    /// 按照固定屏幕空间计算设置图标的位置
    /// </summary>
    /// <param name="left"></param>
    /// <param name="right"></param>
    /// <param name="up"></param>
    /// <param name="down"></param>
    public void UpdateWithScreenSpaceOverlay(float left,float right,float up,float down)
    {
        if (!icon || !target) return;

        Vector3 iPos;
        Vector3 tPos;

        bool isAlive = SetAlive(out iPos,out tPos,left,right,up,down);

        if (!isAlive) return;

        icon.position = iPos;

        CycleFigureTarget(tPos);
    }

    /// <summary>
    /// 按照相机屏幕空间设置图标的位置
    /// </summary>
    /// <param name="distanceToEdge"></param>
    /// <param name="rect"></param>
    /// <param name="cam"></param>
    public void UpdateWithScreenSpaceCamera(float distanceToEdge, RectTransform rect, Camera cam)
    {
        if (!icon || !target) return;

        ////////////////////////////

        Vector3 iPos;
        Vector3 tPos;

        bool isAlive = SetAlive(out iPos, distanceToEdge,out tPos);

        if (!isAlive) return;

        var t = Vector2.zero;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(rect, iPos, cam, out t);
        icon.GetComponent<RectTransform>().anchoredPosition = t;

        RectTransformUtility.ScreenPointToLocalPointInRectangle(rect, tPos, cam, out t);
        CycleFigureTarget(t);
    }

    /// <summary>
    /// 相机空间设置设置图标位置
    /// </summary>
    /// <param name="distanceToEdge"></param>
    /// <param name="rect"></param>
    /// <param name="cam"></param>
    /// <param name="left"></param>
    /// <param name="right"></param>
    /// <param name="up"></param>
    /// <param name="down"></param>
    public void UpdateWithScreenSpaceCamera(RectTransform rect, Camera cam, float left, float right, float up, float down)
    {
        if (!icon || !target) return;

        ////////////////////////////

        Vector3 iPos;
        Vector3 tPos;

        bool isAlive = SetAlive(out iPos, out tPos, left, right, up, down);

        if (!isAlive) return;

        var t = Vector2.zero;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(rect, iPos, cam, out t);
        icon.GetComponent<RectTransform>().anchoredPosition = t;

        RectTransformUtility.ScreenPointToLocalPointInRectangle(rect, tPos, cam, out t);
        CycleFigureTarget(t);
    }

    /// <summary>
    /// 设置激活状态,根据是否在屏幕内,激活,返回的值就是激活状态
    /// </summary>
    public bool SetAlive(out Vector3 camSpacePos,float distanceToEdge,out Vector3 targetCamSpacePos)
    {
        return SetAlive(out camSpacePos,out targetCamSpacePos,distanceToEdge,distanceToEdge,distanceToEdge,distanceToEdge);
    }

    /// <summary>
    /// 获取目标物体与icon的转换在屏幕空间内的位置
    /// </summary>
    /// <param name="camSpacePos"></param>
    /// <param name="targetCamSpacePos"></param>
    /// <param name="left">离屏幕左边缘的间隔</param>
    /// <param name="right">离屏幕右边缘的间隔</param>
    /// <param name="up">离屏幕上边缘的间隔</param>
    /// <param name="down">离屏幕下边缘的间隔</param>
    /// <returns></returns>
    public bool SetAlive(out Vector3 camSpacePos,out Vector3 targetCamSpacePos, float left,float right,float up,float down)
    {
        camSpacePos = new Vector3();
        targetCamSpacePos = new Vector3();

        Vector3 canvasPos = Camera.main.WorldToScreenPoint(target.position);

        float width = UnityEngine.Screen.width;
        float height = UnityEngine.Screen.height;

        Vector3 iPos = canvasPos;

        if (iPos.x > 0 && iPos.x < width && iPos.y > 0 && iPos.y < height)//如果在屏幕区域内,那么隐藏物体,不做位置更新
        {
            icon.gameObject.SetActive(false);
            return false;
        }

        /*if (!iconPair.icon.gameObject.activeSelf) */
        icon.gameObject.SetActive(true);

        if (iPos.x > width - right) iPos.x = width - right;
        else if (iPos.x < left) iPos.x = 0 + left;

        if (iPos.y > height - up) iPos.y = height - up;
        else if (iPos.y < down) iPos.y = 0 + down;

        camSpacePos = iPos;
        targetCamSpacePos = canvasPos;

        return true;
    }

    void CycleFigureTarget(Vector3 targetPos)
    {
        if (!cycleFigure) return;
        Vector3 dir = targetPos - cycleFigure.position;dir.z = 0;
        float angle = Vector3.SignedAngle(Vector3.right, dir, Vector3.forward);
        Quaternion rotation = Quaternion.Euler(0, 0, angle);
        cycleFigure.rotation = rotation;
    }
}

/// <summary>
/// 动态图标,指示物体的位置
/// </summary>
public class PointIconUI : MonoBehaviour
{
    public List<PointIconTypePair> icons=new List<PointIconTypePair>();
    public Dictionary<IconType,List<PointIconPair>> iconPairs;

    //public float distanceToEdge;//距离边缘的宽度

    [Header("icon离四个边缘的空隙大小")]
    public float left;
    public float right;
    public float up;
    public float down;

    [Space]
    [HideInInspector]
    public Canvas canvas;
    //public RectTransform canvas;
    private void Awake()
    {
        iconPairs = new Dictionary<IconType, List<PointIconPair>>();
        foreach(PointIconTypePair typePair in icons)
        {
            if (iconPairs.ContainsKey(typePair.iconType)) continue;
            iconPairs.Add(typePair.iconType, new List<PointIconPair>());
        }
        Debug.Log(UnityEngine.Screen.width + "  ********  " + UnityEngine.Screen.height);
        //Debug.Log(iconPairs.Count);
    }

    /// <summary>
    /// 更新图标的位置,如果目标物体在屏幕外,那么显示图标在对应的边缘
    /// 如果目标物体在屏幕内,那么隐藏图标
    /// </summary>
    private void Update()
    {
        if(canvas.renderMode==RenderMode.ScreenSpaceOverlay)
            UpdateWithScreenSpaceOverlay();
        else
            UpdateWithScreenSpaceCamera();
    }

    void UpdateWithScreenSpaceOverlay()
    {
        foreach (List<PointIconPair> list in iconPairs.Values)
        {
            foreach (PointIconPair iconPair in list)
            {
                //iconPair.UpdateWithScreenSpaceOverlay(distanceToEdge);
                iconPair.UpdateWithScreenSpaceOverlay(left, right, up, down);
            }
        }
    }

    void UpdateWithScreenSpaceCamera()
    {
        foreach (List<PointIconPair> list in iconPairs.Values)
        {
            foreach (PointIconPair iconPair in list)
            {
                //iconPair.UpdateWithScreenSpaceCamera(distanceToEdge, transform.GetComponent<RectTransform>(), canvas.worldCamera);
                iconPair.UpdateWithScreenSpaceCamera(transform.GetComponent<RectTransform>(), canvas.worldCamera, left, right, up, down);
            }
        }
    }

    /// <summary>
    /// 添加一系列图标
    /// </summary>
    /// <param name="type"></param>
    /// <param name="targets"></param>
    public void CreateIcon(IconType type,List<Transform> targets)
    {
        foreach(Transform trans in targets)
        {
            CreateIcon(type,trans);
        }
    }

    //添加一个图标
    public bool CreateIcon(IconType type,Transform target)
    {
        if (!iconPairs.ContainsKey(type)||!target) return false;
        //获取一个type对应的iconTypePair
        PointIconTypePair typePair = icons.Find(item => item.iconType == type);
        if (typePair == null|| !typePair.prefab) return false;

        //实例化一个iconPair下的预制物体
        GameObject icon = Instantiate(typePair.prefab, transform);
        icon.gameObject.SetActive(true);

        //将预制物体添加到字典下对应的类型的列表中
        iconPairs[type].Add(new PointIconPair(icon.transform,target));
        return true;
    }

    //清除所有图标
    public void Clears()
    {
        foreach(List<PointIconPair> list in iconPairs.Values)
        {
            if (list == null) continue;
            foreach(PointIconPair iconPair in list)
            {
                Destroy(iconPair.icon.gameObject);
                Destroy(iconPair.target.gameObject);
            }
            list.Clear();
        }
    }
}

 

posted @ 2020-05-09 00:58  小辉歌  阅读(770)  评论(0编辑  收藏  举报