本文章都是建立在Winodws平台下测试的,其他平台是否同样适用还未进行验证。
TE框架还在更新中,未来可能有较大变动。
前言
所有的功能和代码的演示都是基于以下项目演示的,欢迎大家参考。
GitHub: 基于TEngine框架实现的塔防Demo
热更新
TE框架自带了一套完整的热更流程,也有很多优秀的博主做过相关教程,这里的文章以记录为主补充为辅,希望能够帮助到在学习TE框架时遇到问题的读者。
启用热更新
参数设置
大部分的热更设置都能在UpdateSetting
文件中修改,ResDownLoadPath
就是资源下载的地址,还有诸如是否强制更新、是否提醒更新这里不做赘述,可以看源码理解一下,这里只涉及配置没有涉及到逻辑功能。
打包
上面的两步是生成代码的热更文件和插件Dll,运行成功的话再AssetRaw目录下DLL文件夹
下应该包含dll文件,如果为空请查看一下热更生成报错。
上面的图一偷一个懒,将工程设置和YooAsset构建放在一起了,相同颜色的方框代表两个选项要保持一致,就如上图,默认包为DefaultPackage,加密方式为File Off Set。
图二展示的是几种打包方式,如果选择可以查看官网教程,这里单独说两种,
None
:构建资源但是不做任何操作(StreamingAssets为空)。
ClearAndCopyAll
:构建资源并且复制到StreamingAssets下。
前者方便我们单独打更新包或者补充包,后者方便打首包,具体区别可以都试试看。不管怎么样,StreamingAssets下至少应该要包含图三中的文件,本地会用这些文件和服务器中的索引进行比较,没有的话会报错。
测试
可以使用HFS文件服务器部署本地的服务器,如果是默认的配置,将打包生成的所有文件放入LocalServer\yourProjectName\Windows64\xxxx
下面`,运行工程即可。

本地测试需要打开HTTP链接,否则会报错,Unity默认是关闭的。
分包下载
目前框架中的逻辑是一次性下载完全部的资源,这样的坏处就是会造成内存浪费,如果是强制下载再进入游戏的话也会使进入游戏时间过长,体验拉跨。但是在实际开发中,往往会需要我们按需下载,比如只下载下一关、根据玩家选择下载不同的内容,这里就对这个功能进行了部分扩展。
如何分包
YooAsset中将资源分为Package和Asset Tags,下面有对应的总结。博主这里用的是Tags来进行资源管理,主要原因是对于目前正在做的Demo足够了。另一个原因是,如果用Package的话,最理想的方式还需要额外写一套方法,将附加Package的资源索引也当作热更资源,以后遇到使用场景再尝试添加吧。
维度 |
Package(资源包裹) |
Asset Tags(资源标签) |
本质 |
构建单元(Build Unit) |
逻辑标记(Label / Flag) |
作用 |
隔离资源、独立构建、独立补丁 |
给资源打关键字,用于运行时筛选 |
构建结果 |
生成独立的补丁文件夹(patch) |
无独立文件,仅写入清单(manifest) |
数量 |
多个 Package 可并存(如 Main、DLC、MOD) |
单个资源可打多个标签(用“;”分隔) |
使用场景 |
模块化打包、DLC/渠道包、并行构建 |
首包控制、按需下载、版本差异、运行时过滤 |
资源打包


图一是资源打标签,博主这里添加了三个场景,分别对应Tags,Level3 、4、5,也就是这次需要按需下载的资源,其余的资源全部为Base。
图二是出包的设置,这里选择按标签复制,图上会把Base标签的资源打入StreamingAssets,外部输出的则是所有资源。我们直接将输出的文件丢到服务器上即可。
修改代码
1、脚本IResourceModule
中新重载创建下载器函数,第一个接口是默认的,第二个接口是我们新增的,string[] tags
就是传入指定Tags的参数。
1 2 3 4 5 6 7 8 9 10 11
|
ResourceDownloaderOperation CreateResourceDownloader(string customPackageName = "");
ResourceDownloaderOperation CreateResourceDownloader(string[] tags, string customPackageName = "");
|
2、在ResourceModule
中实现刚才的接口,另外新增一个判断标签是否存在函数,因为YooAsset本来就能区分标签,只用随便Copy下就行。
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
|
public ResourceDownloaderOperation CreateResourceDownloader(string[] tags, string customPackageName = "") { ResourcePackage package = null; if (string.IsNullOrEmpty(customPackageName)) { package = YooAssets.GetPackage(this.DefaultPackageName); } else { package = YooAssets.GetPackage(customPackageName); } Downloader = package.CreateResourceDownloader(tags, DownloadingMaxNum, FailedTryAgain); return Downloader; }
public bool IsTagResourcesExist(string tag, string packageName = "") { var package = string.IsNullOrEmpty(packageName) ? YooAssets.GetPackage(DefaultPackageName) : YooAssets.GetPackage(packageName);
var bundleInfos = package.GetAssetInfo(tag);
if (bundleInfos == null) { Log.Warning($"No resources found for tag: {tag}"); return false; }
return true; }
|
3、ProcedureCreateDownloader
脚本是游戏启动时检测线上资源的脚本,将CreateResourceDownloader
方法传入Base
就可以做到启动游戏是判断Base
资源是否完整了
1 2
| _downloader = _resourceModule.CreateResourceDownloader(GameModule.Resource.DefaultTagName);
|
4、最后就是按需下载资源,这里贴出一个参考代码,博主这边测试是能够运行的,但是很明显代码的保护机制做的不是很完善,最核心的代码就是HandleLevelDownloadAsync
函数前面几行,框架里的用法其实大差不多,只是机制更加完整。
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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| private async UniTask ShowLevelSelectionButtonItems() { int index = 0; List<UniTask> downloadTasks = new List<UniTask>();
foreach (var itemdata in LevelDataControl.Instance.GetAllLevelData()) {
var task = HandleLevelDownloadAsync(itemdata.GroupName, itemdata.PackageName, itemdata.Id); downloadTasks.Add(task);
index++; }
await UniTask.WhenAll(downloadTasks); }
private async UniTask HandleLevelDownloadAsync(string tag, string packageName, int levelId) {
var versionOp = GameModule.Resource.RequestPackageVersionAsync(false, 60, packageName); await versionOp.ToUniTask();
await GameModule.Resource.UpdatePackageManifestAsync(versionOp.PackageVersion, 60, packageName); var downloader = GameModule.Resource.CreateResourceDownloader(new string[] { tag }, packageName);
if (downloader.TotalDownloadCount > 0) { Log.Debug("开始下载关卡资源包: {0} - {1}", packageName, levelId); downloader.DownloadUpdateCallback = (downloadUpdateData) => { Log.Debug("download update {0}", downloader.Progress); GameEvent.Send(ChangeSceneEvent.LevelDownloadProgress, new LevelDownloadProgress(levelId, downloader.Progress)); };
downloader.DownloadErrorCallback = (error) => { Log.Error($"下载出错 [{packageName}] : {error}"); GameEvent.Send(ChangeSceneEvent.LevelDownloadProgress, new LevelDownloadProgress(levelId, -1f)); };
downloader.BeginDownload(); await downloader.ToUniTask();
if (downloader.IsDone) { GameEvent.Send(ChangeSceneEvent.LevelDownloadProgress, new LevelDownloadProgress(levelId, 1f)); } else { Log.Error($"资源包 {packageName} 下载失败!"); GameEvent.Send(ChangeSceneEvent.LevelDownloadProgress, new LevelDownloadProgress(levelId, -1f)); } } else { GameEvent.Send(ChangeSceneEvent.LevelDownloadProgress, new LevelDownloadProgress(levelId, 1f)); } }
|