游戏开发框架自制Unity通用游戏框架UnityXFramework,详细教程(Unity3D技能树 | tolua | 框架 | 热更新)
Posted 林新发
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了游戏开发框架自制Unity通用游戏框架UnityXFramework,详细教程(Unity3D技能树 | tolua | 框架 | 热更新)相关的知识,希望对你有一定的参考价值。
文章目录
一、前言
嗨,大家好,我是新发。
很多同学评论和私信我,催我出一个Unity3D通用游戏框架
,其实一周前我已经写得差不多了,然后我又去搞虚幻引擎了,今天收尾了一下,给大家一个交代,为了表示对广大粉丝的谢意,我做了一份Unity
技能树,感谢大家~
二、Unity3D技能树
如下,长图,可放大查看,如果你是Unity3D
初学者,不知道从哪着手,希望我下面的技能树可以给你一些帮助,共勉!
三、UnityXFramework框架,开源
回归正题,Unity3D通用游戏框架
来了,框架1.0
版本我已经上传到CODE CHINA
,框架名字我起为UnityXFramework
。
项目地址:https://codechina.csdn.net/linxinfa/UnityXFramework
注:我使用的Unity
版本为2021.1.7f1c1
,如果你使用低版本的Unity
打开可能会有兼容问题。
目前版本包含的内容:
1、集成了tolua Framework
框架,集成了sproto
协议通信(包括C#
和lua
);
2、封装了各类管理器:配置加载、资源管理器、网络管理器、版本管理器、声音管理器、日志管理器、界面管理器、特效管理器、图集管理器、事件管理器、常用工具类等;
3、拓展了UGUI
,包括精灵图轴对称、新手引导镂空遮罩、国际化多语言Text
、循环复用列表等;
4、多语言支持,中文简体、中文繁体、英语,可自行添加更多语言;
5、热更新,支持资源热更新和Lua
代码热更新,包括热更包版本检测、下载、校验、解压;
6、打包工具封装,包括打整包APP
、AssetBundle
和增量包ZIP
;
7、…
画个图,方便大家理解,
下面,我就介绍一下UnityXFramework
框架的使用方法,时间有限,可能有一些地方封装得不是很好,希望各位大佬提出改进意见,不过为了考虑通用性,也不宜加太多东西~
四、入口场景:Main.unity
在介绍细节之前,我们先打开入口场景Main.unity
,看能否正常运行,
运行效果如下,
如果你运行也能正常进入,恭喜你,你已经成功运行框架了~
五、目录说明
现在,我们来看看项目目录结构,如下,
目录说明:
目录 | 说明 |
---|---|
Editor | 存放一些编辑器脚本,比如打包工具 |
GameRes | 存放熟肉资源,比如界面预设、模型预设、特效预设等,该目录下的文件会打包成AssetBundle 并放在StreamingAssets 目录中 |
LuaFramework | tolua 框架 |
Plugins | 存放SDK 和库文件 |
RawAssets | 存放生肉资源,比如预设依赖的图片、字体等,模型依赖的网格、材质、贴图等 |
Resources | 存放APP 启动时必要的一些基础资源,非必要的建议存放到GameRes 中,方便资源热更 |
Scenes | 存放场景文件 |
Scripts | 存放C# 脚本,子目录见下面的表格说明 |
StreamingAssets | 存放AssetBundle 文件和一些Raw 资源(打包时Unity 不会帮我们做加密) |
其中,展开Scripts
文件夹,可以看到子目录结构如下,
Scripts
目录说明:
目录 | 说明 |
---|---|
3rd | 第上方库源码,比如LitJson |
Framework | 存放框架代码 |
Animation | 动画相关的代码,动画帧事件 |
Audio | 声音相关的代码,声音管理器 |
Common | 一些共用模块的代码,比如全局变量、Util 工具类脚本等 |
Debug | 调试相关的代码,日志输出、写日志文件和运行时预览 |
Encrypt | 加密相关的代码,对资源和Lua 代码进行解密和解密 |
Event | 事件相关的代码,事件触发器,事件定义 |
I18N | 国际化语言相关的代码,支持多语言切换 |
Network | 网络相关的代码,网络连接、消息通信 |
Panel | 界面管理器相关的代码,界面管理器 |
Particle | 粒子管理器相关代码,粒子管理器 |
Resource | 资源加载相关的代码,资源管理器 |
Sprite | 图集精灵管理相关的代码,图集精灵管理器 |
sproto | sproto 的C# 封装 |
SprotoDef | sproto 的C# 协议定义 |
Timer | 定时器相关的代码 |
UGUIExpand | UGUI 拓展,比如精灵图轴对称、新手引导镂空遮罩、国际化多语言Text 、循环复用列表等 |
Version | 版本号相关的代码,版本号管理器 |
Logic | 游戏逻辑相关的代码 |
View | UI 界面交互相关的代码 |
StartUp.cs | 启动脚本 |
后续游戏业务相关的C#
代码,添加到Logic
和View
目录即可,不过,建议游戏业务使用Lua
来开发,方便热更迭代和Bug
修复。
Lua
游戏业务代码放在Luaframework
目录下的Logic
和View
目录中。
其他目录在下文讲解具体模块时我再展开说~
六、框架启动流程
框架启动流程如下,入口场景是Main.unity
,入口脚本是StartUp.cs
,在Awake
函数中执行初始化,调用热更新逻辑,回调后再初始化一些模块,然后启动LuaFramework
框架,会载入Lua
脚本,启动完毕后,执行Main.lua
脚本,接下来就是Lua
的业务代码了,比如登录界面和登录逻辑都使用Lua
来实现,在Main.lua
中打开登录界面,我写的演示代码就是这个流程。
七、框架使用说明
1、配置表
配置表可以是xml
、lua
或json
,你可以手动创建这些配置表,也可以通过Excel
来生成。
1.1、Excel转xml、lua、json
我也写了一个Excel
转xml
、lua
、json
的python
工具,放在UnityXFramework\\Tools\\ExcelCfg
目录中,
注:你可以继续在
Tools
目录中添加一些python
工具。
你需要安装python3
和openpyxl
库,我之前写过一篇教程,可以参见我这篇文章:《教你使用python读写Excel表格(增删改查操作),使用openpyxl库》
Excel
文件放在Excels
文件夹中,
在gen.py
中指定你要转换的Excel
,如下,
执行python
脚本即可,你也可以直接双击gen.bat
,
生成的配置表在output
文件夹中,你可以自行修改生成的路径,
1.2、C#加载xml配置
C#
加载的配置表放在Assets/GameRes/Config
目录中,以声音配置audioConfig.bytes
为例,
因为打包AssetBundle
无法识别xml
,所以使用bytes
作为文件后缀名,内容其实是xml
格式的,如下
注:其中的
id 6
是资源id
,所有动态加载的资源都会有一个唯一的资源id
,资源在resources.bytes
中配置。
要加载audioConfig.bytes
配置表,我们需要定义一个字段名与xml
字段名一致的item
类,继承ConfigItem
类,如下
public class AudioCfgItem : ConfigItem
public int id;
public string name;
public float volume;
public int channel;
public override string GetKey()
return name;
然后就可以通过ConfigFile
对象来直接加载配置表了,如下
// 声明一个ConfigFile变量
private ConfigFile<AudioCfgItem> m_audioCfg;
// ...
// 加载配置表
m_audioCfg = new ConfigFile<AudioCfgItem>("audioConfig");
接着我们就可以封装一些方法来查询配置了,如下
public AudioCfgItem GetAudioCfg(string audioName)
if (null == m_audioCfg) return null;
var cfgItem = m_audioCfg.GetItem(audioName);
if (null == cfgItem)
GameLogger.LogError("null == audioCfg, name: " + audioName);
return cfgItem;
1.3、lua加载lua配置
lua
加载配置更简单,首先把lua
配置表放在Assets/LuaFramework/Lua/Config
目录中,比如TestLuaCfg.lua
,
配置内容如下,
要加载配置表,只需要require
即可,例:
-- 测试lua配置加载
local TestLuaCfg = require "Config/TestLuaCfg"
-- 打印配置表
LuaUtil.PrintTable(TestLuaCfg)
2、资源加载
2.1、资源存放目录
资源分为生肉资源和熟肉资源,生肉资源就是未加工包装的资源,熟肉资源就是加工包装后的资源。
比如,我们制作一个界面预设,这个界面预设依赖的精灵图就是生肉资源,界面预设本身就是熟肉资源。
生肉资源放在RawAssets
目录中,生肉资源放在GameRes
目录中。
2.2、资源路径配置
动态加载的熟肉资源,需要配置在resources.bytes
中,
如下
上面的配置你可以手动配置,也可以点击菜单Tools/Aux/资源添加编辑器
(或者按快捷键Alt + G
),
此时会打开一个窗口,
把资源拖到资源对象
栏中,然后输入资源描述
,最后点击Add
按钮即可,它会自动在resources.bytes
中添加一行配置;如果是已添加过渡,则会显示Update
按钮,如下,
2.3、资源加载与实例化
加载资源的时候,可以通过资源id
加载,也可以通过uri
加载(即配置中的editorPath
字段的值),例:
// 通过uri加载并实例化
var panelObj = ResourceManager.instance.Instantiate<GameObject>("BaseRes/HotUpdatePanel.prefab");
// 通过id加载并实例化
var cubeObj = ResourceManager.instance.Instantiate<GameObject>(2);
框架底层我封装的资源加载流程是这样的,
注:非
Editor
环境,资源会以AssetBundle
的方式加载,打AssetBundle
的逻辑我已经在打包工具中实现了,具体见下文中打包工具的介绍。
3、网络连接与通信
网络连接使用的是System.Net.Sockets
命名空间下的Socket
封装的。
3.1、网络连接
C#
层示例:
using UnityEngine;
public class Test : MonoBehaviour, INetStateListener
void Awake()
// 设置网络状态监听
ClientNet.instance.AddNetStateListener(this);
void Start()
// 测试网络连接
ClientNet.instance.Connect("127.0.0.1", 8888);
public void OnNetStateChanged(NetState state, object param = null)
switch (state)
case NetState.ConnectSuccess:
GameLogger.LogGreen("连接服务端成功");
break;
case NetState.ConnectFail:
GameLogger.LogYellow("连接服务端失败");
break;
default:
GameLogger.Log("网络状态更新: " + state);
break;
lua
层示例:
Network.Connect('127.0.0.1', 8888, function (ok)
-- 连接回调
end)
3.2、网络通信
3.2.1、协议文件
网络通信,框架中封装的通信协议是sproto
,你也可以改成protobuf
协议之类的。我这里以sproto
协议的通信为例。
我们需要定义协议,包括协议名,协议字段和数据类型等,协议又分为c2s
和s2c
,其中c2s
表示client to server
,即客户端发送给服务端的消息,同理,s2c
就是server to client
,即服务端下发给客户端的消息。
比如有一个sayhello
的c2s
的协议,sayhello
是协议名,what
是字段名,它的数据类型是string
,response
表示服务端返回,返回了error_code
和msg
两个字段,如下,
sayhello 1
request
what 0 : string
response
error_code 0 : integer
msg 1 : string
因为需求上需要兼顾C#
和lua
,所以分别定义C#
协议文件和lua
协议文件,画个图方便理解,如下,
3.2.2、客户端发送协议给服务端:C#层
C#
层协议发送通过ClientNet
的Send
方法来发送,
注意:发送协议时需要先正常连接服务端之后再发
示例:
// 构造SpObject对象
SpObject spObj = new SpObject(SpObject.ArgType.Table, "what", "hi, i am c#");
// 通过ClientNet发送协议消息
ClientNet.instance.Send("sayhello", spObj, (protoname, spobject) =>
// 服务端response返回
var error_code = SpObject.AsInt(spobject, "error_code", -1);
var msg = SpObject.AsString(spobject, "msg", "");
GameLogger.LogFormat("0: 1", error_code, msg);
);
3.2.3、客户端发送协议给服务端:Lua层
lua
层协议发送通过Network.SendData
方法,
示例:
-- 构造数据
local data = what = "hi, i am unity from lua"
-- 发送协议数据
Network.SendData("sayhello", data, function(data)
-- 回到
log("on response: " .. data.error_code .. " " .. data.msg)
end
)
3.2.3、服务端下发协议给客户端:C#层
服务端下发协议给客户端,如果要在C#
层处理,需要提前注册协议的响应函数,示例:
ProtocolProcessor.AddCallback(SpRpcOp.Request, "协议名", 响应函数);
建议统一放在NetworkMsgEventRegister
脚本中进行注册,例:
public class NetworkMsgEventRegister
public void RegistNetworkMsgEvent()
ProtocolProcessor.AddCallback(SpRpcOp.Request, "heartbeat", OnHeartBeat);
private void OnHeartBeat(string protoName, SpObject data)
// TODO
3.2.4、服务端下发协议给客户端:Lua层
服务端下发协议给客户端,如果要在Lua
层处理,也需要提前注册协议的响应函数,建议统一在s2cProcessTab.lua
脚本中,注册,例:
s2cProcessTab =
-- 数字1表示C#层和lua层都进行响应
-- 如果是2,则表示只在lua中进行响应,即使你在C#层注册了响应也不会在C#层触发
heartbeat = 1, HeartMgr.OnHeartBeat ,
其中响应函数如下
-- 响应函数
function HeartMgr.OnHeartBeat(protoName, data)
-- TODO
end
4、版本号管理
4.1、版本号文件:version.bytes
游戏的版本号包括app_version
和res_version
,保存在Assets/Resources
目录的version.bytes
文件中,
如下:
"app_version":"1.0.0.0","res_version":"1.0.0.0"
app_version
是打整包时的整包版本号;res_version
是资源版本号,打整包时默认与app_version
相等,打增量包时,需要对res_version
加一。因为version.bytes
是放在Resources
目录中的,出包后它不能动态修改,所以我们需要另外缓存一个当前最新的版本号,增量更新时,缓存一个version.bytes
到Application.persistentDataPath + "/update/"
目录中,如下:
4.2、版本号管理器:VersionMgr.cs
版本号管理器逻辑封装在VersionMgr.cs
脚本中,
// 获取app版本号
var appVer = VersionMgr.instance.appVersion;
// 获取res版本号
var resVer = VersionMgr.instance.resVersion;
5、日志管理
5.1、日志打印
打印日志封装了一个GameLogger
类,可以根据需要答应带颜色的日志,例:
GameLogger.Log("普通日志");
GameLogger.LogGreen("绿色文本日志");
GameLogger.LogYellow("黄色文本日志");
GameLogger.LogCyan("蓝绿色文本日志");
GameLogger.LogRed("红色文本日志");
效果:
5.2、输出日志文件
另外,会自动保存日志文件,Editor
环境保存在Assets
统计目录的gamelog
文件夹中,
非Editor
环境,则保存到Application.persistentDataPath + "/gamelog/"
文件夹中。
5.3、运行时日志预览
封装了一个LogCat.cs
脚本,支持运行时对日志进行预览,在PC
环境下按F4
,在移动端使用四根手指头同时点击屏幕即可打开日志预览窗口,如下:
5.4、更多日志玩法
我之前写过一篇文章,推荐大家看下:《【游戏开发进阶】新发带你玩转Unity日志打印技巧(彩色日志 | 日志存储与上传 | 日志开关 | 日志双击溯源)》
6、声音管理
6.1、声音配置
声音作为资源,也需要进行资源配置,见上面第2.2
节的资源路径配置的操作,同时会自动添加一个声音名字到资源id
的映射到audioConfig.bytes
中,
如下,
6.2、背景音乐播放
封装了一个声音管理器AudioMgr.cs
,播放背景音乐的接口如下
// AudioMgr.cs
/// <summary>
/// 播放音乐,比如背景音乐
/// </summary>
/// <param name="audioName">声音名字,需要带后缀</param>
/// <param name="loop">是否循环</param>
/// <param name="fadeIn">是否键入</param>
/// <param name="pauseOther">是否停止其他背景音乐</param>
public void PlayMusic(string audioName, bool loop, bool fadeIn, bool pauseOther)
例:
AudioMgr.instance.PlayMusic("bg.wav", true, true, true);
6.3、音效播放
播放音效的接口如下
// AudioMgr.cs
/// <summary>
/// 播放音效
/// </summary>
/// <param name="audioName">声音名字,需要带后缀</param>
public void PlaySound(string audioName)
/// <summary>
/// 播放音效
/// </summary>
/// <param name="audioName">声音名字,需要带后缀</param>
/// <param name="loop">是否循环</param>
/// <param name="fadeIn">是否渐入</param>
public void PlaySoundEx(string audioName, bool loop, bool fadeIn)
例:
AudioMgr.instance.PlaySound("coin.ogg");
AudioMgr.instance.PlaySoundEx("award.ogg", false, true);
6.4、音量调节
我提供了音乐音量和音效音量两个接口,接口
// AudioMgr.cs
/// <summary>
/// 调节音效音量
/// </summary>
/// <param name="factor">0到1</param>
public void UpdateSoundVolume(float factor)
/// <summary>
/// 调节音乐音量
/// </summary>
/// <param name="factor">0到1</param>
public void UpdateMusicVolume(float factor)
示例
AudioMgr.instance.UpdateSoundVolume(0.8f);
AudioMgr.instance.UpdateMusicVolume(0.5f);
7、事件管理
根据观察者模式封装了一个事件管理器:EventDispatcher.cs
,可以订阅事件,然后通过抛事件的方式来触发一些响应逻辑。
7.1、定义事件名
建议在EventNameDef.cs
中定义事件名,例:
7.2、事件订阅
接口
// EventDispatcher.cs
/// <summary>
/// 注册事件
/// </summary>
/// <param name="evt">事件名</param>
/// <param name="handler">响应函数</param>
public void Regist(string evt, MyEventHandler handler)
/// <summary>
/// 注销事件
/// </summary>
/// <param name="evt">事件名</param>
/// <param name="handler">响应函数</param>
public void UnRegist(string evt, MyEventHandler handler)
示例:
以上是关于游戏开发框架自制Unity通用游戏框架UnityXFramework,详细教程(Unity3D技能树 | tolua | 框架 | 热更新)的主要内容,如果未能解决你的问题,请参考以下文章
Unity游戏通用mod框架BepInEx生态的工作原理简介
Unity 框架QFramework v1.0 使用指南 工具篇:05. ResKit 资源管理&开发解决方案 | Unity 游戏框架 | Unity 游戏开发 | Unity 独立游戏
Unity 框架QFramework v1.0 使用指南 架构篇:05. 引入 Utility | Unity 游戏框架 | Unity 游戏开发 | Unity 独立游戏
Unity 框架QFramework v1.0 使用指南 架构篇:20. QFramework.cs 的更多内容 | Unity 游戏框架 | Unity 游戏开发 | Unity 独立游戏
Unity 框架QFramework v1.0 使用指南 架构篇:19. 心中有架构 | Unity 游戏框架 | Unity 游戏开发 | Unity 独立游戏
Unity 框架QFramework v1.0 使用指南 工具篇:09. SingletonKit 单例模板套件 | Unity 游戏框架 | Unity 游戏开发 | Unity 独立游戏