【游戏面包屑】位运算的一种精妙方式

位运算的一种精妙应用


‘|’核心:单一|组合:(相同类型数据)扩展组合


‘&’核心:单一&组合:筛选是否存在相同点(该单一是否存在在该组合)


因为位运算是基于二进制形式的数据。因此可以通过枚举(或其他int类型的数据结构)来进行这种精妙的应用,只需给对象一个标志符(int类型的即可),将该标识符进行位运算即可做到 组合与筛选。
例子:过滤器模式,可以给过滤器的过滤类多一个标识符(如id)。用户在需要过滤时传入 过滤规则组合(‘|’运算组合的id)
然后在过滤算法对标识符(id)进行‘&’筛选出需要的过滤规则即可。


优点:非常灵活(在现有的基础上有新的需求,可以在不改变核心代码的情况下,仅通过改变自定义的组合来满足需求)。


缺点:增加复杂度,且后续维护相对不容易(其核心类可能会越来越臃肿)。而且二进制的位数是有限的,这代表无法无限扩展。


适用场景:适合一些相对比较简单、容易改变的 组合、筛选 的应用场景。并且应用场景在未来的扩展是有限的。
 


实际应用

例子:比如在一个需要显示各种资源剩余情况的UI。而在不同的界面需要显示不同的资源剩余情况UI。在商店需要显示当前金币;在好友列表需要显示亲密度;在属性界面需要显示当前可用属性点和金币。此时使用其灵活的组合就能快速满足需求的实现了。

过程:

   1.设计信息显示导航栏预设体与类

         将导航栏所有需要显示的信息都放在预制体中(需要划分好左右等结构)。在脚本中对应设计该预制体的类UINavigation。

         当一个Panel需要显示导航栏中的部分信息(如金币、属性点),就直接在Panel创建放置的位置对象,在该对象下生成该导航栏类,并激活(create()方法-创建预设体设置父类等)。然后调用这个类并将需要的显示的信息进行激活SetStyle()

   2.设计需要显示导航栏的UI类

         在UI类中新建UINavigation类,并进行设置需要显示UI(style)即可


代码:

预设体例子:

public enum UINavigationStyle
    {

        //每个新的基数单位必须是前一个基数单位的2倍(平方)
        //目的:保证组合后的枚举转换的二进制数的每一位都代表一个基数枚举(避免组合重叠紊乱)
        //例如:需要一个组合枚举为Name1_2_5_6 = Name1|Name2|Name5|Name6 = 1|2|16|32 = 2^0+2^1+2^4+2^5 = 1+2+16+32 = 51。
        //所以下一个数必须保证所有之前的数的位运算-或运算符要小于下一个数。即2的平方。

        //针对枚举再通过泛型进行&运算来解析是否包含该枚举。
        //转为int(二进制&运算),判定该位(枚举)是否为1(存在)。来返回bool值。此时可以使用该bool进行一些Active的设置等操作


        None = 0x000,               //0
        Name1 = 0x001,              //2^0
        Name2 = 0x002,              //2^1
        Name3 = 0x004,              //2^2
        Name4 = 0x008,              //2^3
        Name5 = 0x010,              //2^4 = 16,进一位
        Name6 = 0x020,              //2^5 = 32
        Name1_5 = Name1|Name5,
        Name1_2_5_6  = Name1 | Name2 | Name5 | Name6,
    }

未完成的类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using static GlobalPropertyMgr;

/// <summary>
/// 信息显示-导航栏
/// 说明:与UIPath读取的预设体对应,并读取目标预设体的所有组件
/// </summary>
public class UINavigation
{
    public GameObject Go;
    private static string UIPath = "UINavigation";                  //UI组件预设体资源路径

    private UINavigationStyle style = UINavigationStyle.None;       //默认显示设置

    /// <summary>
    /// 初始化数据
    /// </summary>
    private void Init()
    {
        //根据生成的UINavigation预设体进行绑定组件
        //...
    }

    /// <summary>
    /// 数据清除
    /// </summary>
    private void Clear()
    {
        //将注册的事件移除
        //...
    }

    /// <summary>
    /// 创建UINavigation
    /// 备注:创建后不再使用时用Clear()进行数据、事件清除
    /// </summary>
    /// <param name="parent">要绑定的父对象</param>
    /// <returns></returns>
    public static UINavigation Create(Transform parent)
    {
        //尝试创建组件
        GameObject prefab = Resources.Load<GameObject>(UIPath);
        UINavigation navigation = new UINavigation();
        navigation.Go = GameObject.Instantiate(prefab, parent);
        //预设体的组件绑定
        navigation.Init();
        return navigation;
    }

    /// <summary>
    /// 根据style设定UINavigation
    /// </summary>
    /// <param name="style">需要的组合枚举</param>
    public void SetStyle(UINavigationStyle style)
    {
        //自定义显示的部分UI与当前显示设定的不同
        if(this.style!= style)
        {
            //判定是否需要重置
            if (true)
            {
                //重置旧的UI设定
                _UnSetStyle(style);
                //重新设定UI
                _SetStyle(style);
            }

            //更新当前显示设定为自定义显示设定
            this.style = style;
        }

    }//SetStyle_end


    private void _SetStyle(UINavigationStyle style)
    {
        //对style进行解析。逐个筛选,将style存在的每个目标的对应UI进行激活(Active=true),不存在的UI进行隐藏
        //...
        //使用ToolMgr的IsContain(style,目标对应的枚举)进行获取bool(如果目标枚举存在在style中)
        //将style存在的每个目标的对应UI绑定的事件进行添加(注册)
        //...
    }
    private void _UnSetStyle(UINavigationStyle style)
    {
        //将style存在的每个目标的对应UI绑定的事件进行移除(反注册)
        //...
    }

}//class_UINavigation_end

未完成的 筛选 方法(可以使用从元数据的泛型转int的方法)

    /// <summary>
    /// 判定一个枚举是否存在在组合的枚举中
    /// </summary>
    /// <typeparam name="T">枚举</typeparam>
    /// <param name="flags">组合枚举</param>
    /// <param name="flag">目标枚举</param>
    /// <returns></returns>
    public static bool IsContain<T>(T flags, T flag) where T : struct
    {
        //将T(枚举)转为int
        //...
        int flagsValue = 0;
        int flagValue = 0;
        //通过位运算:&,判定flag是否存在flags中。若存在则返回true
        return (flagsValue & flagValue)!=0;
    }

相关导航栏部分需要重构,上述代码就写了个框架。后续完成重构再进行实例演示。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值