1)关于 Addressable 的疑问
2)Addressable 如何进行热更新
3)如何设置 SceneView 相机的 Shader 变量
4)Activity 默认为 SingleTask 的原因
5)关于 Resources.UnloadUnusedAssets 调用时间过长的问题
UWA 问答社区:answer.uwa4d.com
UWA QQ 群 2:793972859(原群已满员)
Q:最近几天在看 Addressable,虽然未完全看懂,但感觉这相比 AssetBundle 好用得多,也考虑得很周全。主要是在参考官方文档 Addressables 1.6.2(https://docs.unity3d.com/Packages/com.unity.addressables@1.6/manual/AddressableAssetsDevelopmentCycle.html#building-for-content-updates)时,有一些疑问:
1. 相对 AssetBundle,它的打包颗粒度如何控制的?比如说,AssetBundle 可以自己控制多个资源打到一个 AssetBundle 中。
2. 有没有脚本可以自动设置 Addressable 名称以及 Label?毕竟稍具规模的项目,资源量也不小,手工设置过去也不科学。
3. 关于 Check for Content Update Restrictions,官方文档上有这样一句话:Note:This command will do nothing if all your changes are confined to non-static groups,也就是说这个检查对非静态 Group 不起作用。问题是,在哪里定义这个 Group?它是 non-static 还是 static 呢?
4. 关于 Unique Bundle IDs,官方文档上也是 Turn on“Unique Bundle IDs” within the AddressableAssetSettings Inspector,但我没有找到。
A1:第一个问题:Addressable 最终也是打包成 AssetBundle,打包的存储目录由每个 Group 的 BundledAssetGroupSchema 设定的 Build Path 而定。对于同一个 Group 的资源是否打包到一个 Bundle 里面,也是由 BundledAssetGroupSchema 的 Budle Mode 决定的。如果是 Pack Together,则会打包到一个 Bundle 里,Pack Separately 则会将每个 Entry 打成一个 Bundle。关于粒度问题跟使用 Asset Bundle 是类似的,Addressable 会自动管理 Bundle 包的依赖关系,如果依赖的资源没在其它的 Bundle 包里面,则会被自动打进自身的 Bundle 里面。
第二个问题可以参考图片里面的做法:
感谢 Xuan@UWA 问答社区提供了回答A2:问题 3:
Cannot Change Post Release == static
Can Change Post Release == non-static
问题 4:
关于更多 Addressable 的技术话题,可查阅 UWA 学堂相关课程:
《Addressable 系统解析及实践经验》(https://edu.uwa4d.com/course-intro/0/153)
《Addressable 进阶实用方法》(https://edu.uwa4d.com/course-intro/0/151)
感谢黄程@UWA问答社区提供了回答
Q:请教一下 Addressable 如何进行热更新。在之前的项目中,所有的资源打包成 AssetBundle 放在 StreamingAssets 里,有更新的 AssetBundle 会下载到可写目录,游戏加载时优先加载可写目录的 AssetBundle。
那么在 Addressable 中如何实现这个功能呢?能否设置优先读取目录?
A:不知道题主说的设置优先读取目录是指什么意思,暂时按照自己理解的意思来说一下。Addressable 的资源更新功能需要在 Editor 和 Runtime 两方面处理。在 Editor 的部分,需要在 Setting 里面勾选 Build Remote Catalog,第一次使用 New Build 后会产生 Catalog 文件(.hash 和.json 文件),这两个 Catalog 文件和打出来的 Bundle 文件需要放在服务器上。之后再进行资源更新需要 Update a Previous Build 来进行打包。这时候 Editor 会覆盖掉原来的 Catalog 文件(.hash 和.json 文件),同时会有新的 Bundle 文件生成,将新生成的文件替换服务器原来的文件即可。
在 Runtime 的部分,需要调用 CheckForCatalogUpdates 和 UpdateCatalogs 来更新资源。当资源更新到本地后,再调用 Addressable.LoadAssetAsync 则会自动从本地目录进行加载了,不知道这里的本地目录是不是就是题主所说的优先目录。
具体代码可以参考:
https://answer.uwa4d.com/question/5e3b7fe686a39548919837e1
感谢 Xuan@UWA 问答社区提供了回答
Q:做一个效果需要设置一个 WorldToView 矩阵,挂了一个脚本在主相机上设置了全局的变量,但是在 SceneView 下仍然使用的是主相机的 WorldToView 矩阵,有什么办法在 SceneView 渲染时设置 SceneView 的 Camera 的属性呢?
A:如果是想把 GameView 的镜头同步到 SceneView,可以尝试以下方法:
InitializeOnLoad]
public static class SyncSceneViewMenuItem {
private const string MenuName = "Tools/同步GameView镜头到SceneView";
private static bool enabled;
static SyncSceneViewMenuItem() {
SyncSceneViewMenuItem.enabled = EditorPrefs.GetBool(MenuName, false);
this.Apply();
}
[MenuItem(MenuName)]
public static void ToggleSync() {
SyncSceneViewMenuItem.enabled = !SyncSceneViewMenuItem.enabled;
EditorPrefs.SetBool(MenuName, SyncSceneViewMenuItem.enabled);
this.Apply();
}
private void Apply() {
Menu.SetChecked(MenuName, SyncSceneViewMenuItem.enabled);
SyncSceneViewCamera.enabled= SyncSceneViewMenuItem.enabled;
}
}
public class SyncSceneViewCamera : MonoBehaviour {
#if UNITY_EDITOR
public static bool enabled;
private Camera camera;
private void Update() {
if (SyncSceneViewCamera.enabled) {
if (this.camera == null){
this.camera = Camera.main;
}
UnityEditor.SceneView.lastActiveSceneView?.AlignViewToObject(this.camera.transform);
}
}
#endif
}
感谢黄程@UWA问答社区提供了回答
Q:Unity 导出的 Gradle 项目,默认会把 UnityPlayerActivity 配置成 SingleTask。但是实际测试中,有不少第三方 SDK 会冲突 SingleTask 的配置。
Unity 使用这个默认配置是否有什么设计原因?如果修改到 Standard 会有一些什么陷阱?
A1:主要是为了防止游戏被启动导致二次崩溃,但是只依赖 LaunchMode 为 SingleTask 是不够的,确实会有第三方 SDK 强制要求按他们的来,比如腾讯渠道就要求 SingleTop:
游戏的 Activity 的 LaunchMode 需要设置为 SingleTop,设置为 SingleTop 以后在平台拉起游戏的场景下,有可能会出现游戏 Activity 被拉起两个的情况,所以游戏 Activity 的 onCreate 里面需要检测当前 Activity 是否是重复的游戏 Activity,如果是则要结束当前游戏 Activity。
所以两手准备比较万全,能用 SingleTask 的用 SingleTask,不行的就用静态变量来做下判断。
感谢 littlesome@UWA 问答社区提供了回答A2:这个对于降低宕机数的统计有一定的贡献,很多时候你的游戏没有设置成 SingleTask 切换后台再次回到游戏,可能就会被重新拉起来,这就加大了你的宕机统计,对宕机率影响挺大的。
感谢李星@UWA问答社区提供了回答
Q:游戏中有上百个皮肤、坐骑、翅膀等外观展示 Prefab,这些 Prefab 的加载方式均是通过 AssetBundle 加载,当创建出 Prefab 后,会调用 AssetBundle.Unload(false)来卸载掉 AssetBundle,但 GameObject 和相应的资源还会在内存中保存着。
现在遇到的问题是:在手机上全部点开这些外观模型后,第一次调用 Resources.UnloadUnusedAssets 时,会产生一个可能长达 20 秒的卡顿。如果那些 GameObject 均已 Destroy 掉,且内存中这些相应的资源全部变成垃圾待回收了,调用 Resources.UnloadUnusedAssets 会卡这么长时间还可以理解,但现在问题是这些资源都还引用着,不会被这个 API 清理吗?有确认过不是因为其它的资源引起的,甚至 AssetBundle.Unload(false)这个接口都试过屏蔽掉,相同操作下同样会产生卡顿。
请问导致这个超长卡顿可能的原因有哪些?Unity 版本为 2017.4.30f1。
最新进展:测试加载单个 Prefab 资源后(调用完 AssetBundle.LoadAsset),不调用任何其它释放的接口,直接调用 c,在调试日志中相关显示为:
Unloading 27 unused Assets to reduce memory usage. Loaded Objects now:6777.
Total: 20.391873 ms(FindLiveObjects: 1.497475 ms CreateObjectMapping:0.186901 ms MarkObjects: 15.905487 ms DeleteObjects: 2.801631 ms)
现在最大的问题就是:这个 27 是怎么来的?它是哪些资源被标记为垃圾然后删除了?
A:20 秒的情况下,是否有对应的日志?看看 4 个步骤里哪个步骤耗时久,还有 Objects 是什么数量级。
你的测试代码可以改成这样试试:
yield return Resources.UnloadUnusedAssets();
yield return Resources.UnloadUnusedAssets(); // 确保这一条的输出是 Unload 0 个
var info0 = CreateIDToInfoMap(Resources.FindObjectsOfTypeAll<Object>()); // 创建一个字典,Key是InstanceID,Value是对象描述
ab.LoadAsset();
yield return Resources.UnloadUnusedAssets();
var info1 = CreateIDToInfoMap(Resources.FindObjectsOfTypeAll<Object>());
// 对比info0和info1 把描述打印出来,就可以知道少了哪些Object
感谢 littlesome@UWA 问答社区提供了回答
今天的分享就到这里。当然,生有涯而知无涯。在漫漫的开发周期中,您看到的这些问题也许都只是冰山一角,我们早已在 UWA 问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入,也许你的方法恰能解别人的燃眉之急;而他山之 “石”,也能攻你之 “玉”。