Unity通用框架搭建(八)—— 消息事件封装

2023-11-16

在游戏开发中,消息事件通过委托实现。实际开发中,经常会遇到界面之间、玩家之间的相互通信。本文实现的消息事件封装采用的时监听和分发机制,它主要运用在逻辑判断,减少程序之间的耦合度,而且使用起来非常方便。监听服务器消息也可以运用事件监听机制,收到服务器的信息之后可以执行某个事件。曾经维护过一个项目的网络通信相关逻辑全都写在NetManger中,到后期这个脚本一万多行,维护十分困难。合理的框架结构能够帮助我们降低程序的耦合性,使代码易于维护更新。

第一版不定参数设计

利用不定参params object[] args 我们只需要一个监听、一个移除、一个派发函数就可以完成事件的封装

public delegate void GameEventHandler(params object[] args);
 public static void ListenEvent(System.Enum type, GameEventHandler listener)
        {
            List<GameEventHandler> listeners;
            if (!eventListeners.TryGetValue(type, out listeners))
            {
                listeners = new List<GameEventHandler>();
                eventListeners.Add(type, listeners);
            }
            listeners.Add(listener);
        }

但是这样的设计存在一个问题,因为所有的参数类型都是object类型,不可避免的会有装箱/拆箱的操作

  • 装箱:将值类型转换为引用类型 ,以下是一个简单的装箱的过程
int val = 100; 
object obj = val; 
  • 拆箱:将引用类型转换为值类型 ,以下就是简单的拆箱的过程
int val = 100; 
object obj = val; 
int num = (int) obj; 
值类型和引用类型
  • 值类型:整型:Int;长整型:long;浮点型:float;字符型:char;布尔型:bool;枚举:enum;结构:struct;它们统一继承 System.ValueType
  • 值类型存储在内存栈中,引用类型数据存储在内存堆中,而内存单元中存放的是堆中存放的地址;
  • 值类型存取快,引用类型存取慢;
  • 值类型表示实际数据,引用类型表示指向内存堆中的数据的指针和引用;
  • 栈的内存是自动释放的,堆内存是.NET中会由GC来自动释放
  • 引用类型:数组,用户定义的类、接口、委托,object,字符串等

当 CLR 对值类型进行装箱时,会将该值包装为 System.Object 类型,再将包装后的对象存储在堆上。 拆箱就是从对象中提取对应的值类型的一个过程。
与简单的赋值操作相比,装箱和拆箱都需要进行大量的数据计算。对值类型进行装箱时,CLR 必须重新分配一个新的对象。拆箱所需的强制转换也需要进行大量的计算,两者相比,仅仅是程度不高,并且也可能会出现类型转换发生的异常情形。

第二版利用泛型重构

其实最初设计时为了代码简洁方便就直接使用了params object[] args,但是在整理代码重构时发现这里涉及到大量的装箱/拆箱的操作,因此还是得利用泛型来省掉这一部的操作。这里根据多年游戏开发的经验,设置了0~4个参数的监听处理。

using System.Collections.Generic;
using System;
  
public class GlobalEvent
{
    private static Dictionary<System.Enum, List<Delegate>> events = new Dictionary<Enum, List<Delegate>>();

    /// <summary>
    /// 添加监听事件
    /// </summary>
    /// <param name="type">监听事件</param>
    /// <param name="listener">监听回调</param>
    public static void Listen(System.Enum type, Action action)
    {
        List<Delegate> listeners;
        if (!events.TryGetValue(type, out listeners))
        {
            listeners = new List<Delegate>();
            events.Add(type, listeners);
        }
        listeners.Add(action);
    }
    /// <summary>
    /// 带一个参数的监听
    /// 用泛型避免使用 params object[] args 带来装箱和拆箱的性能损耗
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="type"></param>
    /// <param name="action"></param>
    public static void Listen<T>(System.Enum type, Action<T> action)
    {
        List<Delegate> listeners;
        if (!events.TryGetValue(type, out listeners))
        {
            listeners = new List<Delegate>();
            events.Add(type, listeners);
        }
        listeners.Add(action);
        
    }

    //两个参数监听回调
    public static void Listen<T,U>(System.Enum type, Action<T,U> action)
    {
        List<Delegate> listeners;
        if (!events.TryGetValue(type, out listeners))
        {
            listeners = new List<Delegate>();
            events.Add(type, listeners);
        }
        listeners.Add(action);
    }

    //三个参数监听回调
    public static void Listen<T,U,V>(System.Enum type, Action<T, U,V> action)
    {
        List<Delegate> listeners;
        if (!events.TryGetValue(type, out listeners))
        {
            listeners = new List<Delegate>();
            events.Add(type, listeners);
        }
        listeners.Add(action);
    }

    //四个参数监听回调
    public static void Listen<T, U, V,Z>(System.Enum type, Action<T, U, V,Z> action)
    {
        List<Delegate> listeners;
        if (!events.TryGetValue(type, out listeners))
        {
            listeners = new List<Delegate>();
            events.Add(type, listeners);
        }
        listeners.Add(action);
    }

    /// <summary>
    /// 移除所有监听
    /// </summary>
    /// <param name="type"></param>
    public static void Remove(System.Enum type)
    {
        List<Delegate> listeners;
        if (events.TryGetValue(type, out listeners))
        {
            events.Remove(type);
        }
    }

    /// <summary>
    /// 移除指定的监听
    /// </summary>
    public static void Remove(System.Enum type, Action action)
    {
        //移除指定的监听
        List<Delegate> listeners;
        if (events.TryGetValue(type, out listeners))
        {
            if (listeners.Contains(action))
            {
                listeners.Remove(action);
            }
            if (listeners.Count <= 0)
            {
                Remove(type);
            }
        }
    }

    public static void Remove<T>(System.Enum type, Action<T> action)
    {
        //移除指定的监听
        List<Delegate> listeners;
        if (events.TryGetValue(type, out listeners))
        {
            if (listeners.Contains(action))
            {
                listeners.Remove(action);
            }
            if (listeners.Count <= 0)
            {
                Remove(type);
            }
        }
    }

    public static void Remove<T,U>(System.Enum type, Action<T,U> action)
    {
        //移除指定的监听
        List<Delegate> listeners;
        if (events.TryGetValue(type, out listeners))
        {
            if (listeners.Contains(action))
            {
                listeners.Remove(action);
            }
            if (listeners.Count <= 0)
            {
                Remove(type);
            }
        }
    }

    public static void Remove<T,U,V>(System.Enum type, Action<T,U,V> action)
    {
        //移除指定的监听
        List<Delegate> listeners;
        if (events.TryGetValue(type, out listeners))
        {
            if (listeners.Contains(action))
            {
                listeners.Remove(action);
            }
            if (listeners.Count <= 0)
            {
                Remove(type);
            }
        }
    }

    public static void Remove<T, U, V,Z>(System.Enum type, Action<T, U, V,Z> action)
    {
        //移除指定的监听
        List<Delegate> listeners;
        if (events.TryGetValue(type, out listeners))
        {
            if (listeners.Contains(action))
            {
                listeners.Remove(action);
            }
            if (listeners.Count <= 0)
            {
                Remove(type);
            }
        }
    }

    /// <summary>
    /// 调用监听事件
    /// </summary>

    public static void Dispatch(System.Enum type)
    {
        List<Delegate> listeners;
        if (events.TryGetValue(type, out listeners))
        {
            foreach (var listener in listeners)
            {
                Action action = listener as Action;
                action.Invoke();
            }
        }
    }

    public static void Dispatch<T>(System.Enum type,T arg0)
    {
        List<Delegate> listeners;
        if (events.TryGetValue(type, out listeners))
        {
            foreach (var listener in listeners)
            {
                Action<T> action = listener as Action<T>;
                action.Invoke(arg0);
            }
        }
    }

    public static void Dispatch<T,U>(System.Enum type, T arg0,U arg1)
    {
        List<Delegate> listeners;
        if (events.TryGetValue(type, out listeners))
        {
            foreach (var listener in listeners)
            {
                Action<T,U> action = listener as Action<T,U>;
                action.Invoke(arg0,arg1);
            }
        }
    }

    public static void Dispatch<T, U,V>(System.Enum type, T arg0, U arg1,V arg2)
    {
        List<Delegate> listeners;
        if (events.TryGetValue(type, out listeners))
        {
            foreach (var listener in listeners)
            {
                Action<T, U,V> action = listener as Action<T, U,V>;
                action.Invoke(arg0, arg1,arg2);
            }
        }
    }

    public static void Dispatch<T, U, V,Z>(System.Enum type, T arg0, U arg1, V arg2,Z arg3)
    {
        List<Delegate> listeners;
        if (events.TryGetValue(type, out listeners))
        {
            foreach (var listener in listeners)
            {
                Action<T, U, V,Z> action = listener as Action<T, U, V,Z>;
                action.Invoke(arg0, arg1, arg2,arg3);
            }
        }
    }
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Unity通用框架搭建(八)—— 消息事件封装 的相关文章

随机推荐

  • 导致项目失败常见的原因有哪些

    一 规划工作做得不细 或缺少灵活性 项目计划始终是项目开始前的热点讨论话题 但是在实际项目进行中仍会出现问题 因此在做好计划之后 在项目实施中项目经理还需要根据项目实际情况做适当的调整 项目经理可以通过进度猫甘特图制作项目计划 调整增加项目
  • 另类的唯品会和得物比价

    目前得物h5和小程序防爬弄得采集数据很费劲 另辟蹊径做了一款通过模拟器和商家后台方式采集数据的方式 目前实现了跟唯品会的比价 运行界面如下 输入品牌名称 点击 开始 表格中左边依次显示唯品会采集的数据 右边会根据采集模式的不同 从安卓模拟器
  • inux字符驱动之read、write部分

    本期主题 linux字符驱动之read write部分 往期链接 linux设备驱动中的并发 linux设备驱动中的编译乱序和执行乱序 linux设备驱动之内核模块 linux字符驱动 linux字符驱动之ioctl部分 linux字符驱动
  • 把手伸向大数据平台的云原生,是如何将大数据平台迁移至K8s上的?

    我们正身处数据大爆炸的时代 据IDC数据显示 仅在2022年 人类就将创造超过97ZB的数据 要知道截至2012年 人类生产的所有印刷材料的数据量为200PB 仅为2022年一年所创造数据量的50万分之一 据预测 中国数据量规模将从2022
  • TMS运输管理系统,对车辆管理、物流运输、效率提升有哪些优势?

    TMS Transport Management System 运输管理系统 其主要功能是对物流中的运输环节进行管理 此系统适用于运输公司 各企业下面的运输队等 系统对车辆 驾驶员 线路 运输方式等进行全面管理 通过系统调度管理 报表分析统
  • TypeScript学习笔记(三) - 方法

    本篇将介绍在TypeScript里如何定义和使用方法 一 方法标准声明和使用 1 方法声明 2 function func x number y number number 3 return x y 4 在TypeScript里 方法声明可
  • INSTALL_FAILED_INVALID_APK: /data/app/vmdl1735842778.tmp/11_app-debug signatures are inconsistent

    坑 背景 这两天无聊想整合一下SDK和Gradle里的东西 然后手贱 整出来一个错误 安装apk报错 INSTALL FAILED INVALID APK data app vmdl1735842778 tmp 11 app debug s
  • 按指定条件从C++11 map中删除元素

    问题 无法使用remove if配合map容器的erase操作按指定条件从map容器删除元素 C 11 解决方案 可以使用一下函数实现这个需求 template
  • pytorchnp.transpose(npimg, (1, 2, 0))

    最近看pytorch的一个代码 结果中间有一句还是看不太懂 最后过了一些阵子才看懂 在此Mark一下 1 代码如下 def imshow img text should save False npimg img numpy 将torch F
  • css实现图片旋转

    一 css实现图片旋转90度 icon moz transform rotate 90deg webkit transform rotate 90deg 二 水平翻转 icon color f64e78 font size 20px dis
  • 轨迹数据

    如何通过轨迹相似性度量方法 发现新冠易感人群 轨迹作为一种时空数据 指的是某物体在空间中的移动路径 通常表示为GPS点的序列 例如tr
  • 计算机的默认登录账户,win10如何设置默认登录账户_win10电脑设置默认账户登录的两种方法...

    我们在使用电脑时为了保护电脑资料的安全 一般会进行密码登录设置 但是有使用win1064位正式版系统的用户想要在使用电脑的时候自己的账户可以进行自动登录 而其他的账户保持原来的登录方式 那win10如何设置默认登录账户呢 下面小编就来为大家
  • python中__name__和__main__

    在学习python的时候 会经常看到这样的一句话 if name main 那么这句话是什么意思呢 首先要解释一下python文件的两种使用方式 1 直接运行 就是直接运行 py文件 2 模块导入 就是被当成模块导入到其他python文件中
  • ssh 远程登陆指定端口

    ssh 到指定端口 ssh p xx user ip xx 为 端口号 user为用户名 ip为要登陆的ip SSH 原理及远程登录 http www ruanyifeng com blog 2011 12 ssh remote login
  • 基于java的个人博客系统的设计与实现

    源码下载 https wwa lanzous com iH3N7mjya2h 更多源码 http byamd xyz 文件目录 运行步骤 导入数据库 导入idea 修改数据库密码 效果
  • three.js物体纹理及其常用属性介绍

    一 Three中的纹理和材质介绍 THREE中的纹理和材质是用来渲染3D场景中的物体表面的 纹理贴图定义物体表面的颜色和外观 而材质则定义物体表面如何反射光线 纹理可以使用多种类型的图像文件 包括JPEG PNG GIF等 纹理可以是简单的
  • Python运维开发工程师养成记(python continue语句)

    图示 案例 下节分享python pass语句 链接 https ke qq com course 4300856 tuin d8aedf68
  • python常见相关组件汇总(个人学习)

    目录 1 生成6位数字随机验证码 2 md5加密 3 生成唯一token 4 发送手机验证码 实现逻辑 5 生成二维码 6 微信群发 7 微信自动回复 8 提取Django中model中的字段名变成字典 列表 9 数据库中给表创建数据 10
  • 11、Seata AT+SpringCloud+MyBatisPlus+Nacos进行分布式事务操作

    具体请看 http blog shangsw com articles 2020 03 28 1585382453622 html
  • Unity通用框架搭建(八)—— 消息事件封装

    在游戏开发中 消息事件通过委托实现 实际开发中 经常会遇到界面之间 玩家之间的相互通信 本文实现的消息事件封装采用的时监听和分发机制 它主要运用在逻辑判断 减少程序之间的耦合度 而且使用起来非常方便 监听服务器消息也可以运用事件监听机制 收