Game Framework 汇总

Game Framework 是一个基于 Unity 引擎的游戏框架,主要对游戏开发过程中常用模块进行了封装,很大程度地规范开发过程、加快开发速度并保证产品质量。

  • 基础和工具 (Base)- 关于日志、引用池、工具集的文档。
  • 全局配置 (Config)- 存储一些全局的只读的游戏配置,如玩家初始速度、游戏初始音量等。
  • 数据结点 (Data Node)- 将任意类型的数据以树状结构的形式进行保存,用于管理游戏运行时的各种数据
  • 数据表 (Data Table)- 可以将游戏数据以表格(如 Microsoft Excel)的形式进行配置后,使用此模块使用这些数据表。数据表的格式是可以自定义的。
  • 调试器 (Debugger)- 当游戏在 Unity 编辑器中运行或者以 Development 方式发布运行时,将出现调试器窗口,便于查看运行时日志、调试信息等。用户还可以方便地将自己的功能注册到调试器窗口上并使用。
  • 下载 (Download)- 提供下载文件的功能,支持断点续传,并可指定允许几个下载器进行同时下载。更新资源时会主动调用此模块。
  • 实体 (Entity)- 我们将游戏场景中,动态创建的一切物体定义为实体。此模块提供管理实体和实体组的功能,如显示隐藏实体、挂接实体(如挂接武器、坐骑,或者抓起另一个实体)等。实体使用结束后可以不立刻销毁,从而等待下一次重新使用。
  • 事件 (Event)- 游戏逻辑监听、抛出事件的机制。Game Framework 中的很多模块在完成操作后都会抛出内置事件,监听这些事件将大大解除游戏逻辑之间的耦合。用户也可以定义自己的游戏逻辑事件。
  • 文件系统 (File System)- 虚拟文件系统使用类似磁盘的概念对零散文件进行集中管理,优化资源加载时产生的内存分配,甚至可以对资源进行局部片段加载,这些都将极大提升资源加载时的性能。
  • 有限状态机 (FSM)- 提供创建、使用和销毁有限状态机的功能,一些适用于有限状态机机制的游戏逻辑,使用此模块将是一个不错的选择。
  • 本地化 (Localization)- 提供本地化功能,也就是我们平时所说的多语言。Game Framework 在本地化方面,不但支持文本的本地化,还支持任意资源的本地化,比如游戏中释放烟花特效也可以做出几个多国语言的版本,使得中文版里是“新年好”字样的特效,而英文版里是“Happy New Year”字样的特效。
  • 网络 (Network)- 提供使用 Socket 长连接的功能,当前我们支持 TCP 协议,同时兼容 IPv4 和 IPv6 两个版本。用户可以同时建立多个连接与多个服务器同时进行通信,比如除了连接常规的游戏服务器,还可以连接语音聊天服务器。如果想接入 ProtoBuf 之类的协议库,只要派生自 Packet 类并实现自己的消息包类即可使用。
  • 对象池 (Object Pool)- 提供对象缓存池的功能,避免频繁地创建和销毁各种游戏对象,提高游戏性能。除了 Game Framework 自身使用了对象池,用户还可以很方便地创建和管理自己的对象池。
  • 流程 (Procedure)- 是贯穿游戏运行时整个生命周期的有限状态机。通过流程,将不同的游戏状态进行解耦将是一个非常好的习惯。对于网络游戏,你可能需要如检查资源流程、更新资源流程、检查服务器列表流程、选择服务器流程、登录服务器流程、创建角色流程等流程,而对于单机游戏,你可能需要在游戏选择菜单流程和游戏实际玩法流程之间做切换。如果想增加流程,只要派生自 ProcedureBase 类并实现自己的流程类即可使用。
  • 资源 (Resource)- 为了保证玩家的体验,我们不推荐再使用同步的方式加载资源,由于 Game Framework 自身使用了一套完整的异步加载资源体系,因此只提供了异步加载资源的接口。不论简单的数据表、本地化字典,还是复杂的实体、场景、界面,我们都将使用异步加载。同时,Game Framework 提供了默认的内存管理策略(当然,你也可以定义自己的内存管理策略)。多数情况下,在使用 GameObject 的过程中,你甚至可以不需要自行进行 Instantiate 或者是 Destroy 操作。
  • 场景 (Scene)- 提供场景管理的功能,可以同时加载多个场景,也可以随时卸载任何一个场景,从而很容易地实现场景的分部加载。
  • 游戏配置 (Setting)- 以键值对的形式存储玩家数据,对UnityEngine.PlayerPrefs 进行封装,也可以将这些数据直接存储在磁盘上。
  • 声音 (Sound)- 提供管理声音和声音组的功能,用户可以自定义一个声音的音量、是 2D 声音还是 3D 声音,甚至是直接绑定到某个实体上跟随实体移动。
  • 界面 (UI)- 提供管理界面和界面组的功能,如显示隐藏界面、激活界面、改变界面层级等。不论是 Unity 内置的 uGUI 还是其它类型的 UI 插件(如 NGUI),只要派生自 UIFormLogic 类并实现自己的界面类即可使用。界面使用结束后可以不立刻销毁,从而等待下一次重新使用。
  • Web 请求 (Web Request)- 提供使用短连接的功能,可以用 Get 或者 Post 方法向服务器发送请求并获取响应数据,可指定允许几个 Web 请求器进行同时请求。

完整的 Game Framework 包含三部分:

  • GameFramework– 封装基础游戏逻辑,如数据管理、资源管理、文件系统、对象池、有限状态机、本地化、事件、实体、网络、界面、声音等,此部分逻辑实现不依赖于 Unity?引擎,以程序集的形式提供。
  • UnityGameFramework.Runtime– 依赖 UnityEngine.dll 进行对 GameFramework.dll 的补充实现。为了方便兼容 Unity 的各个版本,此部分已经以代码的形式包含在 Unity 插件中。
  • UnityGameFramework.Editor- 依赖 UnityEditor.dll 进行对工具、Inspector 的实现。为了方便兼容 Unity 的各个版本,此部分已经以代码的形式包含在 Unity 插件中。

文件系统 (File System)

*文件系统 (File System)-虚拟文件系统使用类似磁盘的概念对零散文件进行集中管理,优化资源加载时产生的内存分配,甚至可以对资源进行局部片段加载,这些都将极大提升资源加载时的性能。

创建文件系统

1
2
3
4
5
6
7
8
9
10
// 要创建的文件系统的完整路径
string fullPath = Path.Combine(Application.persistentDataPath, "FileSystem.dat");?
// 要创建的文件系统最大能容纳文件数量
int maxFileCount = 16;?
// 要创建的文件系统最大能容纳的块数据数量
int maxBlockCount = 256;?
// 创建文件系统(使用读写模式进行访问)
IFileSystem fileSystem = fileSystemComponent.CreateFileSystem(fullPath, FileSystemAccess.ReadWrite, maxFileCount, maxBlockCount);?
// 创建文件系统(使用只写模式进行访问)
IFileSystem fileSystem = fileSystemComponent.CreateFileSystem(fullPath, FileSystemAccess.Write, maxFileCount, maxBlockCount);

文件系统块数据(Block)是 Game Framework 文件系统中引入的一个数据结构,用于索引文件内容数据区在文件系统中的偏移、长度等信息。
即使文件从文件系统中被删除,文件系统块数据依然会保留,并将原先的文件内容数据区标记为文件系统碎片。
文件系统当前已使用的块数据数量 = 文件系统当前文件数量 + 文件系统当前碎片数量。
显然,一个文件系统最大能容纳的块数据数量不应少于最大能容纳的文件数量。当文件系统增加新文件时,会优先分配合适的文件系统碎片的空间用于存储新文件。

读取文件时考虑使用读取文件片段方法
对于数据表、本地化字典表这类文本或二进制数据,在实际应用的多数情形下,将全部内容加载到内存中是非常浪费的,对于这类数据不妨在使用某条数据时再去读取这条数据,即所谓的懒加载或者延迟加载。
通过合理构造数据结构并利用文件系统能够读取文件片段的特性,即可做到这一点。
例如,先读取字典表的全部 key 值,并记录每个 value 值对应的偏移。在需要获取某个 key 的 value 值时,根据已经记录的偏移值利用读取文件片段的方法实时加载 value 值并缓存。


数据结点 (Data Node)

数据结点 (Data Node)-将任意类型的数据以树状结构的形式进行保存,用于管理游戏运行时的各种数据

通过绝对路径获取或设置数据

1
2
3
4
5
6
7
VarString varPlayerName = "Ellan";?
// 使用绝对路径设置数据结点的数据
dataNodeComponent.SetData("Player.Name", varPlayerName);?
// 可以使用绝对路径获取数据结点的数据变量
Variable playerNameVariable = dataNodeComponent.GetData("Player.Name");?
// 也可以使用绝对路径获取数据结点的数据值
string playerNameValue = dataNodeComponent.GetData<VarString>("Player.Name");

自定义变量类

使用者可以自定义所需的变量类型。

1
2
3
4
5
6
7
8
9
/// <summary>
/// 构建信息类。
/// </summary>
[Serializable]
public class BuildInfo
{
public string GameVersion = string.Empty;
public int InternalVersion = 0;
}

若想把此类型的值用于数据结点中,需要定义相应的变量类 VarBuildInfo 后,即可使用了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/// <summary>
/// 构建信息变量类。
/// </summary>
public class VarBuildInfo : Variable<BuildInfo>
{
/// <summary>
/// 初始化构建信息变量类的新实例。
/// </summary>
public VarBuildInfo()
{
}

/// <summary>
/// 初始化构建信息变量类的新实例。
/// </summary>
/// <param name="value">值。</param>
public VarBuildInfo(BuildInfo value)
: base(value)
{
}

/// <summary>
/// 从构建信息类到构建信息变量类的隐式转换。
/// </summary>
/// <param name="value">值。</param>
public static implicit operator VarBuildInfo(BuildInfo value)
{
return new VarBuildInfo(value);
}

/// <summary>
/// 从构建信息变量类到构建信息类的隐式转换。
/// </summary>
/// <param name="value">值。</param>
public static implicit operator BuildInfo(VarBuildInfo value)
{
return value.Value;
}
}

拓展知识

c#自定义类型的转换方式operator,implicit(隐式)和explicit (显示)声明
operator 关键字来重载内置运算符,或提供类或结构声明中的用户定义转换。它可以定义不同类型之间采用何种转化方式和转化的结果。


虚拟文件系统(VFS)

烟雨大佬教程

https://www.lfzxb.top/gameframework-vfs/

简介

虚拟文件系统(VFS)使用类似磁盘的概念对零散文件进行集中管理,优化资源加载时产生的内存分配,甚至可以对资源进行局部片段加载,这些都将极大提升资源加载时的性能。

原理

通过将许多小文件集合成一个大文件的方式来集中管理零散资源,减少物理磁盘寻址次数从而得以提高性能,并且这个过程是我们自定义的,可以进行加密操作,提高安全性。

网络 (Network)

网络 (Network)- 提供使用 Socket 长连接的功能,当前我们支持 TCP 协议,同时兼容 IPv4 和 IPv6 两个版本。用户可以同时建立多个连接与多个服务器同时进行通信,比如除了连接常规的游戏服务器,还可以连接语音聊天服务器。如果想接入 ProtoBuf 之类的协议库,只要派生自 Packet 类并实现自己的消息包类即可使用。

大致流程

a.创建频道(NetworkChannel)
b.绑定频道辅助器(NetworkChannelHelper)
c.注册频道的消息监听类(PacketHandler),即服务端发送某类消息过来时,客户端有对应的类进行接收处理
d.频道连接服务端
e.使用频道辅助器(NetworkChannelHelper)序列化消息对象,通过频道发送消息
f.服务端接收消息并回应客户端f.消息监听类(PacketHandler)处理服务端回应的消息

创建频道


使用Network组件的CreateNetworkChannel函数即可创建频道,第一个参数是频道名称,第二个参数就是频道辅助器。

频道辅助器(NetworkChannelHelper)

频道辅助器是可以自定义的,只要实现了INetworkChannelHelper接口即可。
NetworkChannelHelper有三个关键的函数:
Serialize函数:用于序列化消息。
DeserializePacketHeader函数:用于反序列化消息头。
DeserializePacket函数:用于反序列化消息内容(不含消息头)。

消息包(Packet)

Packet类是框架的消息包抽象类,我们的消息包类都要继承它。


Web 请求 (Web Request)

Web 请求 (Web Request)- 提供使用短连接的功能,可以用 Get 或者 Post 方法向服务器发送请求并获取响应数据,可指定允许几个 Web 请求器进行同时请求。

发送Web请求


实体(Entity)

实体 (Entity)- 我们将游戏场景中,动态创建的一切物体定义为实体。此模块提供管理实体和实体组的功能,如显示隐藏实体、挂接实体(如挂接武器、坐骑,或者抓起另一个实体)等。实体使用结束后可以不立刻销毁,从而等待下一次重新使用。

实体的创建


设置Entity Groups

Demo实体的创建

UnityGameFramework.Runtime.GameEntry

调用顺序


ShowEntityInfo这个参数最终会传入实体对象的生命周期函数(OnInit)

IEntityManager

EntityComponent有一个类型为IEntityManager的属性,m_entityManager。IEntityManager是一个接口,在EntityComponent的Awake里进行了初始化:

1
m_EntityManager = GameFrameworkEntry.GetModule<IEntityManager>();

IEntityManager接口的实现在GF内的EntityManager

EntityManager的ShowEnity函数

于是,我们又回到ShowEntity函数,EntityManager的ShowEntity函数主要做了下面几件事情:
a.根据配置数据,创建实体(从预制体创建)
b.调用实体对象的OnInit、OnShow生命周期函数,这里会把第2步提到的最后一个参数(new ShowEntityInfo(entityLogicType, userData))传递回来
c.剩余一系列操作……(TODO)

DefaultEntityHepler

在EntityComponent的Awake函数初始化了m_EntityManager

在Start函数里,还绑定了一个EntityHelper对象,这个EntityHelper对象就是实体管理器具体创建实体时要利用的工具类。

EntityManage绑定的EntityHelper对象是DefaultEntityHelper。
DefaultEntityHelper是自定义的一个类,继承了EntityHelperBase。
EntityManager创建实体的时候会调用DefaultEntityHelper的CreateEntity函数即可。

DefaultEntityHelper的CreateEntity函数


entityInstance即为实体对象

最后一个关键地方——Entity

EnityManager的ShowEntity函数,会执行下面的代码:

首先是调用了m_EntityHelper(在这里就是DefaultEntityHelper类)的CreateEntity函数,这个函数返回的并不是一个GameObject,而是一个IEntity类型,具体到DefaultEntityHelper的CreateEntity函数,就是返回Entity对象。
在EntityManage的ShowEntity执行的过程中,会调用Entity的生命周期函数。

Entity的OnInit函数做了什么事情:(省略版)

Entity的OnInit函数基本上就做一件事情——把逻辑处理类绑定到实体对象身上。
至于逻辑处理类的类型,是在一开始就通过参数传递的(EntityExtension的ShowMyAircraft函数):


数据表 (Data Table)

数据表 (Data Table)- 可以将游戏数据以表格(如 Microsoft Excel)的形式进行配置后,使用此模块使用这些数据表。数据表的格式是可以自定义的。

Excel表模板


另存为txt文件(也可以转化为byte文件读取)(设置编码格式为UTF-8)

加载配置表(异步加载,返回事件)

dataTableName 配置表模板类名
dataTableAssetName 配置表路径
LoadDataTable 调用的是自定义静态类DataTableExtension的静态拓展函数

DataRowClassPrefixName 命名空间

配置表类模板


两种不同的解析方式

*TODO E大StarForce有自动生成配置表模板工具

读取配置表


界面 (UI)

界面 (UI)- 提供管理界面和界面组的功能,如显示隐藏界面、激活界面、改变界面层级等。不论是 Unity 内置的 uGUI 还是其它类型的 UI 插件(如 NGUI),只要派生自 UIFormLogic 类并实现自己的界面类即可使用。界面使用结束后可以不立刻销毁,从而等待下一次重新使用。

加载一个UI



添加对应

添加对应界面组名称及深度


流程(Procedure)

流程 (Procedure)– 是贯穿游戏运行时整个生命周期的有限状态机。通过流程,将不同的游戏状态进行解耦将是一个非常好的习惯。对于网络游戏,你可能需要如检查资源流程、更新资源流程、检查服务器列表流程、选择服务器流程、登录服务器流程、创建角色流程等流程,而对于单机游戏,你可能需要在游戏选择菜单流程和游戏实际玩法流程之间做切换。如果想增加流程,只要派生自 ProcedureBase 类并实现自己的流程类即可使用。


Entrance Procedure
入口流程

ProcedureBase

生命周期函数:

备注

查漏补缺