草庐IT

热乎的过万字GameFramework讲解笔记文档

芝麻粒儿 2023-04-14 原文
往期文章分享

本文约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也不是直接就在场景中全部展示出来了,有些需要我们动态从资源中加载。
我直接上个步骤图,看的更形象一些。

  1. 代码中用UIComponent组件加载UI预制体,重点注意第二个参数,将UI进行了分组和层级管理
  2. 回到编辑器中找到GameFramework-UI物体,里面有个UI Groups属性要设置
  3. 预制体需要挂载脚本,脚本需要继承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}] {2}{4}{3}{4}", 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/
📢这位道友请留步☁️,我观你气度不凡,谈吐间隐隐有王者霸气💚,日后定有一番大作为📝!!!旁边有点赞👍收藏🌟今日传你,点了吧,未来你成功☀️,我分文不取,若不成功⚡️,也好回来找我。

温馨提示点击下方卡片获取更多意想不到的资源。

有关热乎的过万字GameFramework讲解笔记文档的更多相关文章

  1. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  2. Matlab imread()读到了什么 (浅显 当复习文档了) - 2

    matlab打开matlab,用最简单的imread方法读取一个图像clcclearimg_h=imread('hua.jpg');返回一个数组(矩阵),往往是a*b*cunit8类型解释一下这个三维数组的意思,行数、数和层数,unit8:指数据类型,无符号八位整形,可理解为0~2^8的数三个层数分别代表RGB三个通道图像rgb最常用的是24-位实现方法,即RGB每个通道有256色阶(2^8)。基于这样的24-位RGB模型的色彩空间可以表现256×256×256≈1670万色当imshow传入了一个二维数组,它将以灰度方式绘制;可以把图像拆分为rgb三层,可以以灰度的方式观察它figure(1

  3. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  4. Ruby 等同于 Sphinx 文档生成器? - 2

    Ruby有一些不错的文档生成器,例如Yard、rDoc,甚至Glyph。问题是Sphinx可以做网站、PDF、epub、LaTex等。它在重组文本中完成所有这些事情。在Ruby世界中有替​​代方案吗?也许是程序的组合?如果我也能使用Markdown就更好了。 最佳答案 自1.0版以来,Sphinx有了“域”的概念,它是从Python和/或C以外的语言标记代码实体(如方法调用、对象、函数等)的方法。有一个rubydomain,所以你可以只使用Sphinx本身。您唯一会缺少的(我认为)是Sphinx使用autodoc从源代码自动创建文档

  5. ruby-on-rails - 在 irb 中阅读文档 - 2

    我怀念ipython的一件事是它有一个?为特定功能挖掘文档的运算符。我知道ruby​​有一个类似的命令行工具,但是我在irb中调用它非常不方便。ruby/irb有类似的东西吗? 最佳答案 Pry是IPython的Ruby版本,它支持?命令来查找有关方法的文档,但语法略有不同:pry(main)>?File.dirnameFrom:file.cinRubyCore(CMethod):Numberoflines:6visibility:publicsignature:dirname()Returnsallcomponentsofthef

  6. ruby - 使用 Nokogiri 和 Ruby 从 html 文档获取链接和 href 文本? - 2

    我正在尝试使用nokogirigem提取页面上的所有url及其链接文本,并将链接文本和url存储在散列中。FooBar我想回去{"Foo"=>"#foo","Bar"=>"#bar"} 最佳答案 这是一个单行:Hash[doc.xpath('//a[@href]').map{|link|[link.text.strip,link["href"]]}]#=>{"Foo"=>"#foo","Bar"=>"#bar"}拆分一点可以说更具可读性:h={}doc.xpath('//a[@href]').eachdo|link|h[link.t

  7. ruby - 如何让 Nokogiri 解析并返回 XML 文档? - 2

    这是一些奇怪的例子:#!/usr/bin/rubyrequire'rubygems'require'open-uri'require'nokogiri'print"withoutread:",Nokogiri(open('http://weblog.rubyonrails.org/')).class,"\n"print"withread:",Nokogiri(open('http://weblog.rubyonrails.org/').read).class,"\n"运行此返回:withoutread:Nokogiri::XML::Documentwithread:Nokogiri::

  8. 100个python算法超详细讲解:画直线 - 2

    1.问题描述使用Python的turtle(海龟绘图)模块提供的函数绘制直线。2.问题分析一幅复杂的图形通常都可以由点、直线、三角形、矩形、平行四边形、圆、椭圆和圆弧等基本图形组成。其中的三角形、矩形、平行四边形又可以由直线组成,而直线又是由两个点确定的。我们使用Python的turtle模块所提供的函数来绘制直线。在使用之前我们先介绍一下turtle模块的相关知识点。turtle模块提供面向对象和面向过程两种形式的海龟绘图基本组件。面向对象的接口类如下:1)TurtleScreen类:定义图形窗口作为绘图海龟的运动场。它的构造器需要一个tkinter.Canvas或ScrolledCanva

  9. Unity Shader 学习笔记(5)Shader变体、Shader属性定义技巧、自定义材质面板 - 2

    写在之前Shader变体、Shader属性定义技巧、自定义材质面板,这三个知识点任何一个单拿出来都是一套知识体系,不能一概而论,本文章目的在于将学习和实际工作中遇见的问题进行总结,类似于网络笔记之用,方便后续回顾查看,如有以偏概全、不祥不尽之处,还望海涵。1、Shader变体先看一段代码......Properties{ [KeywordEnum(on,off)]USL_USE_COL("IsUseColorMixTex?",int)=0 [Toggle(IS_RED_ON)]_IsRed("IsRed?",int)=0}......//中间省略,后续会有完整代码 #pragmamulti_c

  10. Tcl脚本入门笔记详解(一) - 2

    TCL脚本语言简介•TCL(ToolCommandLanguage)是一种解释执行的脚本语言(ScriptingLanguage),它提供了通用的编程能力:支持变量、过程和控制结构;同时TCL还拥有一个功能强大的固有的核心命令集。TCL经常被用于快速原型开发,脚本编程,GUI和测试等方面。•实际上包含了两个部分:一个语言和一个库。首先,Tcl是一种简单的脚本语言,主要使用于发布命令给一些互交程序如文本编辑器、调试器和shell。由于TCL的解释器是用C\C++语言的过程库实现的,因此在某种意义上我们又可以把TCL看作C库,这个库中有丰富的用于扩展TCL命令的C\C++过程和函数,所以,Tcl是

随机推荐