游戏的资源类型大致分为:
根据Addressable系统的特性,我们大致可以将游戏的资源文件分两类:
根据文件夹路径,我们将对资源进行自动标记地址,标签以及分组类型(本地或远程)。
///
/// 自动创建图集
///
/// 路径
/// 文件夹
private static void addSpriteAtlas(string path, DirectoryInfo dir)
{
var dirs = dir.GetDirectories();
if (dirs == null || dirs.Length == 0)
{
string name = path.Replace(AtlasRoot + "/", string.Empty).Replace("/", "_");
string filePath = SpriteAtlas + "/" + name + ".spriteatlas";
if (File.Exists(filePath))
{
int assetIndex = filePath.IndexOf("Assets");
string guidPath = filePath.Remove(0, assetIndex);
var guid = AssetDatabase.AssetPathToGUID(guidPath);
var group = setting.FindGroup("Local_SpriteAtlas");
var entry = setting.CreateOrMoveEntry(guid, group);
var label = name + ".spriteatlas";
if (entry.address != name)
{
entry.SetAddress(name);
addAddressInfo("Local_SpriteAtlas", name);
}
List<string> oldLabels = new List<string>();
foreach (var item in entry.labels)
{
if (item != label)
oldLabels.Add(item);
}
for (int i = 0; i < oldLabels.Count; i++)
{
entry.SetLabel(oldLabels[i], false);
setting.RemoveLabel(oldLabels[i]);
}
if (!setting.GetLabels().Contains("SpriteAtlas"))
{
setting.AddLabel("SpriteAtlas");
}
entry.SetLabel("SpriteAtlas", true);
if (!setting.GetLabels().Contains(label))
{
setting.AddLabel(label);
}
entry.SetLabel(label, true);
return;
}
else
{
SpriteAtlas atlas = new SpriteAtlas();
//设置打包参数
SpriteAtlasPackingSettings packSetting = new SpriteAtlasPackingSettings()
{
blockOffset = 1,
enableRotation = true,
enableTightPacking = false,
padding = 2,
};
atlas.SetPackingSettings(packSetting);
//设置打包后Texture图集信息
SpriteAtlasTextureSettings textureSettings = new SpriteAtlasTextureSettings()
{
readable = false,
generateMipMaps = false,
sRGB = true,
filterMode = FilterMode.Bilinear,
};
atlas.SetTextureSettings(textureSettings);
//设置平台图集大小压缩等信息
TextureImporterPlatformSettings platformSettings = new TextureImporterPlatformSettings()
{
maxTextureSize = 4096,
format = TextureImporterFormat.Automatic,
crunchedCompression = true,
textureCompression = TextureImporterCompression.Compressed,
compressionQuality = 50,
};
atlas.SetPlatformSettings(platformSettings);
int index = filePath.IndexOf("Assets");
string atlasPath = filePath.Remove(0, index);
AssetDatabase.CreateAsset(atlas, atlasPath);
index = path.IndexOf("Assets");
string spritePath = path.Remove(0, index);
Object obj = AssetDatabase.LoadAssetAtPath(spritePath, typeof(Object));
atlas.Add(new[] { obj });
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
int assetIndex = filePath.IndexOf("Assets");
string guidPath = filePath.Remove(0, assetIndex);
var guid = AssetDatabase.AssetPathToGUID(guidPath);
var group = setting.FindGroup("Local_SpriteAtlas");
var entry = setting.CreateOrMoveEntry(guid, group);
var label = name + ".spriteatlas";
if (entry.address != name)
{
entry.SetAddress(name);
addAddressInfo("Local_SpriteAtlas", name);
}
List<string> oldLabels = new List<string>();
foreach (var item in entry.labels)
{
if (item != label)
oldLabels.Add(item);
}
for (int i = 0; i < oldLabels.Count; i++)
{
entry.SetLabel(oldLabels[i], false);
setting.RemoveLabel(oldLabels[i]);
}
if (!setting.GetLabels().Contains(label))
{
setting.AddLabel(label);
}
entry.SetLabel(label, true);
if (!setting.GetLabels().Contains("SpriteAtlas"))
{
setting.AddLabel("SpriteAtlas");
}
entry.SetLabel("SpriteAtlas", true);
AssetDatabase.Refresh();
}
}
else
{
if (dirs.Length > 0)
{
foreach (var info in dirs)
{
addSpriteAtlas(path + "/" + info.Name, info);
}
}
}
}
addressDic.Clear();
/* var groupList = setting.groups;
for (int i = 0; i < groupList.Count; i++)
{
if (!groupList[i].name.Contains("Built In Data") && !groupList[i].name.Contains("Default Local Group"))
setting.RemoveGroup(groupList[i]);
}*/
///创建分组
string loaclRoot = Application.dataPath + "/AddressableAssets/Local";
string remotedRoot = Application.dataPath + "/AddressableAssets/Remoted";
DirectoryInfo[] dirs = new DirectoryInfo(loaclRoot).GetDirectories();
foreach (var info in dirs)
{
string group_name = "Local_" + info.Name;
var group = setting.FindGroup(group_name);
if (group == null)
{
group = setting.CreateGroup(group_name, false, false, false, new List<AddressableAssetGroupSchema> { setting.DefaultGroup.Schemas[0], setting.DefaultGroup.Schemas[1] });
}
AutoMarkRootAddress("Local", info);
if (info.Name != "SpriteAtlas")
AutoMark(info.Name);
}
dirs = new DirectoryInfo(remotedRoot).GetDirectories();
foreach (var info in dirs)
{
string group_name = "Remoted_" + info.Name;
var group = setting.FindGroup(group_name);
if (group == null)
{
group = setting.CreateGroup(group_name, false, false, false, new List<AddressableAssetGroupSchema> { setting.DefaultGroup.Schemas[0], setting.DefaultGroup.Schemas[1] });
}
AutoMarkRootAddress("Remoted", info);
AutoMark(info.Name, false);
}
///自动创建图集
AutoCreateSpriteAtlas();
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Debug.Log("MarkAsset Successful");
private static void markFiles(string path, string name, DirectoryInfo dir, bool local = true)
{
var files = dir.GetFiles();
if (files != null && files.Length > 0)
{
foreach (var file in files)
{
if (file.Extension != ".meta")
{
int index = file.Name.IndexOf(".");
string address = file.Name.Remove(index, file.Name.Length - index);
string group_name = local ? "Local_" + name : "Remoted_" + name;
string assetPath = path + "/" + dir.Name + "/" + file.Name;
List<string> label = new List<string>();
string[] allDirs;
if (local)
{
allDirs = (path + "/" + dir.Name).Replace("Assets/AddressableAssets/Local/" + name + "/", string.Empty).Split('/');
}
else
{
allDirs = (path + "/" + dir.Name).Replace("Assets/AddressableAssets/Remoted/" + name + "/", string.Empty).Split('/');
}
label.Add(name);
for (int i = 0; i < allDirs.Length; i++)
{
label.Add(allDirs[i]);
}
var guid = AssetDatabase.AssetPathToGUID(assetPath);
var group = setting.FindGroup(group_name);
if (group != null)
{
var entry = setting.CreateOrMoveEntry(guid, group);
if (entry.address != address)
{
entry.SetAddress(address);
addAddressInfo(group_name, address + file.Extension);
List<string> oldLabels = new List<string>();
foreach (var item in entry.labels)
{
if (!label.Contains(item))
oldLabels.Add(item);
}
for (int i = 0; i < oldLabels.Count; i++)
{
entry.SetLabel(oldLabels[i], false);
setting.RemoveLabel(oldLabels[i]);
}
for (int i = 0; i < label.Count; i++)
{
var _label = label[i];
if (!setting.GetLabels().Contains(_label))
{
setting.AddLabel(_label);
}
entry.SetLabel(_label, true);
}
}
}
else
{
Debug.LogError("分组 = " + group_name + "不存在");
}
}
}
}
}
自动化打包需要以下几个步骤:
///
/// 标记为资源分组
/// 0 小包,所有资源存放资源服务器
/// 1 分包 ,Local资源存本地,Remoted资源存资源服务器
/// 2 整包,所有资源存本地
///
private void markStatus(int status)
{
List<AddressableAssetGroup> deleteList = new List<AddressableAssetGroup>();
for (int i = 0; i < setting.groups.Count; i++)
{
var group = setting.groups[i];
if (group.name != "Default Local Group" && group.name != "Built In Data")
{
if (group.entries.Count <= 0)
{
///删除没有资源的分组
deleteList.Add(group);
}
else
{
foreach (var schema in group.Schemas)
{
if (schema is UnityEditor.AddressableAssets.Settings.GroupSchemas
.BundledAssetGroupSchema)
{
bool bundleCrc = true;
string buildPath = AddressableAssetSettings.kLocalBuildPath;
string loadPath = AddressableAssetSettings.kLocalLoadPath;
if (group.name.Contains("Local_"))
{
bundleCrc = status == 0;
buildPath = status == 0 ? AddressableAssetSettings.kRemoteBuildPath : AddressableAssetSettings.kLocalBuildPath;
loadPath = status == 0 ? AddressableAssetSettings.kRemoteLoadPath : AddressableAssetSettings.kLocalLoadPath;
}
else if (group.name.Contains("Remoted_"))
{
bundleCrc = !(status == 2);
buildPath = status == 2 ? AddressableAssetSettings.kLocalBuildPath : AddressableAssetSettings.kRemoteBuildPath;
loadPath = status == 2 ? AddressableAssetSettings.kLocalLoadPath : AddressableAssetSettings.kRemoteLoadPath;
}
else if (group.name.Contains("UpdateGroup_"))
{
bundleCrc = true;
buildPath = AddressableAssetSettings.kRemoteBuildPath;
loadPath = AddressableAssetSettings.kRemoteLoadPath;
}
var bundledAssetGroupSchema =
(schema as UnityEditor.AddressableAssets.Settings.GroupSchemas.BundledAssetGroupSchema);
bundledAssetGroupSchema.BuildPath.SetVariableByName(group.Settings,
buildPath);
bundledAssetGroupSchema.LoadPath.SetVariableByName(group.Settings,
loadPath);
bundledAssetGroupSchema.UseAssetBundleCrc = bundleCrc;
bundledAssetGroupSchema.BundleNaming = UnityEditor.AddressableAssets.Settings.GroupSchemas.BundledAssetGroupSchema.BundleNamingStyle.NoHash;
bundledAssetGroupSchema.BundleMode = UnityEditor.AddressableAssets.Settings.GroupSchemas.BundledAssetGroupSchema.BundlePackingMode.PackTogetherByLabel;
}
else if (schema is UnityEditor.AddressableAssets.Settings.GroupSchemas.ContentUpdateGroupSchema)
{
var updateGroupSchema = (schema as UnityEditor.AddressableAssets.Settings.GroupSchemas.ContentUpdateGroupSchema);
if (group.name.Contains("Local_"))
{
updateGroupSchema.StaticContent = !(status == 0);
}
else if (group.name.Contains("Remoted_"))
{
updateGroupSchema.StaticContent = (status == 2);
}
else if (group.name.Contains("UpdateGroup_"))
{
updateGroupSchema.StaticContent = false;
}
}
}
}
}
}
for (int i = 0; i < deleteList.Count; i++)
{
setting.RemoveGroup(deleteList[i]);
}
}
public enum BuildEnvironment
{
Local,//内网测试整包
Debug,
Beta,
Release
}
private void setActiveProfileId()
{
var names = setting.profileSettings.GetAllProfileNames();
if (!names.Contains(environment.ToString()))
{
setting.profileSettings.AddProfile(environment.ToString(), setting.activeProfileId);
}
var id = setting.profileSettings.GetProfileId(environment.ToString());
if (setting.activeProfileId != id)
setting.activeProfileId = id;
if (environment == BuildEnvironment.Local)
{
setting.profileSettings.SetValue(setting.activeProfileId, "RemoteBuildPath", "[UnityEngine.AddressableAssets.Addressables.BuildPath]/[BuildTarget]");
setting.profileSettings.SetValue(setting.activeProfileId, "RemoteLoadPath", "{UnityEngine.AddressableAssets.Addressables.RuntimePath}/[BuildTarget]");
setting.BuildRemoteCatalog = false;
}
else
{
setting.BuildRemoteCatalog = true;
string[] ver = version.Split('.');
string name = setting.profileSettings.GetProfileName(setting.activeProfileId);
string buildPath = "ServerData" + "/[BuildTarget]/" + ver[0] + "." + ver[1] + "/" + name;
if (setting.profileSettings.GetValueByName(setting.activeProfileId, "RemoteBuildPath") != buildPath)
setting.profileSettings.SetValue(setting.activeProfileId, "RemoteBuildPath", buildPath);
string loadPath = url + "/poetry/" + buildPath;
if (setting.profileSettings.GetValueByName(setting.activeProfileId, "RemoteLoadPath") != loadPath)
setting.profileSettings.SetValue(setting.activeProfileId, "RemoteLoadPath", loadPath);
}
}
private void buildByStatus(int status, bool buildApp = true)
{
isBuildSuccess = true;
BuildTools.ClearConsole();
Application.logMessageReceived += onLogMessage;
if (buildApp)
{
/* Generator.ClearAll();
Generator.GenAll();*/
}
AssetDatabase.Refresh();
setActiveProfileId();
AssetDatabase.Refresh();
markStatus(status);
SetMD5Info();
AddressableAssetSettings.BuildPlayerContent();//Addressable打包资源API
AssetDatabase.Refresh();
CopyBuildData();
AssetDatabase.Refresh();
SetMD5Info(false);
if (buildApp)
build();
Application.logMessageReceived -= onLogMessage;
if (isBuildSuccess)
{
string showMessage = string.Empty;
if (status == 0)
{
showMessage = buildApp ? "打包小包成功" : "打包小包资源完成";
}
else if (status == 1)
{
showMessage = buildApp ? "打包分包成功" : "打包分包资源完成";
}
else if (status == 2)
{
showMessage = buildApp ? "打包整包成功" : "打包整包资源完成";
}
if (EditorUtility.DisplayDialog(buildApp ? "打包完成" : "打包资源", showMessage, "确定"))
{
if (buildApp)
{
EditorUtility.RevealInFinder(BuildTools.OutPath);
BuildTools.OutPath = string.Empty;
}
}
}
else
{
if (EditorUtility.DisplayDialog("打包失败", "请检测报错信息", "确定"))
{
EditorUtility.RevealInFinder(BuildTools.OutPath);
BuildTools.OutPath = string.Empty;
}
}
}
详细的源码已上传,直接导入Unity(开发时使用的版是Unity2019.4)可以使用,注意要从PackageManager中下载相应Addressable系统相关组件,并创建好AddressableSetting文件