1.本来关于Addressable 的文章已经写了2篇了,但对这个addressable系统有些地方还是不清楚怎么用,这次就用来记录一下结合xlua 实现资源和代码的全更新方案。
列取一下一开始遇到的问题:
1.xlua 开发的时候我们用的都是.lua 文件,这个是unity 无法识别的文件这个要怎么解决?
2.xlua 读取文件的bytes 都是同步读取方式,最新的addressable 很多的同步读取方式都已经弃用。怎么解决这个同步读取问题?
带着这些问题,下面介绍我提供的解决方案。
分享一下个人Demo工程:https://github.com/IsaWinding/AddressableXluaFramework.git
直接上干货:
- .lua 文件转.txt 文件,并且参与Addressable 的分组策略:
[MenuItem("AddressableMenu/将Xlua .lua文件转为.txt 文件", false, 1)]
public static void OnKeyUpdateAllLuaToTxt()
{
var txtPath = GameSettings.GetLuaTxtPath();
var luaPath = GameSettings.GetLuaResourcesPath();
//FileHelper.DeleteFilesExcept(txtPath, ".txt");
FileHelper.DeleteCreateNewDirectory(txtPath);
FileHelper.CopyDirectoryAndSuffix(luaPath, txtPath, ".lua", ".txt");
AssetDatabase.Refresh();
}
主要是复制.lua 文件到一个特定的.txt 问价夹下,并修改其后缀名。
public static List<string> specPath = new List<string> { "LocalChange", "LocalDontChange",
"RemoteChange", "RemoteDontChange", "LuaTxt" };
特殊分组策略。主要将特殊的文件夹,作为组名进行分组。
分组策略介绍
a.LocalChange 组下的资源,会入初始包。如果后期更新的话,下面的资源会整体更新。
b.LocalNoChange 组下的资源,会入初始包。如果后期更新的话,会将有变化的资源列入新的RomateChange组,打入下次更新资源中。
c.RomateChange 组下的资源 ,不会入初始包,如果后期更新的话,下面的资源会整体更新。
d. RomateNoChange 组下的资源,不会入初始包。如果后期更新的话,会将有变化的资源列入新的RomateChange组,打入下次更新资源中。
清理上次打包的资源包括服务器热更新数据
[MenuItem("AddressableMenu/清理上次打包的资源包括服务器热更新数据", priority = 2)]
public static void ClearAllAddressBuild()
{
AddressableAssetSettings.CleanPlayerContent();
var serverDataPath = GetServerDataPath();
Debug.Log("clear serverdata " + serverDataPath);
if (System.IO.Directory.Exists(serverDataPath))
{
System.IO.Directory.Delete(serverDataPath, true);
}
}
重新构建资源
/// <summary>
/// 重新构建Address资源
/// </summary>
[MenuItem("AddressableMenu/清理上次构建的资源并且重新打包", priority = 3)]
public static void ReBuildAddress()
{
ClearAllAddressBuild();
//1.更新最新的lua 文件
OnKeyUpdateAllLuaToTxt();
//2.对所有资源进行重新分组打标签
LoopSetAllDirectorToAddress(GameSettings.GetABRootPath());
AddressableAssetSettings.BuildPlayerContent();
}
/// 对比更新列表
[MenuItem("AddressableMenu/获取和上次发包的差异列表", priority = 4)]
public static void CheckForUpdateContent()
{
//与上次打包做资源对比
string buildPath = ContentUpdateScript.GetContentStateDataPath(false);
var m_Settings = AddressableAssetSettingsDefaultObject.Settings;
List<AddressableAssetEntry> entrys = ContentUpdateScript.GatherModifiedEntries(m_Settings, buildPath);
if (entrys.Count == 0)
{
Debug.Log("没有资源更新");
return;
}
StringBuilder sbuider = new StringBuilder();
sbuider.AppendLine("Need Update Assets:");
foreach (var _ in entrys){
sbuider.AppendLine(_.address);
}
Debug.Log(sbuider.ToString());
//将被修改过的资源单独分组
var groupName = string.Format("UpdateGroup_{0}", DateTime.Now.ToString("yyyyMMddHHmmss"));
ContentUpdateScript.CreateContentUpdateGroup(m_Settings, entrys, groupName);
}
//迭代打包
[MenuItem("AddressableMenu/迭代构建资源", priority = 5)]
public static void BuildUpdate()
{
//1.更新最新的lua 文件
OnKeyUpdateAllLuaToTxt();
//2.对Lua 文件进行重新分组打标签
LoopSetAllDirectorToAddress(GameSettings.GetLuaTxtPath());
//3.对比更新列表
CheckForUpdateContent();
var path = ContentUpdateScript.GetContentStateDataPath(false);
var m_Settings = AddressableAssetSettingsDefaultObject.Settings;
AddressablesPlayerBuildResult result = ContentUpdateScript.BuildContentUpdate(AddressableAssetSettingsDefaultObject.Settings, path);
Debug.Log("BuildFinish path = " + m_Settings.RemoteCatalogBuildPath.GetValue(m_Settings));
}
//迭代打包
[MenuItem("AddressableMenu/复制服务器热更新资源到本地服务器WWW路径", priority = 6)]
public static void CopyServerDataToLocalServer()
{
FileHelper.CopyDirectory(GetServerDataPath(),LocalServerWWWPath);
Debug.Log("复制服务器热更新资源到本地服务器完成 路径:" +LocalServerWWWPath);
}
[MenuItem("AddressableMenu/打印构建路径", priority = 7)]
public static void Test()
{
Debug.Log("BuildPath = " + Addressables.BuildPath);
Debug.Log("PlayerBuildDataPath = " + Addressables.PlayerBuildDataPath);
Debug.Log("RemoteCatalogBuildPath = " + AddressableAssetSettingsDefaultObject.Settings.RemoteCatalogBuildPath.GetValue(AddressableAssetSettingsDefaultObject.Settings));
}
private static void LoopSetAllDirectorToAddress(string pFileDirectorRoot)
{
if (Directory.Exists(pFileDirectorRoot))
{
SetDirectorABNameNull(pFileDirectorRoot);
var dirctory = new DirectoryInfo(pFileDirectorRoot);
var direcs = dirctory.GetDirectories("*", SearchOption.TopDirectoryOnly);
if (direcs.Length > 0)
{
for (var i = 0; i < direcs.Length; i++)
{
if (direcs[i].FullName != pFileDirectorRoot)
{
LoopSetAllDirectorToAddress(direcs[i].FullName);
}
}
}
}
}
private static void SetDirectorABNameNull(string pFileDirectorRoot)
{
if (Directory.Exists(pFileDirectorRoot))
{
var dirctory = new DirectoryInfo(pFileDirectorRoot);
var files = dirctory.GetFiles("*", SearchOption.TopDirectoryOnly);
bool isAdd = false;
for (var i = 0; i < files.Length; i++)
{
var file = files[i];
if (file.Name.EndsWith(".meta"))
continue;
string assetPath = file.FullName;
assetPath = FormatFilePath(assetPath);
var assetLength = UnityEngine.Application.dataPath.Length - 6;
assetPath = assetPath.Substring(assetLength, assetPath.Length - assetLength);
var groupName = GetGroupName(dirctory.Name, assetPath);
AutoGroup(groupName, assetPath, groupName);
isAdd = true;
}
if (isAdd)
AssetDatabase.Refresh();
}
}
public static string GetGroupName(string pDirctoryName, string pAssetPath)
{
var groupName = pDirctoryName;
foreach (var item in specPath)
{
if (pAssetPath.Contains(item))
{
groupName = item;
break;
}
}
return groupName;
}
public static string FormatFilePath(string filePath)
{
var path = filePath.Replace('\\', '/');
path = path.Replace("//", "/");
return path;
}
public static void AutoGroup(string groupName, string assetPath,string pLable)
{
var settings = AddressableAssetSettingsDefaultObject.Settings;
AddressableAssetGroup group = settings.FindGroup(groupName);
if (group == null){
group = CreatAssetGroup<System.Data.SchemaType>(settings, groupName);
}
var guid = AssetDatabase.AssetPathToGUID(assetPath);
var entry = settings.CreateOrMoveEntry(guid, group);
entry.address = assetPath;
entry.SetLabel(pLable, true, true);
}
private static AddressableAssetGroup CreatAssetGroup<SchemaType>(AddressableAssetSettings settings, string groupName)
{
return settings.CreateGroup(groupName, false, false, false,
new List<AddressableAssetGroupSchema> { settings.DefaultGroup.Schemas[0], settings.DefaultGroup.Schemas[1] }, typeof(SchemaType));
}
游戏启动后的更新流程:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.UI;
public class AddressUpdater : MonoBehaviour
{
private string str;
public Text outputText;
private List<object> _updateKeys = new List<object>();
void Start(){
UpdateCatalog();
}
/// <summary>
/// 对比更新Catalog
/// </summary>
public async void UpdateCatalog(){
str = "";
var handlew = Addressables.InitializeAsync();
await handlew.Task;
//开始连接服务器检查更新
var handle = Addressables.CheckForCatalogUpdates(false);
await handle.Task;
Debug.Log("check catalog status " + handle.Status);
ShowLog("check catalog status " + handle.Status);
if (handle.Status == AsyncOperationStatus.Succeeded)
{
List<string> catalogs = handle.Result;
if (catalogs != null && catalogs.Count > 0)
{
foreach (var catalog in catalogs)
{
ShowLog("catalog " + catalog);
}
Debug.Log("download catalog start ");
str += "download catalog start \n";
outputText.text = str;
var updateHandle = Addressables.UpdateCatalogs(catalogs, false);
await updateHandle.Task;
foreach (var item in updateHandle.Result)
{
ShowLog("catalog result " + item.LocatorId);
foreach (var key in item.Keys){
Debug.Log("catalog key " + key);
ShowLog("catalog key " + key);
}
_updateKeys.AddRange(item.Keys);
}
ShowLog("download catalog finish " + updateHandle.Status);
}
else
{
Debug.Log("dont need update catalogs");
ShowLog("dont need update catalogs");
}
}
Addressables.Release(handle);
DownLoad();
}
/// <summary>
/// 主界面显示Log
/// </summary>
/// <param name="textStr"></param>
private void ShowLog(string textStr){
str += textStr + "\n";
outputText.text = str;
}
public IEnumerator DownAssetImpl(){
var downloadsize = Addressables.GetDownloadSizeAsync(_updateKeys);
yield return downloadsize;
Debug.Log("start download size :" + downloadsize.Result);
ShowLog("start download size :" + downloadsize.Result);
if (downloadsize.Result > 0){
var download = Addressables.DownloadDependenciesAsync(_updateKeys);//, Addressables.MergeMode.Union
yield return download;
//await download.Task;
if (download.Result != null)
{
Debug.Log("download result type " + download.Result.GetType());
ShowLog("download result type " + download.Result.GetType());
foreach (var item in download.Result as List<UnityEngine.ResourceManagement.ResourceProviders.IAssetBundleResource>)
{
var ab = item.GetAssetBundle();
Debug.Log("ab name " + ab.name);
ShowLog("ab name " + ab.name);
foreach (var name in ab.GetAllAssetNames())
{
Debug.Log("asset name " + name);
ShowLog("asset name " + name);
}
}
}
Addressables.Release(download);
}
Addressables.Release(downloadsize);
OnLoadXLuaClient();
}
/// <summary>
/// 下载资源
/// </summary>
public void DownLoad()
{
str = "";
StartCoroutine(DownAssetImpl());
//CtrlLoadSlider();
}
public void OnLoadXLuaClient()
{
//AddressLoadManager.LoadLuaTxtRes(()=> {
// if (XLuaClient.Instance == null)
// {
// var go = new GameObject("XLuaClient");
// go.AddComponent<XLuaClient>();
// }
// XLuaClient.Instance.StartMain();
// OnDownLoadFinish();
//});
if (XLuaClient.Instance == null)
{
var go = new GameObject("XLuaClient");
go.AddComponent<XLuaClient>();
}
else
{
XLuaClient.Instance.OnClear();
}
OnDownLoadFinish();
}
public void OnDownLoadFinish()
{
ShowLog("下载资源完成!!");
UnityEngine.SceneManagement.SceneManager.LoadScene("2_GameMain");
}
}