介绍了Unity热更新的整个流程,其中用到了部分lua知识,有兴趣的可以自行了解
文章目录
一、AssetBundle
1、AssetBundle的作用
将一些资源(声音,图片,预设体等)分类打包,在游戏开始前统一加载。每次从服务器上下载,与本地资源的CRC值进行比较,更新本地资源来确保热更新。
2、AssetBundle的依赖
将一个预设体打包的时候,如果当前预设里含有其他AssetBundle的声音文件、动画文件或其他的文件时,该预设体AssetBundle包中会标注所依赖的包文件,在加载的时候要先加载被依赖的包,含有脚本文件时要确保项目中是否存在相应的脚本。
3、AssetBundle的建立
- 在项目根目录创建AssetBundles文件夹用来存放所有AssetBundle文件
- 编写生成AssetBundle文件的代码,放到Assets/Editor下
using UnityEditor;
using System.IO;
public class CreateAssetBundles
{
/// <summary>
/// 创建所有设置的AssetBundle文件
/// </summary>
[MenuItem("Assets/Build AssetBundles")] //设置到编辑器模式的Assets下Build AssetBundles选项方便调用
static void BuildAllAssetBundles()
{
string dir = "AssetBundles"; //设置AssetBundle根目录路径
//不存在就去创建
if (Directory.Exists(dir) == false)
{
Directory.CreateDirectory(dir);
}
BuildPipeline.BuildAssetBundles(dir, BuildAssetBundleOptions.UncompressedAssetBundle, BuildTarget.StandaloneWindows64); //生成所有设置的AssetBundle
}
}
- 从Project视图下选中需要打到一个AssetBundle包的资源,设置其AssetBundle名称(名称中带有/表示路径,不区分大小写)和后缀,点击Assets下Build AssetBundles选项生成AssetBundle(不会删除之前存在的文件),完成AssetBundle的建立
-
点击资源
-
找到Inspector面板最下方的Blue双击打开设置AssetBundle
-
打开的AssetBundle属性
-
设置AssetBundle
4、AssetBundle的加载
1)manifest文件
//通过AssetBundles包获取manifest文件
string manifestFilePath = "AssetBundles/AssetBundles";
AssetBundle assetBundle = AssetBundle.LoadFromFile(manifestFilePath);
AssetBundleManifest manifest = assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest"); //从AssetBundles包中加载manifest文件
string[] dependencies = manifest.GetAllDependencies("cube.ab"); //获取加载包中的所有依赖
foreach (string dependency in dependencies)
{
AssetBundle.LoadFromFile(Path.Combine(@"D:\mineunity\Test\Test\AssetBundles", dependency)); //加载所有依赖
}
2)内存加载AssetBundle
//异步内存加载
string path1 = "AssetBundles/main/01.ab"; //cube.ab依赖01.ab
AssetBundleCreateRequest request = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path1));
yield return request;
AssetBundle ab1 = request.assetBundle;
//内存加载
string path2 = "AssetBundles/cube.ab"; //预设体保存在cube.ab中
AssetBundle ab2 = AssetBundle.LoadFromMemory(File.ReadAllBytes(path2));
3)本地文件加载AssetBundle
//异步文件加载
string path1 = "AssetBundles/main/01.ab"; //cube.ab依赖01.ab
AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(path1);
yield return request;
AssetBundle ab1 = request.assetBundle;
//文件加载
string path2 = "AssetBundles/cube.ab"; //预设体保存在cube.ab中
AssetBundle ab2 = AssetBundle.LoadFromFile(path2);
4)WWW加载AssetBundle
现已弃用
5)UnityWebRequest加载AssetBundle
string uri1 = @"file:///D:\mineunity\Test\Test\AssetBundles\main\01.ab"; //绝对路径
string uri2 = @"file:///D:\mineunity\Test\Test\AssetBundles\cube.ab";
//string uri1 = @"http://localhost/AssetBundles/main/01.ab"; //服务器地址
//string uri2 = @"http://localhost/AssetBundles/cube.ab";
//通过UnityWebRequest获取
UnityWebRequest request1 = UnityWebRequest.GetAssetBundle(uri1); //加载对应地址AssetBundle
yield return request1.SendWebRequest();
UnityWebRequest request2 = UnityWebRequest.GetAssetBundle(uri2);
yield return request2.SendWebRequest();
//AssetBundle ab = DownloadHandlerAssetBundle.GetContent(request);
AssetBundle ab1 = (request1.downloadHandler as DownloadHandlerAssetBundle).assetBundle;
AssetBundle ab2 = (request2.downloadHandler as DownloadHandlerAssetBundle).assetBundle;
6)通过AssetBundle加载资源
//实例化指定资源,名字需要对应
GameObject cubePrefab = ab2.LoadAsset<GameObject>("Cube"); //也可以指定非GameObject类型资源
Instantiate(cubePrefab);
//实例化包中所有资源
Object[] objs = ab2.LoadAllAssets();
foreach (Object o in objs)
{
Instantiate(o);
}
5、AssetBundle的卸载
1)Resources卸载
- Resources.UnloadAsset卸载指定的资源
- Resources.UnloadUnusedAssets卸载所有没在使用的资源
2)AssetBundle卸载
- AssetBundle.Unload(false),卸载AssetBundle所有没在使用的资源,如果卸载后重新加载了资源,内存中会加载两个同样的AssetBundle文件(一个被引用,另一个没有),造成冗余
- AssetBundle.Unload(true),卸载AssetBundle所有资源,包括正在使用的资源,一般在场景切换时使用
二、xlua
1、xlua的优势
十分方便编译(可以认为不需要编译就可以运行),而C#需要编译后在其他系统中使用,修改起来非常麻烦,在热更新时需要将修改后的项目编译重新打包发布,这时候就可以借助xlua修改部分C#代码进行发布,只需要将xlua文件重新发布即可,修改文件大小比重新发布项目小很多,方便下载。
2、xlua插件的下载
3、xlua插件的导入
- 解压下载好的xlua后,将xLua-master\Assets目录下的所有文件拷贝到项目Assets目录下(可将XLua\Examples删除,不删除可能会报错)
- 将xLua-master下的Tools文件夹拷贝到项目文件夹下
- 将unity编辑器目录下的Editor\Data\Managed中的Unity.Cecil.dll、Unity.Cecil.Mdb.dll、Unity.Cecil.Pdb.dll三个文件拷贝到项目Assets\XLua\Src\Editor目录下
- 打开Unity编辑器中file下的Build Settings,打开player Settings
- 修改Other Settings下的Scripting Define Symbols为HOTFIX_ENABLE,回车确认
- 修改Scripting Define Symbols
Tips:目录中最好不要有中文
4、xlua的使用
1)命名空间
使用using XLua;
2)C#中xlua的特性
a)[hotfix]
一般标注在有可能要修改的类上,让lua知道这段代码有热更新
b)[LuaCallCSharp]
一般用于将lua中的代码映射到C#中
c)[CSharpCallLua]
一般标注在需要修改的方法上,没有该特性将无法用lua修改
3)C#中使用xlua
在添加HOTFIX_ENABLE后,每次更改代码后,都要调用xlua下的Generate Code,看到finish提示后,在调用xlua下的Hotfix Inject进行注入,然后才能运行代码
4)C#中加载lua代码
一般使用自定义的加载器加载lua代码,然后通过一句lua代码进行调用
- 自定义loader装载器:
private byte[] MyLoader(ref string filePath)
{
string absPath = @"C:\Users\Administrator\Desktop\XluaProjects\PlayerGamePackage\"+filePath+".lua.txt"; //使用Recourse加载时,只能识别txt后缀
return System.Text.Encoding.UTF8.GetBytes(File.ReadAllText(absPath)); //注意编码格式,转译成二进制数组返回
}
- 使用自定义loader
private void Awake()
{
luaEnv = new LuaEnv(); //实例化lua环境,程序中应唯一
luaEnv.AddLoader(MyLoader); //加载自定义loader,在读取时会先使用自带loader寻找,寻找不到使用自定义loader寻找,还寻找不到就返回找不到文件
luaEnv.DoString("require'{0}'", luaName); //luaName为自定义lua文件名称
}
- 销毁lua环境
private void OnDestroy()
{
luaEnv.DoString("require'{0}'", luaDisposeName); //调用lua代码销毁所有lua连接
luaEnv.Dispose(); //销毁lua环境
}
5)lua中修改C#
a)lua热更新文件
--由于需要多次使用UnityEngine下的方法,直接存放起来,需要的时候直接使用
local UnityEngine=CS.UnityEngine
--调用该代码可访问C#类中的私有属性
xlua.private_accessible(CS.Test)
--调用C#中的静态方法可直接.方法名,若不为静态方法则需要把自身传递过去或者使用:调用方法
--使用hotfix函数修改C#中代码,第一个参数为类名,第二个参数为方法名,第三个参数为修改的函数内容
xlua.hotfix(CS.Test,'TestMethod',function(self)
UnityEngine.Debug.Log('Hello lua')
end)
--如果想访问私有方法,则应在引用private_accessible后,使用lua的util访问
local util=require 'util'
xlua.private_accessible(CS.Test2)
util.hotfix_ex(CS.Test2, 'Start', function(self)
--lua中self和C#中this类似
self.Start(self)
--lua中没有+=等运算符号
self.number = self.number + 1
--lua中for循环第一个参数为起始值,第二个参数为结束值<=,第三个参数为步长
for i=0, self.number, 1 do
self:TestMethod(self.number)
end
end)
--在lua中数字变量统一为number类型,在C#中去值时如果类型不对应(比如number中存了float值而在C#中用int类型取值),则会返回0
--由于lua不能在C#中创建类和方法,我们可以采取在C#中预先创建几个空类和空方法提供给lua进行修改,作为在lua给C#添加新类的解决方案
b)lua销毁文件
--将所有引用过的方法销毁,之后才能销毁lua环境
xlua.hotfix(CS.Test, 'TestMethod', nil)
xlua.hotfix(CS.Test2, 'TestMethod', nil)
5、总结
- lua提供给C#修复部分代码,而不需要将整个项目重新发布的功能,用于每个小版本的更新或者bug修复,在大版本更新时(大量更改项目中的类和方法),就不推荐使用lua修改了
- 预先估计好有可能更改的类及方法,打上特性标记,才能使用lua修改代码
- 在项目中设置好类(最好是单例),单独运行lua脚本