UGUI进阶知识[五]飘字提示

UnityPackage下载连接
这次是在3D程序里面一个非常常见的飘字提示功能,说难也难 说不难也不难,主要实现的是下列一些功能。

  • 消息队列比较长的时候 飘字提示需要将比较旧的飘字直接弹出消失
  • 新消息出现时,旧消息的挤上效果
  • 消息结束时的上浮效果
  • 简单对象池
  • 简单UI适配

要注意的是当设置OperateAlertItem为某覆盖全屏的UICanvas物体之后,原点位置是在屏幕中心

这个版本的飘字提示还不适合通用,有一些毛病,在
UGUI进阶知识[九]飘字提示改进
这篇文章中已经改正

接下来是主要代码



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// 主要控制类
/// </summary>
public class OperateAlert : MonoBehaviour
{
    public GameObject prefOperAlrtItm;
    public GameObject uiCanvas;

    public float plusOneTime = 0.3f;
    public int topStartPosX = 0;
    public int btmStartPosX = 0;
    /// <summary>
    /// 右边弹出的飘字提升距离屏幕右边的距离
    /// </summary>
    public int rightStartPosXRightInterval = 100;

    int RightStartPosX
    {
        get {
            return (int)(uiCanvas.GetComponent<RectTransform>().rect.width / 2 - rightStartPosXRightInterval);
        }
    }

    public const int TOP_POS_FLAG = 1;
    public const int BOTTOM_POS_FLAG = 2;
    public const int RIGHT_POS_FLAG = 3;
    public const float DEFAULT_TIME = 1.5f;
    public const int DEFAULT_TEST_SIZE = 20;

    List<OperateAlertItem> restPools = new List<OperateAlertItem>();

    List<OperateAlertItem> rightItemList = new List<OperateAlertItem>();
    List<OperateAlertItem> bottomItemList = new List<OperateAlertItem>();
    List<OperateAlertItem> topItemList = new List<OperateAlertItem>();

    static List<MsgStruct> rightStructList = new List<MsgStruct>();
    static List<MsgStruct> bottomStructList = new List<MsgStruct>();
    static List<MsgStruct> topStructList = new List<MsgStruct>();

    float lastTopShowOneItemTime = 0;
    float lastBottomShowOneItemTime = 0;
    float lastRightShowOneItemTime = 0;
    MsgStruct showMsg;


    Coroutine updateItemCor;

    /// <summary>
    /// 控制入口函数
    /// </summary>
    /// <param name="msg"></param>
    /// <param name="pos"></param>
    /// <param name="time"></param>
    /// <param name="size"></param>
    public static void Show(string msg, int pos = TOP_POS_FLAG, float time = DEFAULT_TIME, int size = DEFAULT_TEST_SIZE)
    {
        MsgStruct ms = new MsgStruct();
        ms.showpos = pos;
        ms.msg = msg;
        ms.totalShowTime = time;
        ms.fontSize = size;
        AddToStructList(ms);
    }

    public static void Show(string msg, int pos)
    {
        Show(msg, pos, DEFAULT_TIME, DEFAULT_TEST_SIZE);
    }

    public static void Show(string msg)
    {
        Show(msg, TOP_POS_FLAG, DEFAULT_TIME, DEFAULT_TEST_SIZE);
    }

    OperateAlertItem GetOneItem()
    {
        OperateAlertItem item;

        if (restPools.Count > 0)
        {
            item = restPools[0];
            item.setEnable(true);
            restPools.RemoveAt(0);
            return item;
        }
        else
        {
            item = NewItem(showMsg.fontSize, showMsg.totalShowTime);
        }
        return item;
    }

    void ReturnOneItem(OperateAlertItem item)
    {
        item.setEnable(false);
        item.setLocation(10000, 0, 0);
        restPools.Add(item);
    }

    void Update()
    {
        UpdateList(bottomStructList, BottomStartY, bottomItemList, lastBottomShowOneItemTime, btmStartPosX);

        UpdateList(topStructList, TopStartY, topItemList, lastTopShowOneItemTime, topStartPosX);

        UpdateList(rightStructList, RightStartY, rightItemList, lastRightShowOneItemTime, RightStartPosX);
    }

    void UpdateList(List<MsgStruct> msgList, int StartY, List<OperateAlertItem> itemList, float lastShowTime, int startX)
    {
        if (msgList.Count > 0 && Time.realtimeSinceStartup - lastShowTime > plusOneTime)
        {
            lastShowTime = Time.realtimeSinceStartup;
            MsgStruct ms = msgList[0];
            OperateAlertItem item = GetOneItem();
            item.SetSizeAndTime(ms.fontSize, ms.totalShowTime);
            item.Init(ms.msg, StartY, Time.realtimeSinceStartup, startX);
            msgList.RemoveAt(0);
            //注意最新的是插到list的开头  之前的就在后面了
            itemList.Insert(0, item);
        }

        int count = 0;
        for (int i = 0; i < itemList.Count; i++)
        {
            if (itemList[i].IsEnd == true)
            {
                ReturnOneItem(itemList[i]);
                itemList.RemoveAt(i);
                //这里要进行i--的原因是itemList.RemoveAt(i)之后 如果不这样做 会调过一个元素
                //好像有四个元素的list 在遍历的时候移除了第二个元素 即list[1]
                //这时list[2] 会移动到list[1] list[3]会移动到list[2]
                //如果不进行i--则跳过了变化之后的list[1]
                i--;
            }
            else
            {
                itemList[i].update(count);
                count++;
            }
        }
    }

    static void AddToStructList(MsgStruct ms)
    {
        if (ms.showpos == TOP_POS_FLAG)
        {
            CheckAdd(topStructList, ms);
        }
        else if (ms.showpos == BOTTOM_POS_FLAG)
        {
            CheckAdd(bottomStructList, ms);
        }
        else if (ms.showpos == RIGHT_POS_FLAG)
        {
            CheckAdd(rightStructList, ms);
        }
    }

    static void CheckAdd(List<MsgStruct> list, MsgStruct msg)
    {
        bool canAdd = true;
        for (int i = list.Count - 1; i >= 0; i--)
        {
            if (list[i].totalShowTime == msg.totalShowTime
                && list[i].msg == msg.msg)
            {
                canAdd = false;
                break;
            }
        }
        if (canAdd)
        {
            list.Add(msg);
        }
    }

    OperateAlertItem NewItem(int size = 18, float totalShowTime = 1.5f)
    {
        GameObject go = Instantiate(prefOperAlrtItm);
        go.transform.parent = uiCanvas.transform;
        OperateAlertItem ori = go.GetComponent<OperateAlertItem>();
        ori.SetSizeAndTime(size, totalShowTime);
        return ori;
    }

    int BottomStartY
    {
        get
        {
            float h = uiCanvas.GetComponent<RectTransform>().rect.height;
            return (int)(- h / 2 + 125);
        }
    }

    int TopStartY
    {
        get
        {
            float h = uiCanvas.GetComponent<RectTransform>().rect.height;
            //Debug.LogError("h " + h);
            return (int)(h / 2 - 200);
        }
    }

    int RightStartY
    {
        get
        {
            return 15;
        }
    }
}

struct MsgStruct
{
    public string msg;
    public int showpos;
    public float totalShowTime;
    public int fontSize;
}




using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// 控制子类
/// </summary>
public class OperateAlertItem : MonoBehaviour
{
    const int space = 40;	        //上方文本之间相隔距离像素

    GameObject prefObj;
    readonly int _size;
    /// <summary>
    /// 整个显示过程的开始时间点
    /// </summary>
    float startShowProcessTime;
    int startPosY;

    int targetPosY;
    int curPos;
    int x = 0;

    /// <summary>
    /// 整个从开始显示到完全消失的时间范围
    /// </summary>
    float totalShowTime;

    /// <summary>
    /// 准备显示的时候 透明度由0变到1的过程的结束时间点
    /// </summary>
    const float alphaPlusFnshTime = 0.2f;

    /// <summary>
    /// 整个透明度为1的阶段的结束时间点
    /// </summary>
    float completelyFnshTime;

    /// <summary>
    /// 准备消失的时候
    /// 透明度由1到0的阶段的阶段时间范围
    /// </summary>
    const float alphaMinusDur = 0.2f;


    Text strText;
    public Text StrText
    {
        get
        {

            if (strText == null)
            {
                strText = GetComponentInChildren<Text>();

            }
            return strText;
        }
    }

    Image bgImg;
    public Image BgImg
    {
        get
        {

            if (bgImg == null)
            {
                bgImg = GetComponent<Image>();

            }
            return bgImg;
        }
    }


    /// <summary>
    /// 这里面的index其实会随着新来的信息的插入而增大
    /// 这样新来信息之后 
    /// 前面的信息会随着传入的表示它自己的index的增大而进行位置的更新 实现了挤上的效果
    /// </summary>
    /// <param name="index"></param>
    public void update(int index)
    {
        //deltaTime表示从开始显示的时间开始 过了多久 注释1
        float passTimeSinceStartShow = Time.realtimeSinceStartup - startShowProcessTime;

        //这里index越大表示越早出现的消息 
        //因为外部调用函数遍历的list对于新的元素是从0开始插入的
        //如果插入的元  素有很多  则把队列里面最新的4个之外的 展示item直接进入收尾阶段
        if (index > 3 && passTimeSinceStartShow < completelyFnshTime)
        {
            //这样做是为了让后面的update中注释1的语句得到的passTimeSinceStartShow是进入阶段3
            startShowProcessTime = Time.realtimeSinceStartup - completelyFnshTime;
            passTimeSinceStartShow = completelyFnshTime;
        }

        float alphaRatio = 0;
        if (passTimeSinceStartShow < alphaPlusFnshTime) //阶段1 在准备显示的透明度增加阶段 
        {
            targetPosY = startPosY + index * space;
            alphaRatio = passTimeSinceStartShow / alphaPlusFnshTime;
            updateAlpha(alphaRatio);
        }
        else if (passTimeSinceStartShow < completelyFnshTime) //阶段2 在完全显示的阶段
        {
            updateAlpha(1);
            targetPosY = startPosY + index * space;
        }
        else if (passTimeSinceStartShow < totalShowTime) //阶段3 收尾阶段 渐隐滑出屏幕
        {
            targetPosY = startPosY + 50 + index * space;
            //oriRatio表示的是真正的结束进程的比例
            float oriRatio = (passTimeSinceStartShow - completelyFnshTime) / alphaMinusDur;
            //ratio是为了实现oriRatio从0到1的过程中 实现透明度从1到0变化的转变
            alphaRatio = 1 - oriRatio;
            updateAlpha(alphaRatio);
        }

        //这部分相当于mathf.lerp函数
        curPos = (int)(curPos + (targetPosY - curPos) * 0.2f);

        setLocation(x, curPos);
    }

    internal void setEnable(bool v)
    {
        gameObject.SetActive(v);
    }

    public void setLocation(int x, int y)
    {
        transform.localPosition = new Vector3(x, y);
    }

    public void setLocation(int x, int y, int z)
    {
        transform.localPosition = new Vector3(x, y, z);

    }

    public virtual void Init(string str, int startPosY, float startShowTime, int startPosX = 0)
    {

        //txt.text = str;
        startShowProcessTime = startShowTime;
        this.startPosY = startPosY;
        curPos = startPosY - 50;

        x = startPosX;
        setLocation(startPosX, curPos);
    }


    protected virtual void updateAlpha(float value)
    {
        StrText.CrossFadeAlpha(value, 0, true);
        BgImg.CrossFadeAlpha(value, 0, true);
    }

    public bool IsEnd
    {
        get
        {
            return Time.realtimeSinceStartup - startShowProcessTime > totalShowTime;
        }
    }

    public void SetSizeAndTime(int size, float totalShowTime)
    {
        StrText.fontSize = size;
        this.totalShowTime = totalShowTime;
        completelyFnshTime = this.totalShowTime - alphaMinusDur;
    }
}


©️2020 CSDN 皮肤主题: 游动-白 设计师:上身试试 返回首页