勤做减法;不怕改动。
深入阅读,化为己用。
Config 全局配置:一种自己定义的只在软件内部流通;一种读取外部文件(.txt/.bytes)后再去使用
-> ConfigManager:GetBool/Int/Float/String Add/Remove/HasConfig 是通过字典进行的
Read/ParseData是通过DataProvider进行的
所以:通过GameEntry.Config.AddConfig添加的“全局”配置项只能在软件中使用(可跨场景),不会保存到本地
-> DefaultConfigHelper:这个有Read/ParseData的具体实现,从文本或字节解析之后会通过ConfigManager.AddConfig保存到字典去,读就是解析
-> ProcedurePreload:LoadConfig函数是加载全局配置项:DefaultConfig.txt,主要是保存的Menu和Main的场景编号,加载之后就可以通过Config.GetInt去取了
ps:1)Read/ParseData实现中的数据会存到ConfigManager的字典(m_ConfigDatas)中。
2)IDataProviderHelper被ConfigHelperBase、DataTableHelperBase、LocalizationHelperBase继承,数据表和本地化的逻辑应该和全局配置一致
DataNode 数据节点:树形结构
-> IDataNode:泛型T继承于Variable;Get/SetData;有父有子
ps:并不能像Config一样跨场景使用:场景A Variable v = new VarInt32();
v.SetValue(1);
GameEntry.DataNode.SetData(“1”,v);
场景B Debug.LogWarning($"{GameEntry.DataNode.GetData(“1”)}");
DataTable 数据表:从外部文件(.txt/.bytes)读取数据再去使用
-> DataTableBase:Add/Remove/HasDataRow定义 Read/ParseData和Config一样通过DataProvider进行
-> DataTable:有泛型的Read/ParseData实现,是对m_DataSet的一系列操作
-> IDataRow(GF) <- DataRowBase(UGF) <- DRAircraft(每一张数据表对应的类)这个中会有实现ParseDataRow
-> DataTableExtension:这个里面有创建数据表的实现
-> ProcedurePreload:LoadDataTable函数就是加载数据表文件
ps:DRAircraft(数据表对应的类)中有表数据,DataTableManager中有所有的数据表存在字典(m_DataTables)中
Debugger 调试器
-> 没啥好说的,用就得了;有啥想增减的,自己改。
Download 下载:从uri下载文件并保存到path
-> DownloadManager:Add/RemoveDownload等都是通过TaskPool进行,GetDownloadInfo是调用的m_TaskPool.GetTaskInfo
-> 有两种下载代理辅助器:WWW、WebRequest,Unity2018.3之前的版本可以使用WWW
-> 通过DownloadSuccessEventArgs事件类传递的m_DownloadPath(文件保存路径),及相关资源如何下载使用的
VersionListProcessor/ResourceUpdater(OnDownloadSuccess 把数据写入到m_DownloadPath) -> DownloadManager(OnDownloadAgentSuccess 通过DownloadAgent.Task数据传递) ->
DownloadComponent(AddDownloadAgentHelper) -> DownloadAgent(Start) -> TaskPool(LinkedListNode.Value <- DownloadTask) ->
TaskPool(AddTask) -> ResourceLoader(LoadAsset/LoadDependencyAsset/LoadScene)
VersionListProcessor的UpdateVersionList中有调用:m_DownloadManager.AddDownload,这里m_DownloadPath是组装起来的:ReadWritePath+RemoteVersionListFileName
DownloadManager.AddDownload:创建一个DownloadTask并放入任务池中,返回下载任务的序列编号
在ResourceLoader中加载Asset/DependencyAsset/Scene的任务类继承关系:
IReference <- TaskBase <- DownloadTask
<- LoadResourceTaskBase <- LoadAssetTask
<- LoadDependencyAssetTask
<- LoadSceneTask
ps:这里DownloadComponent和DownloadManager的AddDownload不像WebRequest一样在流程中有直接调用,那这两个类暴露的接口意义何在?
Entity 实体:组件数据。Entity+Component+System(ECS)实体+组件+系统
-> 实体的显示/隐藏,依附/解绑
-> Star Force中定义了EntityData,再由具体的游戏物体去继承扩展,每一种物体对应的数据其实就是一张表
-> Star Force中定义了Entity,是继承于UGF的EntityLogic,再由具体的游戏物体去继承扩展,每一种物体的逻辑是根据游戏的玩法而来
ps:外部的数据,也就是游戏策划表可以按照框架读取,但是具体怎么使用则是玩法来定了。
Event 事件:主要管理框架的各种继承而来的XXXEventArgs
-> EventArgs/IReference <- GameFrameworkEventArgs <- BaseEventArgs <- GameEventArgs(定义的游戏事件基类)
-> EventManager:定义了一个事件池,其模式为:允许没有事件处理函数、允许有多个事件处理函数
订阅、取消订阅、分发、立刻分发都是通过事件池进行处理
事件池的轮询->管理类的轮询->游戏框架组件的轮询->基础组件的轮询
-> EventComponent:组件中的函数是调用的管理类的方法进行处理,组件获取方式:GameEntry.Event;管理类获取方式:GameFrameworkEntry.GetModule()
FileSystem 文件系统:1)区分不同的平台;2)从dat文件读取对应的文件信息
-> CommonFileSystemStream(GF)
-> AndroidFileSystemStream(UGF)
-> ResourceManager(GetFileSystem) -> FileSystemManager(GetFileSystem/LoadFileSystem) -> FileSystem(Load)
-> FileSystemComponentInspector:编辑器运行模式下,会在挂载FileSystemComponent的物体上额外绘制以下内容
1)文件系统数量
2)每个文件系统读取的路径+文件访问模式+文件数量+最大文件数量
每个文件系统读取的路径:1)单机包,从StreamingAssets来;2)网络包,从Persistent Data Path来
文件访问模式:Read;Write;ReadWrite
文件数量/最大文件数量:与ResourceEditor组织资源的形式有关,可以在ResourceCollection.txt中查看FileSystem所属命名
ps:Android的尚未测试,IOS的是使用CommonFileSystemStream?
Fsm 有限状态机(Finite state machine)
-> Fsm:Get/SetData 从字典中根据name得到Data/先从字典中根据name得到Data,如果Data不为空,则从引用池中释放,再存入字典
ChangeState 通过状态类型得到FsmState,离开当前状态,进行新状态
-> FsmState:这个与Procedure结合的很紧密,流程基类:ProcedureBase : FsmState
Localization 本地化:从外部文件(.txt/.bytes)读取数据再去使用
-> LocalizationManager:Has/Add/RemoveRawString/GetString都是通过字典m_Dictionary进行
Read/ParseData和Config、DataTable一样通过DataProvider进行
-> DefaultLocalizationHelper:这个有Read/ParseData的具体实现,但是StarForce读取本地化文件用的是XmlLocalizationHelper,这个根据文件内容格式自定义
-> XmlLocalizationHelper:这个有ParseData具体Xml文件的实现
ps:不论从哪个辅助器去实现的Read/ParseData,都是会存到LocalizationManager的字典(m_Dictionary)中。
NetWork 网络:协议TCP,兼容IPv4/IPv6,提供Socket长连接;接入protoBuf可派生自Packet
-> 根据不同的服务类型,进不同的TCP网络频道,连接/收/发是走的Socket那一套;可以根据协议类型扩展udp、kcp等
ps:需要扩充这个模块,并验证框架性。
ObjectPool 对象池:主要处理的游戏对象,比如Asset、Resource、UI等
-> ObjectPoolManager:创建允许单次/多次获取的对象池
-> ObjectPoolComponentInspector(StarForce):Entity + UI + HPBar + AssetObject + ResourceObject
Entity:EntityGroup -> IObjectPool m_InstancePool
UI:UIManager -> IObjectPool m_InstancePool
HPBar:HPBarComponent -> IObjectPool m_HPBarItemObjectPool
AssetObject:ResourceLoader -> IObjectPool m_AssetPool
ResourceObject:ResourceLoader -> IObjectPool m_ResourcePool
1)对象池基础属性绘制:Name + Type + AutoReleaseInterval + Capacity + Used Count + CanReleaseCount + ExpireTime + Priority
2)对象池中有对象时对象情况绘制:PoolObjectName + Locked + Count/InUse + Flag + Priority + LastUseTime
1.EventPool 事件池:主要处理的各种事件,继承自GameFrameworkEventArgs。
Subscribe/UnSubscribe:订阅/取消订阅事件;
Fire/FireNow:抛出/立刻抛出事件去分发处理;
EventPool的Event是通过ReferencePool创建:Event eventNode = ReferencePool.Acquire();
轮询处理事件,并在引用池中释放处理过的事件节点。
2.TaskPool 任务池:主要处理下载逻辑,比如Download、WebRequest等组件模块有使用。
Add/RemoveTask:增加/移除任务
ProcessRunningTasks/ProcessWaitingTasks:处理正在运行的任务/处理正在等待的任务
无论是什么池,都有一堆的存储数据结构去处理各种逻辑。
无论是对象池、事件池、任务池,创建和释放都是通过引用池进行的。
ReferencePool.Acquire() Reference.Release(T);
Procedure 流程
Launch -> Splash -> Preload(编辑模式) -> ChangeScene -> Main/Menu -> ChangeScene
-> InitResource(单机模式) -> Preload
-> CheckVersion(更新模式) -> UpdateVersion -> VerifyResources
-> VerifyResources -> CheckResources -> UpdateResources -> Preload
-> Preload
-> ProcedureBase:流程基类直接继承有限状态机ProcedureBase : FsmState,
重载方法的参数ProcedureOwner其实是IFsm
-> ProcedureManager:
-> ProcedureBase(OnUpdate) -> FsmState(OnUpdate) -> Fsm(Update) -> FsmManager(Update) -> GameFrameworkModule(Update) -> BaseComponent(Update)
-> ProcedureComponent:IEnumerator Start会创建流程示例,得到入口流程并从入口流程开始
-> ProcedureComponentInspector:绘制入口流程下拉框、可获取流程
ps:Star Force的示例流程是比较完整且规范的,可以以此为基础扩展自己的业务。
ReferencePool 引用池:万物皆引用
-> 有一个是否开启强制检查的开关,打开则会检查引用类型:不能为空、必须是非抽象类、必须有定义
-> ReferenceCollection:正在使用的引用数量、请求引用数量、释放引用数量、增加引用数量、移除引用数量;
Acquire:从引用的队列中Dequeue(出队)一个引用出来提供使用,如果队列中没有了,则生成一个;
Release:如果是强制检查并且队列中已经有这个引用了,则抛出异常;否则把该引用Enqueue(入队);
Add:把一定数量的引用Enqueue(入队);
Remove:把一定数量的引用Dequeue(出队)。
ps:这里的引用:1)IReference;2)T
Resource 资源:管理所有的资源类型的Verify、Update、Apply等
资源加载
编辑模式:ProcedurePreload -> AssetUtility
单机模式:.dat in StreamingAssets 使用Package单机包
更新模式:读取远端带crc32名称的GameFrameworkVersion.crc32.dat文件 使用Full远端更新包
【Resource Builder】
每一次打包之后Internal Resource Version都会+1,然后保存到GameMain/Config/ResourceBuilder.xml中
Working:工作路径(manifest,无版本)
Package:单机包,拷贝到StreamingAssets再打包App(dat Version 有版本)
Full:远端更新包,放到Web服务器(远端)
Packed:本地更新包,放到StreamingAssets(本地) 【已移除】
版本更新(增加了GameVersion比对)
调整BuildInfo.txt和WindowsVersion.txt的json串内容和格式,只需要软件版本和资源版本两个作为更新依据。
0.1.0(0) 主版本号.子版本号.阶段版本号(资源版本号)
当ResourceMode = Updatable时,进行版本更新
先比较软件版本的各个子级版本,如果远端的比本地的大,则更新;
在软件版本相同的情况下,比较资源版本号,如果远端的比本地的大,则更新;
Resource Sync Tools(资源同步工具)
Remove All Asset Bundle Names in Project:移除项目中所有ab名
Sync ResourceCollection.xml to Project:同步资源合集配置到项目
Sync ResourceCollection.xml from Project:从项目同步资源合集配置
ps:核心组件模块,编辑扩展的重点也是这个,与Pool、Download、WebRequest等有密切联系。
Scene 场景:对场景资源的加载/卸载
-> ProcedureChangeScene(OnEnter -> GameEntry.Scene.LoadScene) -> SceneComponent(LoadScene) -> SceneManager(LoadScene) ->
ResourceManager(LoadScene) -> ResourceLoader(LoadScene) -> 1)LoadSceneTask放到任务池中轮询处理;2)资源为准备好则使用资源管理器去更新
-> DefaultLoadResourceAgentHelper.cs的LoadAsset中:SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive),是附加加载的
Setting 游戏配置项:1)从GameFrameworkSetting.dat中存取数据;2)使用PlayerPrefs存取数据
-> SettingComponent:Has/RemoveSetting Get/SetInt Get/SetFloat Get/SetBool Get/SetString Get/SetObject
-> 调用关系:SettingComponent -> ISettingManager -> SettingManager -> ISettingHelper -> SettingHelperBase
-> DefaultSettingHelper
-> PlayerPrefsSettingHelper
-> DefaultSettingHelper:通过DefaultSetting中的SortedDictionary(m_Settings)进行存取
-> PlayerPrefsSettingHelper:通过Unity的PlayerPrefs完成对游戏项目的本地化存取,具体保存的路径可查看官方文档:
https://docs.unity3d.com/ScriptReference/PlayerPrefs.html
Sound 声音
-> DefaultSoundHelper(ReleaseSoundAsset) -> ResourceComponent(UnloadAsset) -> ResourceManager(UnloadAsset) -> ResourceLoader(UnloadAsset) ->
ObjectPool(Unspawn) -> 从ObjectMap中得到Object,如果内部对象不为空,则回收;如果对象池中对象数量大于池的容量且内部对象被获取的次数<=0,则释放。
-> DefaultSoundAgentHelper:Play/Resume 播放/恢复声音(有一个淡入的时间) Pause/Stop 暂停/停止声音(有一个淡出的时间)
每一个声音代理辅助器上都有一个AudioSource,且默认:PlayOnAwake = false; rolloffMode = AudioRolloffMode.Custom
-> DefaultSoundGroupHelper:继承于SoundGroupHelperBase,有一个AudioMixerGroup
-> SoundComponentInspector:绘制了三个辅助器、SoundGroup的详细信息
-> SoundComponent:SoundGroup的信息可以在Inspector面板配置,在Start中会调用AddSoundGroup进行声音组添加。
ps:需要掌握AudioMixer在框架里面使用的技巧。
UI 用户界面
-> 当UIComponent的InstanceRoot没有给定的时候,会自动创建,添加UI组的时候需要把UI组辅助器上Canvas的渲染模式设置为Overlay或者给它绑定WorldCamera。
->ProcedureMenu(OnEnter)/GameEntry.UI.OpenUIForm(UIFormId.MenuForm, this) -> UIExtension(OpenUIForm)
从数据表中得到对应根据UIFromId枚举转换的索引得到的数据行信息,然后得到资产名称,用AssetUtility组装成完整预制路径,
如果表单数据行(DRUIFrom)不允许多个实例,UI组件正在加载此资产或者已经存在,则不会被打开;否则打开此UI界面,
通过对象池获取一个UI表单实例对象,如果为空,则使用ResourceManager.LoadAsset;否则使用InternalOpenUIForm,
LoadAsset会通过ResourceLoader创建一个LoadAssetTask放入任务池去处理,如果有代理的则交给代理处理,最后在XXXSuccessEventArgs中被携带
InternalOpenUIForm会通过辅助器创建一个实例对象,以辅助器作为父物体绑定,并返回一个UIFrom,再对UIFrom进行逻辑处理。
WebRequest 网络请求:主要是Unity WebRequest,通过网络请求得到数据字节流,不会保存到本地
-> GetWebRequestInfo和GetDownloadInfo一样,返回的都是TaskInfo
-> WebRequestManager:Add/RemoveWebRequest
-> 网络响应的数据流在WebRequestSuccessEventArgs中有字节数组存储(m_WebResponseBytes),但是不会写入到本地磁盘
-> ProcedureCheckVersion:OnWebRequestSuccess(byte[] versionInfoBytes = ne.GetWebResponseBytes();
-> 有两种网络请求代理辅助器:WWW、WebRequest,Unity2018.3之前的版本可以使用WWW
-> 增加一个网络请求 -> 订阅/取消订阅网络请求事件
ps:资源更新模式时,本地文件(BuildInfo.txt)中指向远端更新文件的地址,是通过网络请求方式调用的任务池。