Unity2018打开GameFramework遇到的问题&GameFramework解读

本文深入解析GameFramework框架核心架构,涵盖GameFrameworkModule与GameFrameworkComponent双基类体系、XXComponent-XXManager分层设计、UI资源异步加载流程(EditorResourceComponent/ResourceManager)、状态机与Procedure系统、配表加载机制、事件系统及对象池(ObjectPool<T>)实现。同时说明Unity2018兼容问题:asmdef引用配置、UnityWebRequest API变更及VS工具链适配方案。

首先是GameFramework的Editor程序集找不到GameFramework命名空间,需要在它的asmdef文件添加对GameFramework和UnityGameFramework.Runtime程序集的引用。

UnityGameFramework.Runtime程序集也要引用GameFramework程序集。

然后UnityWebRequest在2018没有PostWWWForm函数,先改成Post。然后发现没有自动重新编译。发现PackageManager里没有Visual Studio的插件,Unity Registry里也没有。

查资料知道Visual Studio的插件在2019才出现。2018 不靠 Registry 装 VS 支持,是外部 IDE 模式 + VSTU(Visual Studio Tools for Unity)。

让我在Unity Hub安装Microsoft Visual Studio Community 2017。我有Visual Studio2022,不想装2017,于是换Unity2022。

最根的非Behavior类:GameFrameworkModule

是不继承MonoBehavior的管理器的基类。很多XXManager都是继承它和一个同名接口。

有抽象方法Update,由所有模块的管理器GameFrameworkEntry调用。

最根的Behavior类:GameFrameworkComponent

我们发现GameFrameworkComponent继承MonoBehavior,一大堆XXComponent继承它。那么很明显GameFrameworkComponent就是MonoBehavior型管理器的基类。

XXComponent-XXManager架构

关于GameFramework:UI

我追踪一个UI面板的关闭方法,调用了UIComponent.CloseUIForm,里面调用了UIManager.CloseUIForm,UIComponent持有IUIManager成员。点进它的方法,进入的是接口的声明,定义要去同名实现里。

IUIManager接口,只有一个实现,UIManager,由5个脚本写成。里面使用异步加载,有正在加载的UI集合,有加载完就要销毁的UI集合。

有一个UIGroup,它下面有IUIForm。

我想看释放UI资源的具体实现,一直找到这里。这里让类型参数必须是一个接口。但是后面是用Activator.CreateInstance反射创建对象。接口怎么创建对象?

注意这里获得moduleName,从第一个字符(也就是从1数第2个)截取,其实是把接口的I去掉得到类型名。

public static T GetModule<T>() where T : class
{
    Type interfaceType = typeof(T);
    if (!interfaceType.IsInterface)
    {
        throw new GameFrameworkException(Utility.Text.Format("You must get module by interface, but '{0}' is not.", interfaceType.FullName));
    }

    if (!interfaceType.FullName.StartsWith("GameFramework.", StringComparison.Ordinal))
    {
        throw new GameFrameworkException(Utility.Text.Format("You must get a Game Framework module, but '{0}' is not.", interfaceType.FullName));
    }

    string moduleName = Utility.Text.Format("{0}.{1}", interfaceType.Namespace, interfaceType.Name.Substring(1));
    Type moduleType = Type.GetType(moduleName);
    if (moduleType == null)
    {
        throw new GameFrameworkException(Utility.Text.Format("Can not find Game Framework module type '{0}'.", moduleName));
    }

    return GetModule(moduleType) as T;
}

这个框架有大量的接口,唯一的实现就是去掉I的同名类。包括UIManager、ObjectPoolManager都继承GameFrameworkModule和同名接口。接口定义必须实现的函数、属性。GameFrameworkModule方便上层统一调用。

ObjectPoolManager是生成各种对象池的管理器。

UIManager销毁UI是把它放进一个回收队列,在Update里处理。Update里调用了ObjectPool.Unspawn(这里写的是接口IObjectPool,要去实现类里找方法实现)。里面调用了GF封装的Object.Unspawn。

对象的封装

被管理的对象都继承ObjectBase,而ObjectPool缓存的是Object<T>,Object<T>里面有具体被管理对象。

资源的编辑器异步加载:EditorResourceComponent

我想到,我们想看资源加载的调用链,直接用VS代码搜索相关的API如AssetDatabase.LoadAssetAtPath,加打印,那么它就会把调用栈全打印出来。而不用从上层跳转十八层去找底层API。于是找到了EditorResourceComponent。但是打印并没有很长的调用链。

在EditorResourceComponent里看到,它是在Update里取出链表,加载,加载后执行加载成功回调。那么请求资源方就是往链表里加节点,并且把使用的函数加入回调。

往链表里加节点是LoadAsset做的,查找它的引用,发现它被7个重载引用。

真正被框架调用的只有最后一个重载。调用它的有UI、实体、Item、声音、数据模块。

然后我们看UI模块对资源模块的调用,在UIManager.OpenUIForm,同样一个干活的函数被7个重载调用,还被UIComponent调用。

所以加载一个UI的调用链是:

UIComponent.OpenUIForm>UIManager.OpenUIForm>EditorResourceComponent.LoadAsset放入链表,Update里处理。

那么就在LoadAsset里打印,能看见完整调用栈

拿到加载资源的完整调用栈了,接下来怎么读?从上往下还是从下往上?

资源的编辑器异步加载:ResourceManager

IResourceManager接口有两个实现,一个是编辑器资源加载,另一个是ResourceManager。ResourceManager由27个partial脚本组成。

在DefaultResourceHelper找到了使用UnityWebRequest异步加载的代码。

框架Update调用链

BaseComponent.Update>GameFrameworkEntry.Update遍历GameFrameworkModule链表,调用Update>

其他Component未实现Update函数。GameEntry也不参与。

把GameFrameworkModule加入GameFrameworkEntry:

XComponent持有XManager,在Awake里为自己持有的管理器注册加入GameFrameworkEntry的链表。

状态机系统

Procedure系统

在检查器显示了一堆流程,还有一个入口流程选项。这些应该是CustomEditor写的,于是代码搜索CustomEditor。找到了在\Assets\GameFramework\Scripts\Editor\Inspector

这些流程的名字是获取ProcedureBase的所有子类得到的。有多少子类这里就显示多少名字。

这个检查器不会显示默认显示的字段,因为它继承GameFrameworkInspector,这个检查器就没有执行绘制默认检查器。所以它完全按OnInspectorGUI里的代码绘制。

这里用SerializedProperty包裹流程列表和入口流程。

初始时通过名字找到要包裹的属性。

SerializedProperty有一系列API得到它包裹的任何类型的值,包括字符串值、数组长度、数组任意元素。

private void ReadAvailableProcedureTypeNames()
{
    m_CurrentAvailableProcedureTypeNames = new List<string>();
    int count = m_AvailableProcedureTypeNames.arraySize;
    for (int i = 0; i < count; i++)
    {
        m_CurrentAvailableProcedureTypeNames.Add(m_AvailableProcedureTypeNames.GetArrayElementAtIndex(i).stringValue);
    }
}

配表加载流程

配表都有一个基类,程序获得配表基类的所有子类,得到要加载的配表列表。

资源路径

注意到预制体路径不是放在那一类物品的配表,而是配表里有一个资源Id,另有一个所有资源的Id-路径配表。

事件系统

事件类的继承关系,从子类:

GameEventArgs>BaseEventArgs>GameFrameworkEventArgs>EventArgs。

EventArgs是C# System的类。

再梳理关键类:GameFrameworkModule、GameFrameworkComponent、ObjectBase、Object<T>、ObjectPool<T>、ObjectPoolBase、GameFrameworkEntry、GameEntry

那么到此我们知道这个框架:

所有非Behavior管理器都叫XManager,继承GameFrameworkModule和同名接口;

所有Behavior管理器都叫XComponent,继承GameFrameworkComponent;

XComponent持有XManager;

XManager通过自定义的SetYManager持有对其他管理器的引用,一个管理器可以有多个方法和引用。

GameFrameworkEntry缓存和管理所有GameFrameworkModule,调用它们的Update;

GameEntry缓存和管理所有GameFrameworkComponent;

这就是所谓的专业框架,随便一个调用,调用链有18层函数,一个函数,五六个参数,不同参数组合要写出七八种重载。

我总结出一点读重载的技巧。一堆重载里面有一个是真正干活的,其他的只是调用那个。真正干活的就是参数最多的那个。

这个作者似乎很喜欢用重载,喜欢把一堆参数,有的没有的排列组合。似乎认为这是成熟代码的标志?

不能把参数打包成一个结构体,不想传的参数就不赋值?

启示

这个框架的异步加载启示我们,异步加载本质是往一个集合里添加加载请求,管理器在Update里处理加载。还可以控制Update里的工作量,进一步避免卡顿。

附训练好的 YOLO 权重与 PyQt 可视化检测界面,页面底部含可视化效果预览,毕设/课设开箱即用。 【数据集概况】 · 检测类别(中文):[二维码(QR CODE)] · 训练集:900 张 · 验证集:106 张 · 测试集:106 张 · 总计:1112 张 该数据集聚焦于日常商业场景中二维码的高精度识别需求,涵盖食品饮料标签、电子产品包装、物流单据及办公环境中的各类载体,具有高度现实应用价值,可有效支撑零售溯源、智能仓储、移动支付及防伪验证等实际业务流程。... 【训练曲线与评估图】 【模型训练配置】 参数 | 值 模型 | yolo26n 训练轮数 | 100 epochs 输入尺寸 | 640x640 批次大小 | 24 优化器 | auto 初始学习率 | 0.01 训练设备 【关键指标汇总】 训练了 36 个 epoch,最终轮指标: 指标 | 数值 mAP50 | **0.9137** mAP50-95 | 0.6307 Precision | 0.9145 Recall | 0.8365 train/box_loss | 1.2025 train/cls_loss | 0.5806 val/box_loss | 1.2323 val/cls_loss | 0.8246 【训练过程分析】 经过整整三个月的训练,终于在第36个epoch后,模型的mAP50数值达到了0.9137,这真是个挺让人欣慰的结果。但不得不说,这过程真是熬人。一开始,loss曲线像脱缰的野马,降速极快,让人信心满满;但到了中期,这野马却忽东忽西,走走停停,像是在山间迷路,让人焦头烂额。到了后期,loss曲线又渐渐平稳下来,像极了山河渐稳,让人松了一口气。最后,当所有指标趋于稳定,看着mAP50-95还徘徊在0.63... 使用场景:毕业设计、课程设计、深度学习入门、工业场景落地验证。
内容概要:本文围绕MongoDB在通信行业的深度应用,系统讲解了可查询加密、时间序列集合、窗口函数与性能诊断四大核心技术。通过详实的代码案例,展示了如何利用可查询加密实现敏感数据合规存储与查询,使用时序集合高效处理5G网络KPI等高频写入场景,借助窗口函数进行经营指标的趋势分析,并结合explain()与查询分析器完成性能调优。文章还涵盖分片一致性检查、索引优化等运维实践,全面覆盖通信系统对安全、性能与扩展性的严苛要求。; 适合人群:具备一定数据库基础,从事通信行业研发、架构或运维工作1-3年的技术人员,尤其是参与5G网络监控、客户数据管理或大数据平台建设的相关人员。; 使用场景及目标:① 实现用户手机号、身份证等敏感信息的加密存储与等值查询,满足安全合规要求;② 构建高吞吐、低延迟的5G基站KPI时序数据存储与分析系统;③ 在数据库层直接完成滑动平均、累计求和等经营分析,减少应用层计算负担;④ 诊断慢查询、验证索引有效性,保障系统高性能运行。; 阅读建议:此资源强调理论与实战结合,建议读者在本地或测试环境部署MongoDB 7.0+版本,动手复现文中代码,重点关注schemaMap配置、时序集合参数选择与窗口函数语法细节,并结合explain执行计划理解底层优化机制,从而真正掌握通信级数据架构的设计精髓。
基于线性车辆三自由度动力学+无迹卡尔曼滤波UKF估计模型(横摆角速度+质心侧偏角+车速)联合仿真内容概要:本文介绍了基于线性车辆三自由度动力学与无迹卡尔曼滤波(UKF)相结合的车辆状态估计联合仿真模型,重点实现对车辆横摆角速度、质心侧偏角和车速等关键状态参数的高精度估计。该方法利用三自由度动力学模型描述车辆横向、纵向及横摆运动特性,并通过UKF算法有效融合传感器测量信息,克服噪声干扰与非线性问题,提升状态估计的准确性与鲁棒性。文中还提及该仿真可用于车辆稳定性控制、自动驾驶感知系统等领域的研究与验证。; 适合人群:具备车辆动力学、控制理论及MATLAB/Simulink仿真基础,从事智能驾驶、车辆控制或状态估计相关研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①实现对难以直接测量的车辆状态(如质心侧偏角)的实时估计;②为车辆稳定性控制系统(如ESP、AFS)或高级驾驶辅助系统(ADAS)提供可靠的状态反馈;③作为车辆状态估计算法的研究与教学平台,验证非线性滤波方法在实际系统中的应用效果。; 阅读建议:建议结合车辆动力学理论与卡尔曼滤波原理进行学习,重点关注UKF在非线性系统中的实现细节,如状态方程与观测方程的构建、协方差矩阵的设置与调参,并通过仿真案例验证算法性能。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值