热乎的过万字GameFramework讲解笔记文档
Posted 芝麻粒儿
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了热乎的过万字GameFramework讲解笔记文档相关的知识,希望对你有一定的参考价值。
本文约15千字,新手阅读需要27分钟,复习需要12分钟 【收藏随时查阅不再迷路】
👉关于作者
众所周知,人生是一个漫长的流程,不断克服困难,不断反思前进的过程。在这个过程中会产生很多对于人生的质疑和思考,于是我决定将自己的思考,经验和故事全部分享出来,以此寻找共鸣 !!!
专注于Android/Unity和各种游戏开发技巧,以及各种资源分享(网站、工具、素材、源码、游戏等)
有什么需要欢迎私我,交流群让学习不再孤单。
👉前提
这是小空坚持写的Unity新手向系列,欢迎品尝。
小空为了方便更多的人(新手)看明白,使用的汉字,真实项目尽量使用英文规则昂。
新手(√√√)
大佬(√)
这段时间学习个Unity框架并且准备应用到实际项目中,但是因为作者神龙见首不见尾,没有什么比较详细的文档,学习困难,不过好在作者将框架所有内容开源了,再次记录下学习过程。
👉实践过程
😜Game Framework简介导读
问:她到底是个什么玩意?
答:Game Framework 是一个基于 Unity 引擎的游戏框架,主要对游戏开发过程中常用模块进行了封装,很大程度地规范开发过程、加快开发速度并保证产品质量。
支持5.3以上到现在所有的Unity版本。
问:那她到底有什么功能呢?
答:相当多哦,包含全局配置,日志等工具集,数据表维护,提示器,下载,事件,文件系统,有限状态机,本地化,网络,对象池,流程,资源管理,场景管理,游戏配置,声音,界面,Web请求。-详情参见文末功能备注(链接)
问:她能帮助我什么?
答:帮你快速开发项目以及搭建良好的产品框架,顺便提高自己的业务能力。
问:那应该怎么找到她,学习她呢?
答:Github地址- https://github.com/EllanJiang/GameFramework/
API手册- https://gameframework.cn/api/
😜初识Game Framework
完整的 Game Framework 内容包含三部分:
-
GameFramework – 封装基础游戏逻辑,如数据管理、资源管理、文件系统、对象池、有限状态机、本地化、事件、实体、网络、界面、声音等,此部分逻辑实现不依赖于 Unity 引擎,以程序集的形式提供。
-
UnityGameFramework.Runtime – 依赖 UnityEngine.dll 进行对 GameFramework.dll 的补充实现。为了方便兼容 Unity 的各个版本,此部分已经以代码的形式包含在 Unity 插件中。
-
UnityGameFramework.Editor – 依赖 UnityEditor.dll 进行对工具、Inspector 的实现。为了方便兼容 Unity 的各个版本,此部分已经以代码的形式包含在 Unity 插件中。
我们直接从官网下载unitypackage插件包,我的是2020.7.30版本。导入后如下图:
😜如何使用?
工程目录及作用如下:
- Libraries 存放 GameFramework.dll 核心框架和一些框架必需的第三方库(当前只有一个开源 zip 压缩算法库)
- Prefabs 存放 GameFramework.prefab 预制体,用于快速创建一个游戏框架启动场景
- Scripts 存放 UnityGameFramework 的全部 Runtime 和 Editor 代码
- Example.unity 是一个含有 GameFramework.prefab 预制体的空场景,作为游戏启动的场景
- ProcedureExample.cs 是一个示例流程代码文件,示例将以这个流程作为启动流程。
运行项目后发现Game场景有个浮动小窗口,这是框架提供的调试器窗口
- Console 选项卡在运行时(当然可以在移动设备上)默认按不同日志类型,以不同的日志颜色回显最近的日志。点击某条日志可以查看详细的日志和堆栈详请
- Information 选项卡显示设备硬件信息、游戏版本和资源信息,输入信息,传感器等
- Profiler 选项卡显示性能调试相关的信息,内存,对象池,网络等信息
- Other 选项卡可以用来配置调试框窗口的大小缩放(一般不用修改)、执行内存回收操作或者执行重启游戏操作等
作者英语不是很好,为了用起来快捷将里面部分英文翻译成了中文,看起来确实顺畅多了,哈哈
😜场景
我们先把从官方下载的.unitypackage包导入,然后新建一个场景对应新建一个脚本。
然后打开GameFramework/Prefabs目录,将里面的GameFramework预制体拖到Hierarchy视图里(预制体里面包含了多种基本组件)
接着打开【ScriptSceneStart】脚本,让她继承自【ProcedureBase】,看过案例教程后我们知道框架是用流程来管理的,所以我们的场景第一步就要继承【ProcedureBase】,让她成为流程。
之后我们将场景和框架中的流程绑定起来。
然后打开框架日志,运行程序即可看到效果
注:Unity已经有打印日志的工具了,为什么我们还要用框架中的呢。
因为我们不仅仅需要在编译器中查看,有时候发布出去后在其他平台或设备上查看日志就需要你自己写一套日志方案(这多不符合懒的思想啊)。既然框架提供了这些,为什么不用呢?现成的他不香吗?
难道我们游戏只有一个场景吗?不存在的,必然是多个场景存在,那多个场景在框架中又怎么切换呢?或者说怎么切换流程呢?
我们再新建个场景【Scene_Set】,脚本为【ScriptSceneSet】
运行场景【Scene_Start】后直接切换场景到【Scene_Set】,但是你会发现场景【Scene_Start】没有卸载
原因在于基础场景里挂载着框架的组件【GameFramework】,你用人家的框架,还想干掉人家的框架,开玩笑呢?销毁了你还框什么架,所以这个场景在整个程序运行过程中不销毁的。
除了这个普通的场景加载后或者切换场景后,发现也没销毁,原因是框架不会自动销毁,需要你主动销毁。
使用Scene.GetLoadedSceneAssetNames();可以获得加载的场景数组
Scene.UnloadScene(“场景资源名称”);来卸载,当然了框架会自动排除上面说的挂载着框架的场景
有一点要记得,切换场景的时候Scene.LoadScene(“这需要完整的路径和后缀名”,this);
思路接着往下走,场景切换后我们对应的控制和逻辑脚本也就应该切换到对应的了,所以我试了试:
就这张图的代码来看,切换场景后,下一个场景也有打印日志消息,但是实际的运行结果却没有,这令小空疑惑。而且去检查了流程组件的内容,都打对勾了。
等等,流程!既然是流程,那是不是得自己控制流程的切换,框架将场景切换和流程切换分开,留给开发人员实现,岂不有更多的实现空间?然后小空去翻找官方案例,果然有个继承自【ProcedureBase】的【ProcedureChangeScene】类,往下翻找到了关键句【ChangeState(procedureOwner);】,关键字【ChangeState】这个必定是切换流程的,至于切换到哪个就需要在<>符号里写进去了。回去改小空的Demo:
运行后:(成功,果然Nice,框架方便之处渐渐浮出)
😜UI
经过上一节搞定了场景,接着就要开搞UI了,UI也不是直接就在场景中全部展示出来了,有些需要我们动态从资源中加载。
我直接上个步骤图,看的更形象一些。
- 代码中用UIComponent组件加载UI预制体,重点注意第二个参数,将UI进行了分组和层级管理
- 回到编辑器中找到GameFramework-UI物体,里面有个UI Groups属性要设置
- 预制体需要挂载脚本,脚本需要继承UIFormLogic脚本,这是UI控制脚本,这样才能交给框架进行管理
这一套下来,真的是如行云流水般顺畅,越来越方便了。继续学习看看框架到底还有多少惊喜。
顺畅个屁啊,小空又跑回来更新了,学到后面发现怎么UI也出不来,哎,就是跟你玩,就是不出。不小心踩了个坑。
UI,UI,都说了是UI,我竟然没搞Cnavas,糊涂啊。根据上图步骤,第三步预制体是你布局好UI控件然后将整个Canvas做成预制体。成功后是这样的(下图):
还有一点要留意,运行后你会发现在场景里并没有找到UI组件,其实不然,框架通过UIComponent 创建的的UI 统统都会跑到Framework 里面 UI下面。无论你打开几个场景UI始终都是在框架所在场景内的。
如上图所示,全部在【UI-UI Form Instances】下
😜日志管理工具
在任何编程语言中,日志都是重中之重,是逻辑分析的首要工具,所以我们首要任务就是要学会使用框架的日志工具。
首先是基础的Log日志(如图),和系统的一样输出到Unity编辑器的控制台
发布的产品,最好关闭所有日志或者仅开启错误及以上级别日志,自己开发调试的时候可以自定义开启日志级别。
除了在控制台打印,我们使用框架更多的可能是将日志保存到文件的功能。
自定义一个类继承DefaultLogHelper
using GameFramework;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEngine;
using UnityGameFramework.Runtime;
internal class FileLogHelper : DefaultLogHelper
//设置日志文件保存路径-你可以自定义,也可以使用系统的
private readonly string CurrentLogPath = Utility.Path.GetRegularPath(Path.Combine(Application.persistentDataPath, "current.log"));
private readonly string PreviousLogPath = Utility.Path.GetRegularPath(Path.Combine(Application.persistentDataPath, "previous.log"));
public FileLogHelper()
Application.logMessageReceived += OnLogMessageReceived;
try
//每次运行的时候将日志替换,就像队列一样
if (File.Exists(PreviousLogPath))
File.Delete(PreviousLogPath);
if (File.Exists(CurrentLogPath))
File.Move(CurrentLogPath, PreviousLogPath);
catch
private void OnLogMessageReceived(string logMessage, string stackTrace, LogType logType)
string log = Utility.Text.Format("[0][1] 2434", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"), logType.ToString(), logMessage ?? "<Empty Message>", stackTrace ?? "<Empty StackTrace>", Environment.NewLine);
try
File.AppendAllText(CurrentLogPath, log, Encoding.UTF8);
catch
然后在挂在了Base脚本的物体中选择使用即可,就这么简单!!!
运行的时候,也可以在调试器窗口中找到各个路径位置。
😜事件订阅
既然咱要用她就一定要先说说她的优点:
她简化了组件间的通信,分离了事件的发送者和接收者,大大降低了耦合度。举个栗子:
前段时间的【黑悟空】实机演示,比如悟空的状态,经验值,怪物的血量等内容,会随时有着对应的变化,比如怪物死亡,人物获得经验以及结束判断等等,一次事件发生N多事件要对应变化,要是一个个主动修改你调我/我调你的,调来调去逻辑线不混乱才怪,又不玩捆绑S。所以我们要由主动变被动,我发个广播,谁想听我号令谁就注册下,不想听你不注册没影响,解耦效果显著。小空是android出身,Android里的EventBus订阅用的贼爽,所以第一时间就学习了这个框架的订阅事件
这就达到了,某件事件变化,执行了注册的地方会收到消息,自己处理自己的逻辑。被订阅者发,多个订阅者接收。
来来来,我们看看到底怎么使用;
😜第一步
需要先获得Event组件
EventComponent eventComponent = UnityGameFramework.Runtime.GameEntry.GetComponent();
😜第二步
订阅事件
eventComponent.Subscribe(OpenUIFormSuccessEventArgs.EventId, InitUISuccess);
订阅事件第一个参数是内置的,【OpenUIFormSuccessEventArgs】是初始化打开界面成功事件,第二个参数是你自己声明的函数,界面完成后会触发这个函数。官方还有更多内置函数,详情看下方表格图。
😜第三步
加载UI
😜第四步
在回调函数中打印下,看看有没有接收到消息。
😜第五步
记得在OnLeave方法解除注册昂。你都离开这个流程了,就不要再费心去监听别的事情了。否则会造成内存垃圾昂。
运行结果:
成了。就这么简单?爱作妖的小空这时候又有想法了:实际开发不可能这么简单,可能有N多个UI,N多个时间,这要是多了又是什么样的?不急,接着试验
一运行发现哎都回调了方法啊。这个不错,如果再利用第四个参数传递自定义数据,返回后根据不同的数据做不同的处理岂不快哉。
666,但是还有个疑问,仔细看上面【OpenUIForm】里面的第三个参数-优先级,我尝试输入不同的级别运行后发现并没有想象中的那样【这是一只猫】最先执行。小空就去赶紧看了官方案例,又有发现:官方案例区分的是资源优先级-字体、声音音乐/场景等这种大类别。应该不是用于小小的同级(比如都是UI)。这个留待后续再深入。
小空学到这感觉基本使用可以了,这和Android的EventBus类似啊,在以后的实践学习中希望能发现框架也有EventBus的粘性事件和跨组件监听。补充一个框架内置组件图:
怎么样?使用起来是不是很简单?仿佛在那遥远的山村听到了有人说,
😜创建实体
经历前面文章创建UI的过程,这一步操作起来就顺心多了,坑也少了。
😜第一步
同样需要创建预制体(物体模型)
😜第二步
预制体上需要挂载继承了【EntityLogic】的脚本
😜第三步
走走走,预制体出来我们就要开始创建了,代码先获取实体组件再创建实体
这和UI基本如出一辙昂,再回忆回忆和UI都是预制体,传递的参数都是预制体路径和分组。等等-分组,差点忘记了需要在框架的基础组件中添加分组
小空在学习木头前辈的教程中留意到:这种带有路径的预制体加载方式,实际打包后会不会出现调用不到的问题。给出的答案是:不管是打不打包,框架都会处理好,无需操心。
第四个参数需要注意下,她可以传递【this】,进而在控制中调用流程中的方法。
官网案例也是这么做的:【ProcedureMenu】流程打开UI【GameEntry.UI.OpenUIForm(UIFormId.MenuForm, this);】传递的是【this】,进而【MenuForm】中可以在【OnOpen】方法获取这个流程类【m_ProcedureMenu = (ProcedureMenu)userData;】,这样就可以调用流程中的方法,比如切换另一个流程,事实上官网也是这么做的。
😜第四步
分组机制管理器很多物体超级方便,不可缺少
组里面还有四个参数,小空暂时还没学到这些,保留知识。
【instance auto release inerval】-实例自动释放间隔
【instance auto release inerval】-实例容量
【instance expire time】-实例过期/失效时间
【instance priority】-实例优先级
😜第五步
运行成功
在这强调下,时刻要记得创建的东西都是在【GameFramework】框架组件下。
😜第六步
操作起来怎么这么顺利,就没遇到个让人劈叉的问题?
小空还真疏忽了,在UI中就说过创建后一定要在框架下写上分组,在这小空又忘了,报错如下:不过小空现在已经记住了,时刻保持组名和代码中的参数保持一致。
在这进了一个坑(不承认我学艺不精),找搞C++的姐请教后实践,详情请看文章:
快速代码补全-不同关键字权限,不同程序集自动生成的有微差别
补充:当一个物体有灯光信息的话,不适合做成预制体,这会导致从预制体初始化出来的物体效果不对。
原因是:在一个物体或者光源从场景中变成一个prefab的时候,那些和烘焙相关的信息被重置,是一个稳妥的做法,因为你做成一个prefab,意味着它可能被使用于不同场景,那残留的之前的烘焙信息也就没有意义了。反过来说,如果保留,你在别的场景中apply了这些信息,那其他场景的效果就错了。无法通用的信息,保留在prefab中,是危险的。
烘焙信息是跟着场景走的,如果一个物体或者光源和场景没有关联了,那它身上存储的烘焙信息也就没有意义了。
所以推荐布置在场景中,用切换流程切换场景来实现,普通的物体可以设置成预制体。官方案例也是这么搞的。
👉其他
📢作者:小空和小芝中的小空
📢转载说明-务必注明来源:https://zhima.blog.csdn.net/
📢这位道友请留步☁️,我观你气度不凡,谈吐间隐隐有王者霸气💚,日后定有一番大作为📝!!!旁边有点赞👍收藏🌟今日传你,点了吧,未来你成功☀️,我分文不取,若不成功⚡️,也好回来找我。
温馨提示:点击下方卡片获取更多意想不到的资源。
花一天时间做一个高质量飞机大战游戏,过万字Unity完整教程!漂亮学妹看了直呼666!
以上是关于热乎的过万字GameFramework讲解笔记文档的主要内容,如果未能解决你的问题,请参考以下文章
花一天时间做一个高质量飞机大战游戏,过万字Unity完整教程!漂亮学妹看了直呼666!
如何在二三线城市月薪过万5年面试的经验,万字吐血总结2021年java面试题
如何在二三线城市月薪过万5年面试的经验,万字吐血总结2021年java面试题