前言

游戏开发中,我们会创建各种各样的管理器,可以说每一个模块我们就得创建一个管理器,例如ResourceManager、UIManager、DataManager、LogManager等等,作者之前项目是各种Manager继承字MonoBehaviour,然后挂在Hierarchy上,这样有一个弊端,我们每添加或者删除一个manager就要保存一次场景,做Unity开发的都知道git或者svn协同开发,唯独scene是不好合并的,所以如果涉及到频繁修改场景是非常不好的设计,其次是场景树中一堆Manager的节点,感觉不太雅观。

老的Manager继承的代码:

/// <summary>
/// 直接挂在UI上的MonoBehaviour单例
/// </summary>
/// <typeparam name="T"></typeparam>
public class SingletonMonoBehaviour<T> : MonoBehaviour where T : MonoBehaviour
{private static T _instance;public static T Instance{get{if (_instance == null){GameObject singleton = new GameObject("(singleton)" + typeof(T));_instance = singleton.AddComponent<T>();DontDestroyOnLoad(singleton);Debug.Debugger.Log("[Singleton] An instance of " + typeof(T) +" is needed in the scene, so '" + singleton +"' was created with DontDestroyOnLoad.");}else{Debug.Debugger.Log("[Singleton] Using instance already created:" + _instance.gameObject.name);}return _instance;}}
}

框架启动

一般框架启动就是实例化各种Manager,下载或者加载数据表这些操作。拿加载Manager举例,笔者之前公司项目在游戏启动的时候好多好多行Manager的加载实例

并没有截全,项目越大,Manager加载的行数越多,当你看到项目中甚至好几十行这种类似的代码,总会觉得有一点点“恶心”从框架层面来讲,我觉得框架应该将耦合性降到最低,如果我想实现一个新的Manager,我只要写一个C#脚本,程序启动的时候就自动加载并且创建了实例,甚至不用去刻意写添加方法,这就用到C#的利器特性加反射来实现。

优化加载方案

老早之前笔者一直有个疑惑,我们都知道继承接口是为了让类具备接口里面的功能,为啥我们实现了接口方法,这个类就具备了这个方法的功能呢?最常见的就是Unity中我们要实现一个卡牌拖动,那我们让这个卡牌继承IDragHandler接口实现接口方法OnBeginDrag、OnDrag、OnEndDrag等方法,那么这张卡就可以被拖动了,感觉很神奇,有一种想看源码实现的冲动,但Unity不开源,我们想看却看不到,这里指的看不了是针对小白程序,下面我就来举例GFFramework加载Manager也有类似的原理实现,介绍之后你就明白Unity底层是怎么实现的了。
需求:我们想实现自动加载程序自启动模块。

定义IGameStart接口

顾名思义,这接口就是模块启动接口,凡是继承改接口的模块(类)都具备启动功能(实现接口的Start方法)。

namespace GFFramework.GameStart
{public interface IGameStart{void Start();void Update();void LateUpdate();}
}

Start方法就是启动方法,至于Update和LateUpdate方法这个我是想放在Unity的MonoBehaviour生命周期中去管理,也就是说整个工程中只有一个类继承自MonoBehaviour,在这一个类里各个生命周期的方法去调用各个模块的生命周期的方法,一方面是因为我们下面要介绍的C#热更方案ILRuntime不支持继承MonoBehaviour的类的更新,还有一方面是好多好多脚本都继承MonoBehaviour可控性差。

继承IGameStart接口的类
using Game.UI;
using GF.Debug;
using GFFramework.GameStart;
using System.Reflection;
namespace Game
{[GameStartAtrribute(0)]public class LocalGameStart : IGameStart{public void Start(){Debugger.Log("本地代码启动!");Debugger.Log("准备启动热更逻辑!");//启动程序//GameObject.Find("GFFrame").GetComponent<GFLauncher>().Launch();var types = Assembly.GetExecutingAssembly().GetTypes();foreach (var t in types){M_UIManager.Inst.CheckType(t);}M_UIManager.Inst.Init();//加载并显示1号窗口M_UIManager.Inst.LoadWindows(1);M_UIManager.Inst.ShowWindow(1);}public void Update(){}public void LateUpdate(){}}
}

这个类就做了一些准备工作和加载第一个Window。

如何加载这些继承自IGameStart接口的类呢?
var types = Assembly.GetExecutingAssembly().GetTypes();
var istartType = typeof(IGameStart);
foreach (var t in types)
{if (t.IsClass && t.GetInterface("IGameStart") != null){var attr = t.GetCustomAttribute(typeof(GameStartAtrribute), false);if (attr != null){var gs = Activator.CreateInstance(t) as IGameStart;gs.Start();GFLauncher.OnUpdate += gs.Update;GFLauncher.OnLateUpdate += gs.LateUpdate;}}
}

上面的代码写在程序启动的入口代码上,反射获取本地所有程序集,然后找到继承自IGameStart的类,然后创建这些类的实例,并且调用了他们的初始化Start方法,看到这里我们回头想想刚刚抛出的疑惑?为啥继承自接口的类实现了接口方法,那么这个类就具备了接口功能,底层就是这样通过反射实现的。

特性

看看上面的程序,其实还有一个通过特性来查找的判断,为何要这样设计呢,我们反射查找到继承自某个接口的类,如果还要细分,可以通过特性来判断查找,上面的逻辑就是当满足继承自IGameStart接口的类并且有我们自定义的GameStartAttribute特性标记,我们才创建该类的实例并且调用它的Start方法。如果特性不了解的同学可以看下特性介绍。

iOS JIT和反射

笔者刚做Unity开发的时候也是有这个观念,Unity在iOS平台上不允许用反射代码,这是iOS的限制,后来发现并不是这样,iOS限制的是JIT, just in time, 即时编译。虽然iOS平台允许用反射,但涉及到业务逻辑的时候并不推荐用反射代码,因为会有一定的效率问题。
我们写的Unity程序在安卓上运行的好好的,导出到iOS平台就会有报错,这是Unity开发者会经常碰到的问题,最典型的的报错就是:
ExecutionEngineException: Attempting to JIT compile method 'XXXX' while running with --aot-only.
笔者上个项目才碰到这样的报错,原因是引用了Reflection.Emit命名空间下的代码,重写了类的ToString方法,在打印这个类的对象的时候iOS会卡死报错,就是因为触发了jit。所以如果你们项目中有Reflection.Emit这样的代码,要小心谨慎!
至于详细的JIT方面的知识,可以阅读小匹夫的谁偷了我的热更新?Mono,JIT,iOS

反射的原理

.net所编写的程序集包含两个重要部分IL和metadata,我们写好的代码很多类很多成员在编译的时候都把这些信息记录在元数据表里面,反射就是将这个过程反过来,通过元数据记录的关于类的信息找到该类的成员,并能使它“复活”(因为元数据里所记录的信息足够详细,以至于可以根据metadata里的信息找到该类的IL code加以利用)。
举例:

Type t = typeof(System.string);
MemberInfo[] mis = t.GetMembers();
foreach (MemberInfo mi in mis){Console.WriteLine(mi.MemberType + mi.Name);
}

以上代码就会获的关于String类的所有信息。

管理器

管理器基类
/// <summary>
/// Manager基类
/// </summary>
/// <typeparam name="T">Manager类型</typeparam>
/// <typeparam name="V">被管理者所用的类标签</typeparam>
public class ManagerBase<T,V>:IMgr  where T: IMgr, new()where V: ManagerAtrribute
{static private T i;static public T Inst{get{if (i == null){i = new T();}return i;}}protected ManagerBase (){this.ClassDataMap = new Dictionary<string, ClassData>();}protected Dictionary<string, ClassData> ClassDataMap{get;set;}virtual  public void CheckType(Type type){var attrs = type.GetCustomAttributes(typeof(V), false);if (attrs.Length > 0){var attr = attrs[0];if (attr is V){var _attr = (V)attr;SaveAttribute(_attr.Tag, new ClassData() { Attribute = _attr, Type = type });}}}virtual public void Init(){}virtual public void Start(){}virtual public void Update(){}public ClassData GetCalssData(string typeName){ClassData classData = null;this.ClassDataMap.TryGetValue(typeName, out classData);return classData;}public void SaveAttribute(string name, ClassData data){this.ClassDataMap[name] = data;}public T2 CreateInstance<T2>(string typeName , params object[] args) where T2 : class{var type = GetCalssData(typeName).Type;if (type != null){if (args.Length == 0){return Activator.CreateInstance(type) as T2;}else{return Activator.CreateInstance(type,args) as T2;}}else{return null;}}
}

主要流程:
1.框架启动会拿到所有的程序集Assembly里面的所有Type
2.检查所有管理器找到继承自ManagerBase的管理器
3.实例化继承自ManagerBase的管理器
4.对应的管理器会根据V的类型标签中的Type去自动拿到Type信息
5.对应的管理器自己实现自己的业务逻辑
这样自动注册流程就完成了注册

UIManager & SceneViewManager

UIAttribute & ScreenViewAttribute & ManagerAttribute

这样定义特性标签之后,我们UI资源就可以根据设置标签来指定类型和位置,例如GameWindow界面

就通过UI标签来指定类型和指定资源路径

GFFramework地址

https://github.com/dingxiaowei/GFFrameWork

更多教程

http://dingxiaowei.cn/

【GFFrameWork】管理器和框架启动相关推荐

  1. postfix 部署ssl后还是25_宝塔面板的邮局管理器Postfix无法启动解决办法

    今天群里有童鞋的宝塔邮局管理器的Postfix无法启动,所以这篇文章就来水这个,如果碰到Postfix无法启动的问题,可以参考本教程~~ 1.宝塔邮局管理器 说实话,这玩意自己用用足够了,而且配置简单 ...

  2. linux的多重启动管理器,使用多重启动管理器GRUB引导Linux系统.pdf

    维普资讯 2007年 (第35卷)第6期 信患事L爿' 使用 多重启 动 管理器 GRUB ,导 Linux系统 白伸伸 (兰州职业技术学院 信息工程系,甘肃 兰州 730000) 擅要:GRUB是一 ...

  3. win7 启动管理器修改默认启动项

    我的DELL XPS14z 安装的win7 64位家庭高级版, 嘿嘿,随后免费升至旗舰版了,升级方法网上很多哈!我就不多说了. 最近给我的超级本安装了GGhost做了系统备份,以防万一,但是备份完成后 ...

  4. java日志——修改日志管理器配置+日志本地化

    [0]README 0.1) 本文描述+源代码均 转自 core java volume 1, 旨在理解 java日志--修改日志管理器配置+日志本地化 的相关知识: [1]修改日志管理器配置 1.1 ...

  5. 打开文件管理器_大批量图纸管理的贴心助手好管家,参照管理器,你值得拥有...

    原创:就说我在开发区 趣说CAD,让枯燥生动起来. 参照管理器 在开始今天的内容之前,先来看一则沃兹尼亚克对乔布斯在推出iPad过程中所起作用的评价. 沃兹尼亚克何许人也,能评价乔布斯当然也非无名之辈 ...

  6. php 进程管理,PHP 进程管理器 PHP-FPM

    php-fpm是什么 php-fpm是PHP的一个进程管理器.php下面的众多work进程皆有php-fpm进程管理器管理. php-fpm的工作原理 php-fpm全名是PHP FastCGI进程管 ...

  7. php-fpm进程的用户组,一文看懂PHP进程管理器php-fpm

    php-fpm是什么 php-fpm是PHP的一个进程管理器.php下面的众多work进程皆有php-fpm进程管理器管理. php-fpm的工作原理 php-fpm全名是PHP FastCGI进程管 ...

  8. php进程原理_PHP进程管理器php-fpm的工作原理

    PHP进程管理器php-fpm的工作原理 发布时间:2020-07-21 17:46:39 来源:亿速云 阅读:133 作者:小新 今天小编给大家分享的是PHP进程管理器php-fpm的工作原理,相信 ...

  9. php5启动子,看懂PHP进程管理器php-fpm

    php-fpm是什么 php-fpm是PHP的一个进程管理器.php下面的众多work进程皆有php-fpm进程管理器管理. 相关学习推荐:PHP编程从入门到精通 php-fpm的工作原理 php-f ...

最新文章

  1. java r$_基于javacv的人脸检测Demo
  2. Dev-C++ 5.11安装教程
  3. 前端笔记—第15篇js中的DOM操作
  4. SonarQube中的Maven项目的单元和集成测试报告
  5. Android 2.3 r1 中文 API (57) —— ScaleGestureDetector
  6. pwm驱动电机 为什么pwm不能太快_认识直流电机的PWM驱动控制电路
  7. JavaScript 数组拼接打印_JavaScript数组_创建数组_一维数组_length(二十三)
  8. 60-100-024-使用-MySQL 表锁
  9. vue 存储对象 不要监听_Vue源码解析----响应式原理
  10. vue 离开页面时间_vue 前端页面无操作时,系统退出登录的定时器设计
  11. activity finish后没有destroy_Activity 基础知识点
  12. tensorflow线下训练SSD深度学习物体检测模型,C++线上调用模型进行识别定位(干货满满)
  13. html5新标签笔记,HTML5新标签学习笔记
  14. 嵌入式接口之GPIO驱动LED的实验(附完整代码和工程以及详细的调试过程)
  15. 斯坦福李飞飞教授:人口普查不用上门,谷歌街景加深度学习就搞定
  16. PTA数组后五道演讲比赛中有10个评委打分(实型数据,十分制分数)一维数组中,然后输入欲删除数x,最后删除数组中值为x的元素并输出,键盘输入一个4×4阶的矩阵,编程输出它的转置矩阵。
  17. 视频监控系统中的流媒体服务器、直写和全切换三种取流架构方案
  18. Spring源码分析之BOP/IOC/DI/AOP
  19. 在校大学生如何申请软著,手把手教会你(内有免费模板)
  20. 如何优雅关闭 Netty服务

热门文章

  1. 如何在Windows上安装Ghost
  2. b级计算机机房标准,B级电子信息系统机房技术要求(国标GB50174–2008)
  3. to string在C语言中,C++中的to_string()函数[C++11支持],to_string11
  4. [转载]Android开发真实谎言:个人无空间 无奈搞吸费
  5. 打印机设置默认为黑白
  6. 计算机毕设之基于Java的保险业务管理系统的设计与实现
  7. 解决本地从 github上传或下载代码时每次都需要输入用户名和密码
  8. R语言实战应用精讲50篇(三十)-R语言实现支持向量机(附R语言代码)
  9. vue的生命周期钩子是什么?
  10. 杭州网络实名制遇冷:真实身份无规可查