目录
Play Mode script播放模式脚本(编辑模式下如何运行)
Addressableassetsettings 可寻址资源数据设置
Use Existing Build(requires built groups)
前言
在Unity游戏开发中,资源管理是一个至关重要的环节。传统的资源管理方式,如直接引用、Resources文件夹管理和AssetBundle管理,都存在一定的局限性。直接引用虽然简单快捷,但无法实现动态加载;Resources文件夹下的所有文件都会被打包到安装包中,无论资源是否被使用,这会导致安装包体积过大,且无法进行热更新;而AssetBundle虽然支持热更新,但管理难度较大。
为了解决这些问题,Unity推出了Addressable Asset System(可寻址资产系统),也就是Addressables。Addressables是一个强大的资源管理系统,它基于AssetBundle系统,但提供了更为丰富的功能,如异步加载、依赖管理、内存管理等。通过使用Addressables,开发者可以更加高效地管理游戏资源,实现按需加载、优化内存使用,并支持热更新和增量更新,从而提升游戏性能和用户体验。
Addressables是什么
Addressables翻译过来是可寻址的意思,它是可寻址资源管理系统
是Unity从2018.2版本开始建议用于替代AssetBundle的高阶资源管理系统
在之后的Unity的新版本中,AssetBundle将渐渐被淘汰
但是
AssetBundle对于大家来说,还是必备的知识点,因为目前市面上还有很多的项目依旧在使用较老版本的Unity进行开发或者迭代,所以AssetBundle还是一种主流传统的资源管理方式
Addressables和AssetBundle的主要作用是一样的
1.管理资源
2.热更新
3.减小包的体积
Addressables和AssetBundle的区别
Addressables是基于AssetBundle架构做的高阶流程
Addressables的优点
1.自动化管理AB包打包、发布、加载
2.可以更方便的进行本地、远程资源的加载
3.系统会自动处理资源关联性
4.内存管理更方便
5.迭代更方便
导入Addressables包
Package Manager里搜索Addressables并安装
点击Windows/Asset Management/Addressables/Groups
他会在Asset文件夹下创建一个文件
Addressables资源设置
设置Addressables
方法一:选中资源,勾选Inspector窗囗中的Addressable
方法二:选中资源,拖入Addressables Groups窗口中
注意:
1.C#代码无法作为可寻址资源
2.Resources文件夹下资源如果变为寻址资源,会移入Resources moved文件夹中
原因:Resources文件夹下资源会最终打包出去,如果变为可寻址资源意味着想通过Addressables进行管理,那么它就没有必要通过Resources方式去加载和打包,所以会自动迁移,避免重复打包,浪费空间
右键选择资源时菜单内容
Move Addressables to Group:将该资源放入到现有的另一个组中
Move Addressables to New Gourp:使用与当前组相同设置创建一个新组,并将该资源放入该新组中
Rmove Addressables:移除资源,该资源会变为不可寻址资源
simplify Addressable Names:简化可寻址资源名,会删除名称中的路径和拓展,简化缩短名称Copy Address to clipboard:将地址复制到剪贴板
Change Address:改名
Create New Group:创建新组
资源信息(关键)
1.GroupName\Addressable Name:分组名\可寻址名(可重名,描述资源)
2.Path:路径(不可重复,资源定位)
3.Labels:标签(可重复、可用于区分资源种类,例如青铜装备、黄金装备)
创建分组相关
Packed Assets:打包资源分组
Blank(no schema):空白(无架构)
区别:Packed Assets默认自带默认打包加载相关设置信息,Blank没有相关信息需要自己关联
组对于我们来说意义重大,之后在资源打包时,一个组可以作为一个或多个AB包
选中某一组后右键
Remove Group(s):移除组,组中所有资源恢为不可寻址资源
simplify Addressable Names:简化可寻址名称,会删除名称中的路径和拓展,简化缩短名称
Set as Default:设置为默认组,当直接勾选资源中的Addressable时,会自动加入该组
Inspect Group setting:快速选中关联的组相关配置文件
Rename:重命名
Create New Group:创建新组
配置概述相关
Manage Profiles:管理配置文件
可以配置打包日标、本地远程的打包加载路径等等信息
Tools工具相关
Inspect System settings:检查系统设置
Check for content Update Restrictions:检查内容更新限制
window:打开Addressables相关窗口
Groups view:分组视图相关
show sprite and subobject Addressable:显示可寻址对象的精灵和子对象,一般想要看到图集资源内内容时可以勾选该选项
Group Hierarchy with Dashes:带破折号的组层次结构
Play Mode script播放模式脚本(编辑模式下如何运行)
确定在编辑器播放模式下运行游戏时,可寻址系统如何访问可寻址资源
Use Asset Database:使用资源数据库(最快的),一般在开发阶段使用,使用此选项时,您不必构建打包可寻址内容,它会直接使用文件夹中的资源在实际开发时,可以不使用这种模式,这种模式没有测试的意义
Use Existing Build:正儿八经的远端加载资源
一般在最终发布测试阶段使用使用现有构建(需要构建组),从早期内容版本创建的捆绑包加载资产在使用此选项之前,必须使用生成脚本(如默认生成脚本)打包资源远程内容必须托管在用于生成内容的配置文件的RemoteLoadPath上
Build(构建打包相关)
New Build:构建资源(相当于打包资源分组)
Update a Previour Build:更新以前的版本
clean Build:清空之前的构建资源
资源名注意事项
1.资源路径一定不允许相同(后缀不同,名字相同可以)
2.资源名我们可以随意修改
3.之后在加载资源时我们可以使用名字和标签作为双标识加载指定资源
资源分组
我们可以按规则将资源进行分组
比如:角色、装备、怪物、UI等等
指定资源加载
命名空间:using UnityEngine.AddressableAssets;
AssetReference 通用资源标识类 可以用来加载任意类型资源
AssetReferenceAtlasedSprite 图集资源标识类
AssetReferenceGame0bject 游戏对象资源标识类
AssetReferenceSprite 精灵图片资源标识类
AssetReferenceTexture 贴图资源标识类
AssetReferenceTexture2D
AssetReferenceTexture3D
AssetReferenceT<> 指定类型标识类
通过不同类型标识类对象的申明 我们可以在Inspector窗口中筛选关联的资源对象
加载资源
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
public class Lesson:MonoBehaviour
{
public AssetReference assetReference;
public AssetReferenceT<AudioClip> audioReference;
public AssetReferenceT<Material> materialRed;
void Start()
{
//注意:所有Addressables加载相关都使用异步加载
//需要引用命名空间:using UnityEngine.ResourceManagement.AsyncOperations;
//AsyncOperationHandle<GameObject> handle = assetReference.LoadAssetAsync<GameObject>();
//加载成功后使用
//1.通过事件函数传入的参数判断加载是否成功 并且创建
//2.通过资源标识类对象判断 并且创建
//通过异步加载返回值 对完成进行事件监听
//handle.Completed += TestFun;
assetReference.LoadAssetAsync<GameObject>().Completed += (handle) =>
{
//使用传入的参数(建议)
//判断是否加载成功
if (handle.Status == AsyncOperationStatus.Succeeded)
{
GameObject cube = Instantiate(handle.Result);
//一定资源加载过后 使用完后 再去释放
assetReference.ReleaseAsset();
materialRed.LoadAssetAsync().Completed += (obj) =>
{
cube.GetComponent<MeshRenderer>().material = obj.Result;
//这样会造成使用这个资源的对象 资源丢失
materialRed.ReleaseAsset();
//这个异步加载传入对象的资源
print(obj.Result);
//这个是 资源标识类的资源
print(materialRed.Asset);
};
}
//使用标识类创建
//if(assetReference.IsDone)
//{
// Instantiate(assetReference.Asset);
//}
};
audioReference.LoadAssetAsync().Completed += (handle) =>
{
//使用音效
};
}
}
加载场景
using UnityEngine;
using UnityEngine.AddressableAssets;
public class Lesson:MonoBehaviour
{
public AssetReference sceneReference;
void Start()
{
sceneReference.LoadSceneAsync().Completed += (handle) =>
{
//初始化场景的一些信息
print("场景加载结束");
};
}
}
释放资源
变量名.ReleaseAsset();
需要在资源异步加载完后再去释放
释放资源方法后不能再使用,如果要再使用需要重新加载
释放资源不会影响场最中被实例化出来的对象,但是会影响使用
直接实例化
//只适用于 想要实例化的 对象 才会直接使用该方法 一般都是GameObject预设体
gameobjcetReference.InstantiateAsync();
自定义标识类
自定义类 继承AssetReferenceT<Material>类 即可自定义一个指定类型的标识类
该功能主要用于Unity2020.1之前,因为之前的版本不能直接使用AssetReferenceT泛型字段
public class AssetReferenceAudio : AssetReferenceT<AudioClip>
{
public AssetReferenceAudio(string guid) : base(guid)
{
}
}
Label标签的作用
相同作用的不同资源
我们可以让他们的资源名相同
通过标签Label区分他们来用途
用于之后的动态加载
利用名字和标签可以单独动态加载某个资源
也可以利用它们共同决定加载哪个资源
[AssetReferenceUILabelRestriction("标签名")]//只能赋值带有标签名的变量对象
public AssetReference assetReference;
动态加载单个资源
通过资源名或标签名动态加载单个资源
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
public class Lesson:MonoBehaviour
{
AsyncOperationHandle<GameObject> handle;
void Start()
{
handle = Addressables.LoadAssetAsync<GameObject>("Cube");
handle.Completed += (handle) =>
{
//判断加载成功
if (handle.Status == AsyncOperationStatus.Succeeded)
Instantiate(handle.Result);
//一定要是 加载完成后 使用完毕后 再去释放
//不管任何资源 只要释放后 都会影响之前在使用该资源的对象
Addressables.Release(handle);
};
//Addressables.LoadAssetAsync<GameObject>("Red").Completed += (handle) =>
//{
// //判断加载成功
// if (handle.Status == AsyncOperationStatus.Succeeded)
// Instantiate(handle.Result);
//};
}
}
注意:
1.如果存在同名或同标签的同类型资源,我们无法确定加载的哪一个,它会自动加载找到的第一个满足条件的对象
2.如果存在同名或同标签的不同类型资源,我们可以根据泛型类型来决定加载哪一个
释放资源
需要指定要释放哪一个返回值
Addressables.Release(handle);
动态加载场景
//参数一:场景名
//参数二:加载模式 (叠加还是单独,叠加就是两个场景一起显示,单独就是只保留新加载的场景,正常情况为单独)
//参数三:场景加载是否激活,如果为false,加载完成后不会直接切换,需要自己使用返回值中的ActivateAsync方法
//参数四:场景加载的异步操作优先级
Addressables.LoadSceneAsync("SampleScene", UnityEngine.SceneManagement.LoadSceneMode.Single, false).Completed += (obj) =>
{
//比如说 手动激活场景
obj.Result.ActivateAsync().completed += (a) =>
{
//然后再去创建场景上的对象//然后再去隐藏 加载界面
//注意:场景资源也是可以释放的,并不会影响当前已经加载出来的场景,因为场景的本质只是配置文件
Addressables.Release(obj);
};
};
总结
1.根据名字或标签加载单个资源相对之前的指定加载资源更加灵活
主要通过Addressables类中的静态方法传入资源名或标签名进行加载
注意:
1 - 1.如果存在同名或同标签的同类型资源,我们无法确定加载的哪一个,它会自动加载找到的第一个满足条件的对象
1 - 2.如果存在同名或同标签的不同类型资源,我们可以根据泛型类型来决定加载哪一个
2.释放资源时需要传入之前记录的AsyncOperationHandle对象
注意:一定要保证资源使用完毕过后再释放资源
3.场景异步加载可以自己手动激活加载完成的场景
动态加载多个资源
根据资源名或标签名加载多个对象
public class Lesson:MonoBehaviour
{
void Start()
{
//加载资源
//参数一:资源名或标签名
//参数二:加载结束后的回调函数
//参数三:如果为true表示当资源加载失败时,会自动将已加载的资源和依赖都释放掉;如果为false,需要自己手动来管理释放
AsyncOperationHandle<IList<Object>> handle = Addressables.LoadAssetsAsync<Object>("Red", (obj) =>
{
//print(obj.name);
});
//如果要进行资源释放管理 那么我们需要使用这种方式 要方便一些
//因为我们得到了返回值对象 就可以释放资源了
handle.Completed += (obj) =>
{
foreach (var item in obj.Result)
{
//print(item.name);
}
//释放资源
Addressables.Release(obj);
};
//注意:我们还是可以通过泛型类型,来筛选资源类型
}
}
根据多种信息加载对象
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
public class Lesson:MonoBehaviour
{
void Start()
{
//参数一:想要加载资源的条件列表(资源名、Lable名)
//参数二:每个加载资源结束后会调用的函数,会把加载到的资源传入该函数中
//参数三:可寻址的合并模式,用于合并请求结果的选项。
//如果键(Cube,Red)映射到结果([1,2,3],[1,3,4]),数字代表不同的资源
//None:不发生合并,将使用第一组结果 结果为[1,2,3]
//UseFirst:应用第一组结果 结果为[1,2,3]
//Union:合并所有结果 结果为[1,2,3,4]
//Intersection:使用相交结果 结果为[1,3]
//参数四:如果为true表示当资源加载失败时,会自动将已加载的资源和依赖都释放掉
// 如果为false,需要自己手动来管理释放
List<string> strs = new List<string>() { "Cube", "HD" };
Addressables.LoadAssetsAsync<Object>(strs, (obj) => {
print(obj.name);
}, Addressables.MergeMode.Intersection);
//注意:我们还是可以通过泛型类型,来筛选资源类型
}
}
配置相关
Profile 概述窗口配置
概述配置用来干什么?
主要用于配置Addressable打包(构建)加载AB包时使用的一些变量
这些变量定义了
1.在哪里保存打包(构建)的AB包
2.运行时在哪里加载AB包
可以添加自定义变量,以便在打包加载时使用
之后在设置 组中打包和加载路径相关时,都是使用这里面的变量
打开Profiles窗口
方法一:Window > Asset Management > Addressables > Profiles
方法二:在AddressableAssetSettings中打开
方法三:在Addressables Groups窗口中打开
右侧是默认的一些路径相关
BuildTarget:构建目标,可以在这里设置是哪个平台,默认是你激活哪个平台就是哪个平台LocalBuildPath:本地构建路径,默认在项目的Library库文件夹中
LocalLoadPath:本地加载路径,在哪里加载本地已有的资源
RemoteBuildPath:远程构建路径
RemoteLoadPath:远程加载路径,在哪里下载远程内容和目录
注意:
1.一般情况下,不要去修改本地构建和加载路径默认值
2.当我们针对不同平台远程分发内容时,通过多个配置文件最方便。如果你想要最终的发布包包含所有内容,那么一个默认配置就够了
新建
Profile:概述文件
Variable:变量(所有概述文件通用)Build Load Path Variables:构建加载路径变量(所有概述文件通用)
Profiles变量语法
所有的变量类型都是string字符串类型
可以在其中填写一些固定的路径或值来决定路径
还可以使用两个语法指示符让原本的静态属性变成动态属性
[]:方括号,可以使用它包裹变量,在打包构建时会计算方括号包围的内容
比如
使用自己的变量[BuildTarget]
使用别的脚本中变量[UnityEditor.EditorUserBuildSettings.activeBuildTarget]
在打包构建时,会使用方括号内变量对应的字符串拼接到目录中
{ }:大括号,可以使用它包裹变量,在运行时会计算大括号包围的内容
比如
使用别的脚本中变量{ UnityEngine.AddressableAssets.Addressables.RuntimePath}
注意:方括号和大括号中使用的变量一定是静态变量或者属性。名称、类型、命名空间必须匹配
比如在运行时 UnityEditor编辑器命名空间下的内容是不能使用的
Addressableassetsettings 可寻址资源数据设置
配置文件有哪些
我们在导入Addressables包之后 创建的那些就是配置文件
AddressableAssetsData文件夹下的内容都是本质为ScriptableObject的数据配置文件
我们可以在工程中对Addressables相关内容进行设置
他们会影响我们的打包方式等等相关内容
AddressableAssetsData(可寻址资源数据)
-AssetGroups(资源组)
--当我们创建一个组就会多一些相关数据配置文件
- AssetGroupTemplates(资源组模板,主要是对资源组的一些默认设置)
--Packed Assets: 打包资源数据配置
- DataBuilders(数据生成器,这些内容决定了在不同模式下,资源打包和使用的方式)
--BuildScriptFastMode:构建脚本快速模式
--BuildScriptPackedMode: 构建脚本打包模式
--BuildScriptPackedPlayMode: 构建脚本打包播放模式
--BuildScriptVirtualMode: 构建脚本虚拟模式
- AddressableAssetSettings(可寻址资源设置)
-DefaultObject(默认对象)
AddressableAssetSettings 参数讲解
概述配置
Profile In Use:可以在这选择使用的是哪一套配置文件
Manage Profies:点击它会打开管理配置文件窗口
Diagnostics
Log Runtime Exceptions:记录运行时加载相关的异常
Catalog
目录相关设置,将资源的地址映射到其物理位置
Player Version Override:重写用于制定远程目录名称的时间戳如果不设置默认使用时间戳作为远程目录命名
Compress Local Catal0g:在压缩的AssetBundle文件中生成目录。可以压缩大小,但是会增加生成和加载的时间
Optimize Catalog Size:通过为内部ID创建查找表来减小目录的大小。会增加加载目录所需的时间
Build Remote Catalog
Build & Load Paths:在何处生成和加载远程目录。从列表中选择一个配置文件路径,如果要分别设置生成路径和加载路径,请选择<custom>。
仅在启用生成远程目录时可见。
Build Path:远程构建路径,在何处构建远程目录。通常,应该使用RemoteBuildPath配置文件变量。仅当将生成和加载路径设置为<custom>时显示。
Load Path:远程加载路径,用于访问远程目录的URL。通常,应该使用RemoteLoadPath配雪文件变量。仅当将生成和加载路径设置为<custom>时显示。
Path Preview:路径预览
Downloads
Custom Certificeate handle:用于自定义证书处理的类。该列表包含项目中扩展UnityEngine的所有类。网络。证书管理员。
Max Concurrent Web Requests:系统对超过此限制的任何请求进行队列处理
Catalog Download Timeout:等待目录文件下载的时间为多少秒。
Build
生成构建相关设置
lgnore Invalid/Unsupported Files in Build:忽略生成中无效不受支持的文件,如果启用Addressable生成脚本将排除无效或不受支持的文件,而不是中止生成
Unique Bundle lDs:唯一约束ID,是否为每个构建中的包生成一个唯-的名称
Contiguous Bundles:连续捆绑,生成更高效的捆绑包布局。如果有Addressables 1.12.1或更早版本生成的捆绑包,请禁用此选项以最小化捆绑包更改
Non-Recursive Dependency Calculation:非递归依赖计算,不使用递归计算依赖项
Shader Bundle Naming Prefix:着色器包命名前缀
MonoScript Bundle Naming Prefix:Mono脚本包命名前缀
Strip Unity Version From AssetBundles:从AssetBUndles中剥离Unity版本,决定是否从包头中删除版本信息
Disable Visible SuW Asset Representations:禁用可见子资源,如果您不直接使用子对象(Sprite、子网格等),则启用此选项可以提高构建时间
Build and Payer Mode Scripts
构建和编辑器模式脚本在编辑器中进入播放模式时,选择Addressable系统如何加载资产这些脚本处理默认的构建进程,并提供不同的方式在编辑模式下访问数据
我们可以在AddressableAssetData/DataBuilders文件夹中找到这些脚本
如果要为他们自定义脚本,那么对应的脚本需要时BuildScriptBase的子类,并且继承IDataBuilder
Use Asset Database:使用资源数据库
Simulate Groups:模拟组
Use Existing Build:使用现有版本
Default Build Scripts:默认生成脚本
Asset Group Templates
资源组模板
Packed Asset:打包资源
定义可用于创建新组的模板列表
创建新模板时,必须先将其添加到此列表中,然后才能使用它
Addressables包包含一个模板
其中包含默认构建脚本使用的模式。
可以在AddressableAssetData/AssetGroupTemplates文件夹中找到该模板。
Initalization Objects
初始化对象列表配置初始化对象
可以在Project右键Create>Addressables>Initialization(初始化)>Cache Initialization Setting(缓存初始化设置)
Packed Assets 打包资源数据配置
打包资产配置的作用
Packed Assets 翻译过来的意思是 打包资产(资源)
它的作用是确定如何处理组中的资源
比如:你可以指定生成AB包的位置和包压缩相关的等等设置
打包资产参数讲解
Content Packing & Loading
Build & Load Paths:配置文件路径对,定义可寻址构建系统在哪个路径为此组创建内容,以及可寻址系统在运行时在何处加载这些内容
Build Path:打包路径LocalBuildPath-本地路径RemoteBuildPath-远程路径
Load Path:加载路径LocalLoadPath-本地路径RemoteLoadPath-远程路径
Path Preview:路径预览
Asset Bundle Compression
Asset Bundle Compression:AB包的压缩方式,默认为LZ4,它通常是最有效的选项,建议使用
Uncompressed:不压缩,包较大,不推荐
LZ4:压缩,相对LZMA大一点点,建议使用,用什么解压什么,内存占用低
LZMA:压缩最小,解压慢,用一个资源要解压所有
Asset Bundle CRC
Use Asset Bundle Cache:使用AB包缓存,是否缓存远程分发的包
Asset Bundle CRC:是否在加载前验证AB包的完整性
Disabled:永远不检查完整性
Enabled Including Cached:检查完整性,包括缓存也检查
Enabled, Excluding Cached:检查完整性,但是不检查缓存的包
Include Addresses in Catalog:是否将地址字符串包括在目录中。如果不使用地址字符串在组中加载资产,则可以通过不包括它们来减小目录的大小。
Include GUlDs in Catalog:是否在目录中包含GUID字符串。您必须包含guid字符串才能使用资产参考。如果不使用AssetReferences或GUID字符串在组中加载资产,则可以通过不包括它们来减小目录的大小。
Include Labels in Catalog:是否在目录中包含标签字符串。如果不使用标签在组中加载资产,则可以通过不包括这些资产来缩小目录的大小.
创建自定义的配置
在Project窗口右键或者点击+号
Create(创建)——>Addressables(可寻址)——>Group Templates(组模板)——>Blank Group Template(空白组模板)
资源打包(发布)
资源打包是指什么?
资源打包是指,将可寻址资源打包到AssetBundle资源绑定包中
以前我们学习AssetBundle时需要我们自己写代码或者通过工具进行打包
现在Addressable将这个过程自动化了
可寻址资源中资源一般以组为单位进行打包
最终的AB包数量,都是基于资源组决定的
打包好的AB包,我们可以自定义如何使用它们
1.发布游戏时所有AB包作为原始资源一起打包出去(这样做的目的仅仅是单机游戏为了减小游戏包体)
2.将AB包放到远端服务器,发布游戏时只打包必备资源(这样做的目的是不仅可以减小包体,还可以进行热更新)
资源打包的模式
之前在讲解Packed Assets配置相关时
有一个打包模式的字段Bundle Mode
有三种打包的方式
Pack Together:创建包含组中所有资产的单个包
Pack Separately:为组中的每个类型的资源创建一个包。如精灵图片中的精灵图片被包装在一起
Pack Together by Label:为共享相同标签组合的资产创建一个包
一般情况下,我们都使用第一种模式,按组来进行打包,所以我们在整理资源时要尽量合理
资源打包的注意事项
1.场景资源始终有自己单独的包
当一个可寻址包中有场景资源和普通资源时,场景资源和其它资源始终会被分开打包,也就是说,该组会生成两个包,一个场景的,一个其它资源的
2.资源依赖的注意事项
如果资源a和资源b都使用了资源c,但是资源a和b是可寻址资源但不在一个组中,而c不是可寻址资源。那么这时资源c分别会被打进a和b的包,相当于这时的c就被重复利用了,浪费了空间。较好的解决方案是:
将c也作为可寻址资源, a - A包 b - B包 c - C包。这时c不会被打进A、B包,A、B包只会依赖于C,而Addressable会自动帮助我们处理依赖问题。
注意:在使用图集时,尤为要注意这个问题,当不同包中内容使用同一个图集中图片时,建议大家将图集也作为可寻址资源,并且有专门的一个图集包,让其它使用图集中图片的对象会对该图集包产生依赖。
3.合理安排可寻址资源分组
同类型,同作用放一起
比如:角色组、怪物组、武器组、衣服组、登录UI组、装备UI组、音效组、可变贴图组、图集组等等
4.关于包的数量(分组的数量)
要根据实际情况来对资源进行布局
包(分组)过多、包(分组)过大都不太好
要根据自己的实际情况进行组的安排
编辑器中资源加载的几种方式
Use Asset Database(fastest)
使用资源数据库(最快的)
不用打AB包,直接本地加载资源,主要用于开发功能时
Simulate Groups(advanced)
模拟组(后期)
不用打AB包
通过ResourceManager从资产数据库加载资产,就像通过AB包加载一样
通过引入时间延迟,模拟远程AB包的下载速度和本地AB包加载速度
在开发阶段可以使用这个模式来进行资源加载
Use Existing Build(requires built groups)
正儿八经的加载AB包资源
必须打AB包后使用
会从AB包中加载资源
本地资源发布
本地发布
所有组的加载和发布都选择本地路径
LocalBuildPath - 打包路径
LocalLoadPath - 加载路径
注意:使用默认设置,当发布应用程序时,会自动帮我们将AB包放入StreamingAssets文件夹中
如果修改了路径,我们需要自己将内容放入StreamingAssets文件夹中
不建议大家修改 默认的本地构建和加载路径 ,因为如果你修改了 就需要自己手动的去把AB包相关内容移动到StreamingAssets文件夹中
资源更新
资源更新指的是什么?
当项目正式发布后,对于远程加载的资源
我们可以通过改变资源服务器上的AB包内容来达到更新游戏的目的
Addressables会自动帮助我们判断哪些资源更新了,并加载最新的内容
内容更新限制参数回顾
在组设置中有一个内容更新限制的设置
Content Update Restriction
整包更新
组设置为 Can Change Post Release
整包更新指,某一个分组的资源发生变化后
我们需要将其整体进行打包
这种方式比较适用于大范围资源更新时使用
坏处是,玩家需要下载的内容较大,比较耗时耗流量
局部更新
组设置为 Cannot Change Post Release
局部更新指,当组中有资源发生变化时
我们可以单为发生变化的内容生成AB包
之后使用该资源时,Addressables会自动加载最新的内容
它相对整包更新来说,更节约时间和流量
其它相关内容
根据资源定位信息加载资源
加载可寻址资源的方式
1.通过标识类进行加载(指定资源加载)
2.通过资源名或标签名加载单个资源(动态加载)
Addressables.LoadAssetAsync<GameObject>("Cube")
3.通过资源名或标签名或两者组合加载多个资源(动态加载)
Addressables.LoadAssetsAsync<GameObject>(new List<string>() { "Cube", "SD" }, (obj) => { }, Addressables.MergeMode.Intersection);
加载资源时Addressables帮助我们做了哪些事情?
1.查找指定键的资源位置
2.收集依赖项列表
3.下载所需的所有远程AB包
4.将AB包加载到内存中
5.设置Result资源对象的值
6.更新Status状态变量参数并且调用完成事件Completed
如果加载成功Status状态为成功,并且可以从Result中得到内容
如果加载失败除了Status状态为失败外
如果我们启用了 Log Runtime Exceptions选项 会在Console窗口打印信息
根据名字或者标签获取 资源定位信息 加载资源
AsyncOperationHandle<IList<IResourceLocation>> handle = Addressables.LoadResourceLocationsAsync("Cube", typeof(GameObject));
handle.Completed += (obj) =>
{
if(obj.Status == AsyncOperationStatus.Succeeded)
{
foreach (var item in obj.Result)
{
//我们可以利用定位信息 再去加载资源
//print(item.PrimaryKey);
Addressables.LoadAssetAsync<GameObject>(item).Completed += (obj) =>
{
Instantiate(obj.Result);
};
}
}
else
{
Addressables.Release(handle);
}
};
根据名字标签组合信息获取 资源定位信息 加载资源
//参数一:资源名和标签名的组合
//参数二:合并模式
//参数三:资源类型
AsyncOperationHandle<IList<IResourceLocation>> handle2 = Addressables.LoadResourceLocationsAsync(new List<string>() { "Cube", "Sphere", "SD" }, Addressables.MergeMode.Union, typeof(Object));
handle2.Completed += (obj) => {
if(obj.Status == AsyncOperationStatus.Succeeded)
{
//资源定位信息加载成功
foreach (var item in obj.Result)
{
//使用定位信息来加载资源
//我们可以利用定位信息 再去加载资源
print("******");
print(item.PrimaryKey);
print(item.InternalId);
print(item.ResourceType.Name);
Addressables.LoadAssetAsync<Object>(item).Completed += (obj) =>
{
//Instantiate(obj.Result);
};
}
}
else
{
Addressables.Release(handle);
}
};
异步加载的使用方式
动态异步加载的使用方式
AsyncOperationHandle<GameObject> handle = Addressables.LoadAssetAsync<GameObject>("Cube");
//通过事件监听的方式 结束时使用资源
handle.Completed += (obj) =>
{
if (handle.Status == AsyncOperationStatus.Succeeded)
{
print("事件创建对象");
Instantiate(obj.Result);
}
};
3种使用异步加载资源的方式
1.事件监听(目前学习过的)
2.协同程序
3.异步函数(async和await )
通过协程使用异步加载
StartCoroutine(LoadAsset());
通过异步函数async和await加载
//注意:WebGL平台不支持异步函数语法
async void Load()
{
handle = Addressables.LoadAssetAsync<GameObject>("Cube");
AsyncOperationHandle<GameObject> handle2 = Addressables.LoadAssetAsync<GameObject>("Sphere");
//单任务等待
//await handle.Task;
//多任务等待
await Task.WhenAll(handle.Task, handle2.Task);
print("异步函数的形式加载的资源");
Instantiate(handle.Result);
Instantiate(handle2.Result);
}
Async Operation Handle
获取加载进度
IEnumerator LoadAsset()
{
AsyncOperationHandle<GameObject> handle = Addressables.LoadAssetAsync<GameObject>("Cube");
//if (!handle.IsDone)
// yield return handle;
//注意:如果该资源相关的AB包 已经加载过了 那么 只会打印0
while (!handle.IsDone)
{
DownloadStatus info = handle.GetDownloadStatus();
//进度
print(info.Percent);
//字节加载进度 代表 AB包 加载了多少
//当前下载了多少内容 / 总体有多少内容 单位是字节数
print(info.DownloadedBytes + "/" + info.TotalBytes);
yield return 0;
}
if (handle.Status == AsyncOperationStatus.Succeeded)
{
Instantiate(handle.Result);
}
else
Addressables.Release(handle);
}
无类型句柄转换
AsyncOperationHandle<Texture2D> handle = Addressables.LoadAssetAsync<Texture2D>("Cube");
//AsyncOperationHandle temp = handle;
//把无类型句柄 转换为 有类型的泛型对象
//handle = temp.Convert<Texture2D>();
强制同步加载资源
//如果执行了WaitForCompletion 那么会卡主主线程 一定要当资源加载结束后
//才会继续往下执行
print("1");
handle.WaitForCompletion();
print("2");
print(handle.Result.name);
注意:
Unity2020.1版本或者之前,执行该句代码不仅会等待该资源
他会等待所有没有加载完成的异步加载加载完后才会继续往下执行
Unity2020.2版本或以上版本,在加载已经下载的资源时性能影响会好一些
所以,总体来说不建议大家使用这种方式加载资源
自定义更新目录和下载AB包
目录的作用
目录文件的本质是Json文件和一个Hash文件
其中记录的主要内容有
Json文件中记录的是:
1.加载AB包、图集、资源、场景、实例化对象所用的脚本(会通过反射去加载他们来使用)
2.AB包中所有资源类型对应的类(会通过反射去加载他们来使用)
3.AB包对应路径
4.资源的path名
等等
Hash文件中记录的是:
目录文件对应hash码(每一个文件都有一个唯一码,用来判断文件是否变化)
更新时本地的文件hash码会和远端目录的hash码进行对比
如果发现不一样就会更新目录文件
当我们使用远端发布内容时,在资源服务器也会有一个目录文件
Addressables会在运行时自动管理目录
如果远端目录发生变化了(他会通过hash文件里面存储的数据判断是否是新目录)
它会自动下载新版本并将其加载到内存中
手动更新目录
//1.如果要手动更新目录 建议在设置中关闭自动更新
//2.自动检查所有目录是否有更新,并更新目录API
Addressables.UpdateCatalogs().Completed += (obj) =>
{
Addressables.Release(obj);
};
//3.获取目录列表,再更新目录
//参数 bool 就是加载结束后 会不会自动释放异步加载的句柄
Addressables.CheckForCatalogUpdates(true).Completed += (obj) =>
{
//如果列表里面的内容大于0 证明有可以更新的目录
if(obj.Result.Count > 0)
{
//根据目录列表更新目录
Addressables.UpdateCatalogs(obj.Result, true).Completed += (handle) =>
{
//如果更新完毕 记得释放资源
//Addressables.Release(handle);
//Addressables.Release(obj);
};
}
};
预加载包
建议通过协程来加载
StartCoroutine(LoadAsset());
1.首先获取下载包的大小2.预加载
3.加载进度
IEnumerator LoadAsset()
{
//1.首先获取下载包的大小
//可以传资源名、标签名、或者两者的组合
AsyncOperationHandle<long> handleSize = Addressables.GetDownloadSizeAsync(new List<string>() { "Cube", "Sphere", "SD" });
yield return handleSize;
//2.预加载
if(handleSize.Result > 0)
{
//这样就可以异步加载 所有依赖的AB包相关内容了
AsyncOperationHandle handle = Addressables.DownloadDependenciesAsync(new List<string>() { "Cube", "Sphere", "SD" }, Addressables.MergeMode.Union);
while(!handle.IsDone)
{
//3.加载进度
DownloadStatus info = handle.GetDownloadStatus();
print(info.Percent);
print(info.DownloadedBytes + "/" + info.TotalBytes);
yield return 0;
}
Addressables.Release(handle);
}
}
引用计数规则
什么是引用计数规则?
当我们通过加载使用可寻址资源时
Addressables会在内部帮助我们进行引用计数
使用资源时,引用计数 + 1
释放资源时,引用计数 - 1
当可寻址资源的引用为0时,就可以卸载它了
为了避免内存泄露(不需要使用的内容残留在内存中)
我们要保证加载资源和卸载资源是配对使用的
注意:释放的资源不一定立即从内存中卸载
在卸载资源所属的AB包之前,不会释放资源使用的内存
(比如自己所在的AB包 被别人使用时,这时AB包不会被卸载,所以自己还在内存中)
我们可以使用Resources.UnloadUnusedAssets卸载资源(建议在切换场景时调用)
AB包也有自己的引用计数(Addressables把它也视为可寻址资源)
从AB包中加载资源时,引用计数 + 1
从AB包中卸载资源时,引用计数 - 1
当AB包引用计数为0时,意味着不再使用了,这时会从内存中卸载
总结:Addressables内部会通过引用计数帮助我们管理内存
我们只需要保证 加载和卸载资源配对使用即可
构建布局报告
构建布局报告有什么作用?
构建布局报告提供了有关可寻址资源的构建打包的详细信息和统计信息
包括
1.AB包的描述
2.每个资源和AB包的大小
3.解析作为依赖项隐式包含在AB包中的不可寻址资源
4.AB包的依赖关系
我们可以通过查看报告文件获取这些信息
如何查看构建布局报告?
1.启用调试构建布局功能
Edit > Preferences > Addressables
启用Debug Build Layout
2.只要我们构建打包可寻址资源后
就可以在Library / com.unity.addressables / 文件夹中找到buildlayout.txt文件
构建布局报告的内容
内容中主要包含:
1.摘要信息(包括AB包数量、大小等等)
2.每组相关信息(哪些资源,几个包,包大小等等)
3.依赖相关信息
常见问题
用多包策略还是大包策略?
1.AB包太多(太小)的问题
- 每个包都会有内存开销,如果加载过多的包可能会带来更多的内存消耗
- 并发下载的问题,如果包小而多,以为着下载包时可能需要更多的时间
- 目录文件会因为过多的包而变大,因为它要记录更多的包信息
- 复用资源的可能性更大,比如多个包使用同样一个资源,但是该资源不是寻址资源,那么在每个包中都会有该资源
2.AB包太少(太大)的问题
-
过大的包如果下载失败,下次会重新下载,因为使用UnityWebRequest下载包时不会恢复失败的下载,比如100MB的包,下了50MB,玩家中断下载了,下次又得重新下
-
能单独加载,但是不能单独卸载,更大的包意味着包中有更多资源,比如加载了1个大包中100个资源,但是现在用完了99个,还剩一个再用,即使99都卸载了,但是由于引用计数这个大包也不会卸载,就会造成内存的浪费
所以没有最好的策略,只有根据自己的项目需求合理安排分组打包
要根据资源的使用情况来合理设置资源分组,在分组时权衡好各方面的问题
哪种压缩方式更好?
AB包的压缩方式:不压缩、LZ4、LZMA
一般情况LZ4用于本地资源,LZMA用于远端资源
主要原因是LZMA的压缩内容更小,更节约下载时间和流量
注意:压缩不会影响加载内存的大小,只会影响包体大小,下载时间等
但是也要根据实际情况
比如:
1.不压缩:包体并不大的单机游戏,使用不压缩最好,没有包体大小的压力,加载也是最快的,因为不用解压
2.LZ4:它是基于块的压缩,所以提供了加载文件的能力,加载资源时不用全加载AB包,只加载使用的内容,相对LZMA来说更节约内存
3.LZMA:不建议用它在本地内容中,因为它虽然包最小,但是加载最慢,用它只是为了节约下载时间和极限压缩包体大小
综合来说,也没有最优的方式,还是要根据实际情况来选择
减小目录文件大小
当我们想要极限压缩包体大小时,可能希望优化目录文件的大小
1.压缩本地目录
AddressableAssetSettings > Catalog > Compress Local Catalog
2.禁用内置场景和资源
默认Addressables提供了从Resources等内置资源文件夹中加载资源以及加载内置场景
如果你不通过Addressables加载他们,可以禁用,这样目录文件就不会包含其中信息
但是我们就只能使用老方法加载同名
个人建议取消,因为一般我们不会通过Addressables去加载非寻址资源
注意事项
1.关于AB包最大的限制,老版本不支持大于4G的包,虽然新版本中已经没有这个限制
但是为了兼容性,还是建议大小控制在4G以下
2.活用可寻址资源上的Groups View中的两个功能
Show Sprite and Subobject Addresses:当窗口中内容特别多时,禁用它可以提升窗口加载的性能
Group Hierarchy with Dashes:启用带破折号的层级结构,可以让我们在内容特别多时以层级结构查看分组信息