Unity红点系统的实现

Unity红点系统的实现

在使用Unity开发游戏的时候经常用到红点系统,当玩家点击之后,或者收到服务器数据之后,都需要刷新红点的显示。如果每个人都自己写自己的红点模块,会增加不少的重复任务量,因此迫切需要一个通用的红点系统,其他模块只需要编写自己模块的红点类型和对应的是否显示红点的判断即可。因此RedDotManager应运而生。

案例

下面通过一个邮件红点来演示如何操作使用。如下图,当点击系统邮件按钮和玩家邮件按钮之后,对应按钮上面的红点会消除,两个按钮分别点击之后,所有邮件按钮上面的红点才消除。
在这里插入图片描述

我们在每个按钮下面都拖上一个带有RedDotItem脚本的红点Prefab。

在这里插入图片描述

上图中的“Image”就是红点,我们只要在逻辑层控制该“Image”的显示即可。

图中可以看到该红点可以设置多个红点类型,也就是支持一个红点由多个含义。比如所有邮件按钮上的红点就是由系统邮件和玩家邮件一起控制显示,当任意一个返回true,显示红点的时候,该红点就需要一直显示。直到该RedDotTypes下面的所有逻辑都返回false的时候,红点才不显示。

红点类型–RedDotType

C#

/**
 * 红点类型的枚举;
 * RedDotType.cs
 * 
 * Created by Onelei 12/11/2017 10:28:04 AM. All rights reserved.
**/

namespace RedDot
{
    public enum RedDotType
    {
        None = 0,
        Email_UnReadSystem = 1,
        Email_UnReadPlayer = 2,
    }
}

红点组件–RedDotItem

我们看下红点身上的组件RedDotItem.cs

/**
 * 红点物体;
 * RedDotItem.cs
 * 
 * Created by Onelei 12/11/2017 10:21:47 AM. All rights reserved.
**/
using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;

namespace RedDot
{
    public class RedDotItem : MonoBehaviour
    {  
        /// <summary>
        /// 红点物体;
        /// </summary>
        public GameObject Go_RedDot;
        /// <summary>
        /// 红点类型;
        /// </summary>
        public List<RedDotType> redDotTypes;

        private bool bCachedRedDot = false;

        void Start()
        {
            //注册红点;
            RedDotManager.Instance.RegisterRedDot(redDotTypes, this);
            //设置红点;
            bool bRedDot = RedDotManager.Instance.Check(redDotTypes);
            SetData(bRedDot, true);
        }

        void OnDestroy()
        {
            //取消注册红点;
            RedDotManager.Instance.UnRegisterRedDot(this);
        }

        /// <summary>
        /// 设置红点显示;
        /// </summary>
        /// <param name="bRedDot"></param>
        public void SetData(bool bRedDot, bool bForceRefresh = false)
        {
            if (bForceRefresh)
            {
                Go_RedDot.SetActive(bRedDot);
                bCachedRedDot = bRedDot;
                return;
            }

            if (bCachedRedDot != bRedDot)
            {
                Go_RedDot.SetActive(bRedDot);
                bCachedRedDot = bRedDot;
            }
        }
        /// <summary>
        /// 获取当前物体挂载的所有红点;
        /// </summary>
        /// <returns></returns>
        public List<RedDotType> GetRedDotTypes()
        {
            return this.redDotTypes;
        }

    }
}

在Start和OnDestroy的时候,注册和反注册红点消息的监听。在Start的时候根据自身的红点类型来判断是否显示红点即可。此时红点的GameObject也注册进去了,所以,后面只要根据红点是否显示,直接在管理类里面控制刷新显示即可。

RedDotManager.cs是红点的管理类,该类主要用来将注册进来的红点使用Dictionary统一管理。

红点初始化–Initilize

        /// <summary>
        /// 初始化红点系统(注意只需要初始化一次);
        /// </summary>
        public void Initilize()
        {
           RedDotConditionDict.Clear();

            // 添加红点数据判断;
            RedDotConditionDict.Add(RedDotType.Email_UnReadSystem, new RedDot_EmailUnReadSystem());
            RedDotConditionDict.Add(RedDotType.Email_UnReadPlayer, new RedDot_EmailUnReadPlayer());

        }

我们需要一个通用的红点判断的基类,因此设计RedDotBase如下

/**
 * 红点数据类-基类;
 * RedDotBase.cs
 * 
 * Created by Onelei 12/11/2017 10:23:47 AM. All rights reserved.
**/

namespace RedDot
{
    public abstract class RedDotBase
    {
        /// <summary>
        /// 是否显示红点(true表示显示,false表示不显示;)
        /// </summary>
        /// <param name="?"></param>
        /// <returns></returns>
        public virtual bool ShowRedDot(object[] objs)
        {
            return false;
        }
    }
}

不同的红点判断继承该父类,子类重写ShowRedDot函数即可。我们看下红点判断函数的实现

/**
 * 未读邮件红点判断逻辑-玩家红点;
 * RedDot_EmailUnReadPlayer.cs
 * 
 * Created by Onelei 12/11/2017 10:25:00 AM. All rights reserved.
**/
using RedDot;

public class RedDot_EmailUnReadPlayer : RedDotBase
{
    public override bool ShowRedDot(object[] objs)
    {
        return MailManager.Instance.IsPlayerRedDot();
    }
}
/**
 * 未读邮件红点判断逻辑-系统红点;
 * RedDot_EmailUnRead.cs
 * 
 * Created by Onelei 12/11/2017 10:25:00 AM. All rights reserved.
**/
using RedDot;

public class RedDot_EmailUnReadSystem : RedDotBase
{
    public override bool ShowRedDot(object[] objs)
    {
        return MailManager.Instance.IsSystemRedDot();
    }
}

我们通过MailManager里面的数据类判断是否显示红点,ShowRedDot函数的参数是object数组,方便检查红点的时候传入参数。比如判断某个英雄是否可以升级,就可以传入英雄的ID。

红点注册–RegisterRedDot

        /// <summary>
        /// 添加红点界面;
        /// </summary>
        /// <param name="redDotType"></param>
        /// <param name="item"></param>
        private void RegisterRedDot(RedDotType redDotType, RedDotItem item)
        {
            if (RedDotObjDict.ContainsKey(redDotType))
            {
                RedDotObjDict[redDotType].Add(item);
            }
            else
            {
                List<RedDotItem> items = new List<RedDotItem>();
                items.Add(item);
                RedDotObjDict.Add(redDotType, items);
            }
        }

红点检查–Check是否显示

        /// <summary>
        /// 检查红点提示,内部调用;
        /// </summary>
        /// <param name="redDotType"></param>
        /// <returns></returns>
        private bool Check(RedDotType redDotType, object[] objs)
        {
            if (RedDotConditionDict.ContainsKey(redDotType))
            {
                return RedDotConditionDict[redDotType].ShowRedDot(objs);
            }
            return false;
        }

红点系统管理类–RedDotManager

完整代码如下:

/**
 * 红点系统管理类;
 * RedDotManager.cs
 * 
 * Created by Onelei 12/11/2017 10:21:47 AM. All rights reserved.
**/

using System.Collections.Generic;

namespace RedDot
{
    public class RedDotManager
    {
        private static RedDotManager _instance;
        public static RedDotManager Instance
        {
            get
            {
                if (null == _instance)
                {
                    _instance = new RedDotManager();
                }
                return _instance;
            }
        }

        /// <summary>
        /// 红点数据;
        /// </summary>
        Dictionary<RedDotType, RedDotBase> RedDotConditionDict = new Dictionary<RedDotType, RedDotBase>();
        /// <summary>
        /// 红点物体;
        /// </summary>
        Dictionary<RedDotType, List<RedDotItem>> RedDotObjDict = new Dictionary<RedDotType, List<RedDotItem>>();
        /// <summary>
        /// 初始化红点系统(注意只需要初始化一次);
        /// </summary>
        public void Initilize()
        {
           RedDotConditionDict.Clear();

            // 添加红点数据判断;
            RedDotConditionDict.Add(RedDotType.Email_UnReadSystem, new RedDot_EmailUnReadSystem());
            RedDotConditionDict.Add(RedDotType.Email_UnReadPlayer, new RedDot_EmailUnReadPlayer());

        }
         
        /// <summary>
        /// 注册红点;
        /// </summary>
        /// <param name="redDotType"></param>
        /// <param name="item"></param>
        public void RegisterRedDot(List<RedDotType> redDotTypes, RedDotItem item)
        {
            for (int i = 0; i < redDotTypes.Count; i++)
            {
                RegisterRedDot(redDotTypes[i], item);
            }
        }
        /// <summary>
        /// 取消注册红点;
        /// </summary>
        /// <param name="item"></param>
        public void UnRegisterRedDot(RedDotItem item)
        {
            Dictionary<RedDotType, List<RedDotItem>>.Enumerator itor = RedDotObjDict.GetEnumerator();
            while (itor.MoveNext())
            {
                List<RedDotItem> redDotItems = itor.Current.Value;
                if (redDotItems.Contains(item))
                {
                    redDotItems.Remove(item);
                    break;
                }
            }
        }

        /// <summary>
        /// 检查红点提示;
        /// </summary>
        /// <param name="redDotType"></param>
        /// <returns></returns>
        public bool Check(List<RedDotType> redDotTypes, object[] objs = null)
        {
            for (int i = 0; i < redDotTypes.Count; i++)
            {
                //只要有一个需要点亮,就显示;
                if (Check(redDotTypes[i], objs))
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 更新该类型的所有红点组件;
        /// </summary>
        /// <param name="redDotType"></param>
        /// <returns></returns>
        public void NotifyAll(RedDotType redDotType, object[] objs = null)
        {
            if (RedDotObjDict.ContainsKey(redDotType))
            {
                for (int i = 0; i < RedDotObjDict[redDotType].Count; i++)
                {
                    RedDotItem item = RedDotObjDict[redDotType][i];
                    if (item != null)
                    {
                        List<RedDotType> redDotTypes = item.GetRedDotTypes();
                        bool bCheck = Check(redDotTypes, objs);
                        item.SetData(bCheck);
                    }
                }
            }
        }
        #region private
        /// <summary>
        /// 添加红点界面;
        /// </summary>
        /// <param name="redDotType"></param>
        /// <param name="item"></param>
        private void RegisterRedDot(RedDotType redDotType, RedDotItem item)
        {
            if (RedDotObjDict.ContainsKey(redDotType))
            {
                RedDotObjDict[redDotType].Add(item);
            }
            else
            {
                List<RedDotItem> items = new List<RedDotItem>();
                items.Add(item);
                RedDotObjDict.Add(redDotType, items);
            }
        }
        /// <summary>
        /// 检查红点提示,内部调用;
        /// </summary>
        /// <param name="redDotType"></param>
        /// <returns></returns>
        private bool Check(RedDotType redDotType, object[] objs)
        {
            if (RedDotConditionDict.ContainsKey(redDotType))
            {
                return RedDotConditionDict[redDotType].ShowRedDot(objs);
            }
            return false;
        } 
        #endregion
    }
}

测试

编写一个测试函数,在Start函数里面添加按钮的回调

    // Use this for initialization
    void Start()
    {
        // 设置按钮回调;
        Button_EmailSystem.onClick.AddListener(OnClickEmailSystem);
        Button_EmailPlayer.onClick.AddListener(OnClickEmailPlayer);

    }

我们需要在按钮点击的时候,计算该红点类型所对应的的红点逻辑,同时刷新所有绑定了该红点类型的RedDotItem身上的红点。

    /// <summary>
    /// 系统按钮的回调;
    /// </summary>
    void OnClickEmailSystem()
    {
        // 点击之后表示邮件读取过了,设置邮件为已读状态;
        // (注意这里不能够设置红点的显隐,因为是有数据控制的,所以要控制数据那边的红点逻辑);
        // RedDot.RedDotManager.Instance.SetData(RedDot.RedDotType.Email_UnRead,false);

        RedDot.MailManager.Instance.SetSystemRedDot(false);
        RedDot.RedDotManager.Instance.NotifyAll(RedDot.RedDotType.Email_UnReadSystem);
    }

    /// <summary>
    /// 玩家按钮的回调;
    /// </summary>
    void OnClickEmailPlayer()
    {
        // 点击之后表示邮件读取过了,设置邮件为已读状态;
        // (注意这里不能够设置红点的显隐,因为是有数据控制的,所以要控制数据那边的红点逻辑);
        // RedDot.RedDotManager.Instance.SetData(RedDot.RedDotType.Email_UnRead,false);

        RedDot.MailManager.Instance.SetPlayerRedDot(false);
        RedDot.RedDotManager.Instance.NotifyAll(RedDot.RedDotType.Email_UnReadPlayer);
    }

总结

我们将红点的数据和表现分离,通过RedDotItem来控制红点的显示。RedDotItem是一个单独的Prefab,我们可以设计不同的红点,比如红点上面是否有数字等,都可以在一个RedDotItem上面进行扩展。同时还支持一个红点类型绑定在不同的Prefab上面,一个Prefab上面绑定多个红点。这样一对多的关系,我们使用Dictionary来方便管理,Key就是红点类型,Value是一个List,里面包含了该类型绑定的所有RedDotItem。只要取出来对应的红点类型的红点是否和之前不一样,不一样就刷新该Key对应的Value的所有RedDotItem红点的显示即可。

完整Unity工程地址:https://github.com/onelei/RedDotManager

  • 6
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
Unity红点系统是一种常用的游戏UI设计,主要用于提醒玩家当前有新的任务、奖励或其他未读信息需要处理。下面是一个简单的Unity红点系统设计: 1. 定义红点控件:在UI界面中添加一个红点控件,通常是一个小圆或小数字。该控件需要有一个唯一的名称,用于后续的操作。 2. 定义红点数据结构:为每个需要红点提醒的功能定义一个红点数据结构,包含以下信息: - 功能名称:用于标识该功能。 - 红点控件名称:与UI界面中的红点控件名称对应。 - 是否需要红点提醒:标识该功能是否需要红点提醒。 - 红点数量:如果需要显示数字红点,则需要记录具体的数量。 3. 定义红点管理类:创建一个红点管理类,用于管理所有的红点数据和UI界面上的红点控件。该类需要提供以下功能: - 添加红点数据:向红点管理类中添加新的红点数据。 - 更新红点状态:根据红点数据中的信息,更新UI界面上对应的红点控件状态。 - 监听红点变化:提供回调函数,当某个红点数据的状态发生变化时,通知相应的UI界面进行更新。 4. 使用红点系统:在需要使用红点系统的地方,调用红点管理类的方法添加红点数据和监听红点变化。当红点数据的状态发生变化时,红点管理类会自动更新UI界面上的红点控件状态。 通过以上步骤,就可以实现一个简单的Unity红点系统。当玩家有新的任务或奖励时,红点控件会自动提醒玩家,增强了游戏的交互性和用户体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值