关于Addressable的疑问

关于Addressable的疑问

1)关于Addressable的疑问
2)Addressable如何进行热更新
3)如何设置SceneView相机的Shader变量
4)Activity默认为SingleTask的原因
5)关于Resources.UnloadUnusedAssets调用时间过长的问题


这是第195篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题,建议阅读时间10分钟,认真读完必有收获。

UWA 问答社区:answer.uwa4d.com
UWA QQ群2:793972859(原群已满员)

Addressable

Q:最近几天在看Addressable,虽然未完全看懂,但感觉这相比AssetBundle好用得多,也考虑得很周全。主要是在参考官方文档Addressables 1.6.2时,有一些疑问:

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系统解析及实践经验》
《Addressable进阶实用方法》

感谢黄程@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5e649911438f7d0db495c724


Addressable

Q:请教一下Adressable如何进行热更新。在之前的项目中,所有的资源打包成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问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5e64aa06438f7d0db495c727


Camera

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问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5d52a35f80d96a06d776dc55


Android

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问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5e5e1c29438f7d0db495c6b8


Resource

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问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5e64e0e6438f7d0db495c72b


今天的分享就到这里。当然,生有涯而知无涯。在漫漫的开发周期中,您看到的这些问题也许都只是冰山一角,我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入,也许你的方法恰能解别人的燃眉之急;而他山之“石”,也能攻你之“玉”。

官网:www.uwa4d.com
官方技术博客:blog.uwa4d.com
官方问答社区:answer.uwa4d.com
UWA学堂:edu.uwa4d.com
官方技术QQ群:793972859(原群已满员)