diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c7ff2e --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ + +UnityProject/UserSettings/Layouts/default-2021.dwlt +UnityProject/UserSettings/Search.settings + +#Luban +Tools/Luban/ +Tools/Luban.ClientServer/ +Configs/.cache.meta +GenerateDatas/ + +#FileServer +Tools/FileServer/AssetRoot + +UnityProject/ProjectSettings/CommonBurstAotSettings.json +UnityProject/ProjectSettings/BurstAotSettings_StandaloneWindows.json diff --git a/Books/0-介绍.md b/Books/0-介绍.md new file mode 100644 index 0000000..9ad024b --- /dev/null +++ b/Books/0-介绍.md @@ -0,0 +1,29 @@ +# 介绍- Introduce + +### TEngine + + status + + + license + + + last + + + issue + + + topLanguage + + +TEngine是一套用于Unity框架解决方案,用于帮助研发团队快速进行开发。 + +### 开箱即用 +开箱即用、用法简洁,即用即上手.高可读性和详细的文档说明帮助你更快更好的进行游戏开发。您不需要关心框架的底层,分离独自实现您的GamePlay。 + +### 性能强大 +TEngine 底层使用基于UniTask的异步以及事件分发,可插件定制化,把复杂游戏简单化切以高性能、低耦合度实现。 + +### 高内聚低耦合 +内嵌了The Best 次时代热更新解决方案HybridCLR、百万DAU验证的资源解决方案YooAsset、你的游戏最佳配置解决方案Luban diff --git a/Books/1-快速开始.md b/Books/1-快速开始.md new file mode 100644 index 0000000..7426159 --- /dev/null +++ b/Books/1-快速开始.md @@ -0,0 +1,63 @@ +# 快速开始- Quickly Start +快速上手 +本教程引导从空项目开始体验TEngine。出于简化起见,只演示目标平台为Windows的情况。 + +请在Standalone平台上正确跑通热更新流程后再自行尝试Android、iOS平台的热更新,它们的流程非常相似。 + +### 1.使用Unity2021.3.20f1c1打开项目工程。 + +### 2.默认选择顶部栏目EditorMode编辑器下的模拟模式并点击Launcher开始运行 +![image](src/1-1.png) + +### 3.Editor编辑器下运行成功! +![image](src/1-2.png) + +### 4.打包运行 + * 1.运行菜单 HybridCLR/Install... 安装HybridCLR,每次更新HybridCLR版本需要重新执行一次安装。 + * 2.运行菜单 HybridCLR/Define Symbols/Enable HybridCLR 运行开启HybridCLR热更新。 + * 3.运行菜单 HybridCLR/Generate/All 进行必要的生成操作。这一步不可遗漏!!! + * 4.运行菜单 HybridCLR/Build/BuildAssets And CopyTo AssemblyPath,生成热更新dll并copy到热更程序集中。 + * 5.运行菜单 YooAsset/AssetBundle Builder 构建AB。 + * 6.打开Build Settings对话框,点击Build And Run,打包并且运行热更新示例工程。 + +### 遇到问题请查看HybridlCLR的常见错误(commonerrors) + +### 系统需求 +默认版本:Unity2021.3.20f1c1 + +支持版本: Unity2019.4 & Unity2020.3 & Unity2021.3 & Unity2022.3 + +支持平台: Windows、OSX、Android、iOS、WebGL + +开发环境: .NET4.x + +### 目录结构 +``` +Assets +├── AssetArt // 美术资源目录 +│ └── Atlas // 自动生成图集目录 +├── AssetRaw // 热更资源目录 +│ ├── UIRaw // UI图片目录 +│ │ ├── Atlas // 需要自动生成图集的UI素材目录 +│ │ └── Raw // 不需要自动生成图集的UI素材目录 +├── Editor // 编辑器脚本目录 +├── HybridCLRData // hybridclr相关目录 +├── Scenes // 主场景目录 +├── GameScripts // 程序集目录 +└── TEngine // 框架核心目录 + ├── AssetSetting // YooAsset资源设置 + ├── Editor // TEngine-Editor程序集 + └── Runtime // TEngine-Runtime程序集 +``` + +### 热更新程序集划分 +``` +Assets/GameScripts +├── Main // 主程序程序集(启动器与流程) +└── HotFix // 游戏热更程序集目录 [Folder] + ├── GameBase // 游戏基础框架程序集 [Dll] + ├── GameProto // 游戏配置协议程序集 [Dll] + └── GameLogic // 游戏业务逻辑程序集 [Dll] + ├── GameApp.cs // 热更主入口 + └── GameApp_RegisterSystem.cs // 热更主入口注册系统 +``` \ No newline at end of file diff --git a/Books/2-框架概览.md b/Books/2-框架概览.md new file mode 100644 index 0000000..26b4563 --- /dev/null +++ b/Books/2-框架概览.md @@ -0,0 +1,82 @@ +# TEngine + +## TEngine-Runtime +### AOT内核基于Gameframework,优化、最简化以及商业化适配。 +![image](src/2-1.png) + +## AOT游戏框架模块基类。 +#### 框架思路为面向接口编程,如Resource资源模块,开发白皮书为先定义IResourceManager的接口规范,然后编写ResourceManager继承框架具体实现(ModuleImp)以及实现接口。最后实现调用层Module,调用层可以拓展编辑器供开发者自定义模块参数。 +``` csharp +/// +/// 游戏框架模块抽象类。ModuleImp为具体框架模块实现。 +/// +internal abstract class ModuleImp +{ + /// + /// 获取游戏框架模块优先级。 + /// + /// 优先级较高的模块会优先轮询,并且关闭操作会后进行。 + internal virtual int Priority => 0; + + /// + /// 游戏框架模块轮询。 + /// + /// 逻辑流逝时间,以秒为单位。 + /// 真实流逝时间,以秒为单位。 + internal abstract void Update(float elapseSeconds, float realElapseSeconds); + + /// + /// 关闭并清理游戏框架模块。 + /// + internal abstract void Shutdown(); +} + +//=====================================================================// + +/// +/// 游戏框架模块抽象类。Module 为Mono调用层。 +/// +public abstract class Module : MonoBehaviour +{ + /// + /// 游戏框架模块初始化。 + /// + protected virtual void Awake() + { + ModuleSystem.RegisterModule(this); + } +} +``` + +## 热更域程序集设计与说明 +``` +Assets/GameScripts +├── Main // 主程序程序集(启动器与流程) +└── HotFix // 游戏热更程序集目录 [Folder] + ├── GameBase // 游戏基础框架程序集 [Dll] + ├── GameProto // 游戏配置协议程序集 [Dll] + └── GameLogic // 游戏业务逻辑程序集 [Dll] + ├── GameApp.cs 热更主入口 + └── GameApp_RegisterSystem.cs 热更主入口注册系统 +``` +游戏内主要玩法逻辑包括UI会在GameLogic中编写,GameBase则存放一些通用性的逻辑,GameProto存放与服务区交互的协议以及配置表逻辑。若有项目需求完全可以进行自定义增删HotFix程序集。 + +PS:注意增删程序集后需要同步到HybridClr的Setting面板以及TEngineSetting的面板。TEngineSettings面板有按钮可以从HybridClr中同步AOT与热更程序集。 + +## 常用模块接口 + +[3-1-资源模块](./3-1-资源模块.md) + +[3-2-事件模块](./3-2-事件模块.md) + +[3-3-内存池模块](./3-3-内存池模块.md) + +[3-4-对象池模块](./3-4-对象池模块.md) + +[3-5-UI模块](./3-5-UI模块.md) + +[3-6-配置表模块](./3-6-配置表模块.md) + +[3-7-流程模块](./3-7-流程模块.md) + +[3-8-网络模块](./3-8-网络模块.md) diff --git a/Books/3-1-资源模块.md b/Books/3-1-资源模块.md new file mode 100644 index 0000000..330429d --- /dev/null +++ b/Books/3-1-资源模块.md @@ -0,0 +1,200 @@ +## 3-1.资源模块 - ResourceModule + +#### 资源模块默认使用Addressable可寻址定位。(!注意需要打包的资源不可以重名) + +资源模块运行模式有EditorSimulateMode、OfflinePlayMode以及HostPlayMode +编辑器模式下以顶部导航栏的选项卡为优先选项,打包后以Scene场景中ResourceModule脚本的Enum选项卡为优先选项(打包后不会走EditorSimulateMode) + +Scene窗口Resource对象可以设置一些资源模块的常用设置,比如打包后的资源模式、资源校验等级以及自动卸载资源间隔等。 +![image](src/3-1-1.png) + +Menu窗口TEngineSetting可以设置一些资源模块的热更新设置,比如资源服务器地址以及在线参数等。备注在Luban目录下有一个基于Node的静态文件服务器,可以把在线参数以及AB放入用于做热更新测试。 +![image](src/3-1-2.png) + +## 重要拓展概念 +* AssetReference (资源引用标识) 通用加载资源的时候绑定一个引用标识使你无需关心手动Dispose资源句柄。 + +* AssetGroup(资源组数据)进行资源分组绑定管理内存中的生命周期资源生命周期托管给资源组的根节点进行Dispose。 + +* LruCacheTable (Least Recently Used Cache缓存表) + +* ArcCacheTable (Adaptive Replacement Cache缓存表) + +## 加载资源示范 +注意!资源模块默认使用Addressable可寻址定位。传入资源名字无需后缀即可!! +``` csharp + +//同步加载。 +GameModule.Resource.LoadAsset(location); + +//异步加载。 +GameModule.Resource.LoadAssetAsync(location, OnLoadSuccess); +private void OnLoadSuccess(AssetOperationHandle assetOperationHandle){} + +//使用UniTask异步加载。 +await GameModule.Resource.LoadAssetAsync(location,CancellationToken.None); +``` + +## 常用接口 +remark:资源模块初始化的生命周期已经在流程模块实现了,具体有需求可以自定义开发。 +``` csharp +/// +/// 获取当前资源包版本。 +/// +/// 资源包版本。 +public string GetPackageVersion(); + +/// +/// 异步更新最新包的版本。 +/// +/// 请求URL是否需要带时间戳。 +/// 超时时间。 +/// 请求远端包裹的最新版本操作句柄。 +public UpdatePackageVersionOperation UpdatePackageVersionAsync(bool appendTimeTicks = false, int timeout = 60); + +/// +/// 向网络端请求并更新清单 +/// +/// 更新的包裹版本 +/// 更新成功后自动保存版本号,作为下次初始化的版本。 +/// 超时时间(默认值:60秒) +public UpdatePackageManifestOperation UpdatePackageManifestAsync(string packageVersion, bool autoSaveVersion = true, int timeout = 60); + +/// +/// 创建资源下载器,用于下载当前资源版本所有的资源包文件。 +/// +public ResourceDownloaderOperation CreateResourceDownloader(); + + +/// +/// 清理包裹未使用的缓存文件。 +/// +public ClearUnusedCacheFilesOperation ClearUnusedCacheFilesAsync(); + + +/// +/// 清理沙盒路径。 +/// +public void ClearSandbox(); + +/// +/// 强制执行释放未被使用的资源。 +/// +/// 是否使用垃圾回收。 +public void ForceUnloadUnusedAssets(bool performGCCollect); + +/// +/// 检查资源是否存在。 +/// +/// 要检查资源的名称。 +/// 检查资源是否存在的结果。 +public HasAssetResult HasAsset(string assetName); + +/// 同步加载资源。 +/// +/// 要加载资源的名称。 +/// 要加载资源的类型。 +/// 资源实例。 +T LoadAsset(string assetName) where T : Object; + +/// +/// 同步加载资源。 +/// +/// 要加载资源的名称。 +/// 父节点位置。 +/// 要加载资源的类型。 +/// 资源实例。 +T LoadAsset(string assetName, Transform parent) where T :Object; + +/// +/// 同步加载资源。 +/// +/// 资源操作句柄。 +/// 要加载资源的名称。 +/// 要加载资源的类型。 +/// 资源实例。 +T LoadAsset(string assetName,out AssetOperationHandle handle) where T : Object; + +/// +/// 同步加载资源。 +/// +/// 要加载资源的名称。 +/// 资源操作句柄。 +/// 父节点位置。 +/// 要加载资源的类型。 +/// 资源实例。 +T LoadAsset(string assetName, Transform parent,out AssetOperationHandle handle) where T :Object; + +/// +/// 异步加载资源。 +/// +/// 要加载资源的名称。 +/// 取消操作Token。 +/// 要加载资源的类型。 +/// 异步资源实例。 +UniTask LoadAssetAsync(string assetName,CancellationToken cancellationToken) where T : Object; + +/// +/// 异步加载游戏物体。 +/// +/// 要加载的游戏物体名称。 +/// 取消操作Token。 +/// 异步游戏物体实例。 +UniTask LoadGameObjectAsync(string assetName,CancellationToken cancellationToken); + +/// +/// 同步加载资源并获取句柄。 +/// +/// 要加载资源的名称。 +/// 要加载资源的类型。 +/// 同步加载资源句柄。 +AssetOperationHandle LoadAssetGetOperation(string assetName) where T : Object; + +/// +/// 异步加载资源并获取句柄。 +/// +/// 要加载资源的名称。 +/// 要加载资源的类型。 +/// 异步加载资源句柄。 +AssetOperationHandle LoadAssetAsyncHandle(string assetName) where T : Object; + +/// +/// 同步加载子资源对象 +/// +/// 资源类型 +/// 资源的定位地址 +public SubAssetsOperationHandle LoadSubAssetsSync(string location) where TObject : UnityEngine.Object; + +/// +/// 异步加载子资源对象 +/// +/// 资源类型 +/// 资源的定位地址 +public SubAssetsOperationHandle LoadSubAssetsAsync(string location) where TObject : UnityEngine.Object; + +/// +/// 同步加载子资源对象 +/// +/// 资源信息。 +public SubAssetsOperationHandle LoadSubAssetsSync(AssetInfo assetInfo); + +/// +/// 异步加载场景。 +/// +/// 场景的定位地址 +/// 场景加载模式 +/// 加载完毕时是否主动激活 +/// 优先级 +/// 异步加载场景句柄。 +SceneOperationHandle LoadSceneAsync(string location, LoadSceneMode sceneMode = LoadSceneMode.Single, bool activateOnLoad = true, int priority = 100); + +/// +/// 异步加载场景 +/// +/// 场景的资源信息 +/// 场景加载模式 +/// 加载完毕时是否主动激活 +/// 优先级 +/// 异步加载场景句柄。 +SceneOperationHandle LoadSceneAsync(AssetInfo assetInfo, LoadSceneMode sceneMode = LoadSceneMode.Single, bool activateOnLoad = true, int priority = 100); +``` \ No newline at end of file diff --git a/Books/3-2-事件模块.md b/Books/3-2-事件模块.md new file mode 100644 index 0000000..def3d55 --- /dev/null +++ b/Books/3-2-事件模块.md @@ -0,0 +1,69 @@ +# 3-2.事件模块 - GameEvent +高效且无GC的事件系统GameEvent,可以指定事件ID/事件String监听和分发事件。通过事件来驱动模块,如战斗的角色身上的事件流、UI和网络以及Model的数据流、开发中的绝大部分情况都可以通过事件来进行驱动。(配合UI模块或者拓展的战斗模块实现MVE[Model - View - Event]事件驱动架构) + +事件模块支持string和int作为事件Id,但推荐是使用int因为可以避免事件字典的哈希碰撞。这里实现了StringId.StringToHash的方法来定义事件ID达到事件系统的最佳性能。 + +注:UI模块的事件和UI生命周期存在绑定,销毁UI时可以自动移除UI所监听的事件,开发过程中只需要关心添加事件,避免了关闭UI但没有移除事件监听的问题,角色模块也可以参考实现。(AddUIEvent) +``` csharp +public static readonly int Hellp = StringId.StringToHash("Hellp.Hellp"); + +class A +{ + public A() + { + //添加事件监听string + GameEvent.AddEventListener("TEngine很好用",TodoSomeThings); + //添加事件监听int 事件ID + GameEvent.AddEventListener(Hellp,TodoSomeThings2); + } +} + +class B +{ + private void SaySomeThings() + { + //发送事件流 + GameEvent.Send("TEngine很好用"); + GameEvent.Send(Hellp); + } +} + +【举个例子:游戏中血量扣除的时候,服务器发来了一个减少Hp的消息包, +我们可以在收到这个消息包的时候发送一个事件流,在玩家头顶的HP进度 +条组件/左上角Hp的UI血条组件添加一个监听事件,各个模块负责自己监听后的逻辑】 +Server -> SendMessage(ReduceHP) + +class ClientHandle +{ + private void HandleMessage(MainPack mainpack) + { + ... + HpPack hpPack = mainpack.hpPack; + int playerId = mainpack.playerId; + var player = PlayerMgr.Instance.GetPlayer(playerId); + if(player != null){ + player.Event.Send("Hpchange",hpPack); //局部的事件管理器 + GameEvent.Send("Hpchange",hpPack); //全局事件中心 + } + } +} + +class PlayerHp +{ + public ECSEventCmpt Event { get; set; } + PlayerHp(){ + Event.AddEventListener(Hellp,HandleUpChange); + } +} + +[Window(UILayer.UI)] +class BattleMainUI: UIWindow +{ + public override void RegisterEvent() + { + AddUIEvent(Hellp,HandleUpChange); + } + + public void HandleUpChange(HpPack pack){} +} +``` \ No newline at end of file diff --git a/Books/3-3-内存池模块.md b/Books/3-3-内存池模块.md new file mode 100644 index 0000000..74c52f5 --- /dev/null +++ b/Books/3-3-内存池模块.md @@ -0,0 +1,23 @@ +# 3-3.内存池模块 - MemoryPool +内存池更为轻量化,相对于对象池更适合一些更抽象碎片化的内存对象。 + +Scene窗口MemoryPool对象可以设置内存池检查,防止回收问题与内存泄漏问题。 +![image](src/3-3-1.png) + +使用案例 +``` csharp +/// +/// 资源组数据。 +/// DisposeGroup。 +/// +public class AssetGroup : IMemory +{ + public void Clear(){} +} + +//内存池中获取/生成内存对象。 +AssetGroup assetGroup = MemoryPool.Acquire(); + +//释放内存对象还给内存池。 +MemoryPool.Release(assetGroup); +``` \ No newline at end of file diff --git a/Books/3-4-对象池模块.md b/Books/3-4-对象池模块.md new file mode 100644 index 0000000..debdbd2 --- /dev/null +++ b/Books/3-4-对象池模块.md @@ -0,0 +1,64 @@ +# 3-4.对象池模块 - ObjectModule +对象池较中量级,在客户端开发中是一个经常使用的技术,技术点我相信大家都懂,这里不过多讨论。 + +使用案例 +``` csharp +/// +/// Actor对象。 +/// +public class Actor : ObjectBase +{ + /// + /// 释放对象。 + /// + /// 是否是关闭对象池时触发。 + protected override void Release(bool isShutdown){} + + /// + /// 创建Actor对象。 + /// + /// 对象名称。 + /// 对象持有实例。 + /// + public static Actor Create(string name, object target) + { + var actor = MemoryPool.Acquire(); + actor.Initialize(name, target); + return actor; + } +} + +/// +/// Actor对象池。 +/// +private IObjectPool _actorPool; + +void Start() +{ + //创建允许单次获取的对象池。 + _actorPool = GameModule.ObjectPool.CreateSingleSpawnObjectPool(Utility.Text.Format("Actor Instance Pool ({0})", name)); +} + +/// +/// 创建Actor对象。 +/// +/// 对象名称。 +/// 对象持有实例。 +/// Actor对象实例 +public Actor CreateActor(string actorName, GameObject target) +{ + Actor ret = null; + if (_actorPool.CanSpawn()) + { + ret = _actorPool.Spawn(); + } + else + { + ret = Actor.Create(actorName, target); + _actorPool.Register(ret,true); + } + + return ret; +} + +``` \ No newline at end of file diff --git a/Books/3-5-UI模块.md b/Books/3-5-UI模块.md new file mode 100644 index 0000000..06bf04b --- /dev/null +++ b/Books/3-5-UI模块.md @@ -0,0 +1,126 @@ +# 3-5.UI模块 - UIModule +一个游戏70%都是UI,剩下30%才是GamePlay,所以有一套简洁强大的商业化UI模块以及UI开发工作流将是项目的一大利器,能够提高至少一倍的开发效率。(配合事件模块实现MVE[Model - View - Event]事件驱动架构) + +UI脚本为纯C#实现,脱离Mono的生命周期,由UIModule的帧更新驱动并管理UI的生命周期。 + +IUIBehaviour为UI通用行为接口、UIBase为UI基类、UIWindow为UI窗口基类,UIWidget为UI组件基类。 + +## 前期配置: +注意!!!!m_item节点为特殊节点表示是UI下的UIWidget组件,不会继续往下遍历生成UI代码。若需要这个UIWidget组件m_item的代码则在m_item右键生成这个组件的UI脚本。 + +Scene窗口下右键ScriptGenerator菜单下About目录有默认UI命名前缀规范。 +![image](src/3-5-1.png) + +有自定义需求可以在TEngineSetting下进行自定义。 +![image](src/3-5-2.png) +### 开发工作流: +1.遵守前期默认配置或者自定义配置进行UI编排 +![image](src/3-5-3.png) + +2.在UI的根节点右键ScriptGenerator生成UI代码到剪贴板上!!!(注-使用-UniTask的生成代码可以做异步事件流驱动的UI。) +![image](src/3-5-4.png) + +3.自行创建UI脚本到需要的目录下并复制UI脚本。 + +## 举例示范 +``` csharp +// 同步打开面板 +GameModule.UI.ShowUI([nullable]userData); + +// 异步打开面板 +GameModule.UI.ShowUIAsync([nullable]userData); + +namespace GameLogic +{ + /// + /// BattleMainUI面板 + /// UIWindow需要以下特性,UILayer可以自行定义,fullScreen表示为全屏面板会停止和隐藏这个面板堆栈后面的面板。 + /// + [Window(UILayer.Bottom,fullScreen:true)] + class BattleMainUI : UIWindow + { + private TouchMove m_touchView; + + #region 脚本工具生成的代码 + private RectTransform m_rectContainer; + private GameObject m_itemTouch; + private Button m_btnLeaveBattle; + private GameObject m_goTopInfo; + private Button m_btnPause; + public override void ScriptGenerator() + { + m_rectContainer = FindChildComponent("m_rectContainer"); + m_itemTouch = FindChild("m_rectContainer/m_itemTouch").gameObject; + m_btnLeaveBattle = FindChildComponent