Unity 之 Addressable可寻址系统 -- 代码加载介绍 -- 进阶(一)

news2024/11/25 22:59:16

Unity 之 可寻址系统 -- 代码加载介绍 -- 进阶(一)

  • 一,可寻址系统代码加载
    • 1.1 回调形式
    • 1.2 异步等待
    • 1.3 面板赋值
    • 1.4 同步加载
  • 二,可寻址系统分标签加载
    • 2.1 场景搭建
    • 2.2 代码示例
    • 2.3 效果展示
  • 三,代码加载可寻址的解释

概述:本片文章为大家介绍可寻址系统使用代码动态加载物体的多种形式。

一,可寻址系统代码加载

准备工作,创建几个预制体分别为:Cube,Capsule,Sphere,并将预制体设置为可寻址系统的资源,然后将Cube的地址修改为Cube,如下图:

1.1 回调形式

using UnityEngine;
// 引用命名空间
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

public class LoadManager : MonoBehaviour
{
    void Start()
    {
        // 回调形式
        LoadGameObject();
        LoadGameObjectCallBack();
        InstantiateGameObject();
    }
  
    # region 回调形式
    
    /// <summary>
    /// 加载物体 
    /// 逻辑简单且不需复用,直接使用Lambda表达式的形式
    /// </summary>
    void LoadGameObject()
    {
        // 参数:"Cube" 为可寻址系统的地址
        Addressables.LoadAssetAsync<GameObject>("Cube").Completed += (obj) =>
        {
            GameObject go = obj.Result;

            Instantiate(go, Vector3.zero, Quaternion.identity);
        };
    }
    
    /// <summary>
    /// 加载物体
    /// </summary>
    void LoadGameObjectCallBack()
    {
        Addressables.LoadAssetAsync<GameObject>("Assets/Prefab/Sphere.prefab").Completed += LoadCallBack;
    }

    /// <summary>
    /// 加载物体的回调函数
    /// </summary>
    void LoadCallBack(AsyncOperationHandle<GameObject> handle)
    {
        GameObject go = handle.Result;
        Instantiate(go, Vector3.right * 2, Quaternion.identity);
    }
    
    /// <summary>
    /// 加载并实例化物体
    /// </summary>
    void InstantiateGameObject()
    {
        Addressables.InstantiateAsync("Assets/Prefab/Capsule.prefab").Completed += (obj) =>
        {
            // 已经实例化后的物体
            GameObject go = obj.Result;
            go.transform.position = Vector3.left * 2;
        };
    }
    
    #endregion   
}

运行结果如下:
成功加载三个预制


1.2 异步等待

若你不习惯回调的形式,可寻址系统也给了我们加载异步等待的形式直接去加载物体,示例代码如下:

using UnityEngine;
// 引用命名空间
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

public class LoadManager : MonoBehaviour
{
    void Start()
    {
        // 异步形式
        AsyncLoadCube();
        AsyncInstantiateCube();
    }
    
    #region 异步形式
    
    private async void AsyncLoadCube()
    {
        // 虽然这里使用了Task,但并没有使用多线程
        GameObject prefabObj = await Addressables.LoadAssetAsync<GameObject>("Assets/Prefabs/Cube.prefab").Task;
        // 实例化
        GameObject cubeObj = Instantiate(prefabObj);
        cubeObj.transform.position = Vector3.zero;
    }
    
    private async void AsyncInstantiateCube()
    {
         // 直接使用InstantiateAsync方法
         GameObject cubeObj = await Addressables.InstantiateAsync("Assets/Prefabs/Cube.prefab").Task;
         cubeObj.transform.position = Vector3.right * 2;
    }
    #endregion
}

运行结果如下:


1.3 面板赋值

在代码中声明AssetReference类型的变量,将上面准备好的Cube拖拽上来进行赋值。若此时拖拽上来的没有未加入可寻址系统的资源,他会自动变成一个可选寻址资源。

准备场景如下:

示例代码如下:

using UnityEngine;
// 引用命名空间
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

public class LoadManager : MonoBehaviour
{
    // 可寻址资源的弱引用
    public AssetReference cubeRef;
    
    void Start()
    {
        // 面板引用
        RefLoadCube();
    }  

    #region 面板形式

    void RefLoadCube()
    {
        cubeRef.LoadAssetAsync<GameObject>().Completed += (obj) =>
        {
            // 加载完成结果
            GameObject cubePrefab = obj.Result;
            // 实例化
            GameObject cubeObj = Instantiate(cubePrefab);
            // 修改位置
            cubeObj.transform.position = Vector3.zero;
        };
    }
    #endregion
}

运行结果如下:

PS:
如果我们声明的不是AssetReference类型,而是我们常用的GameObject类型,那么场景就直接依赖了Cube预制体,打包时Cube预制体就会被打到场景中。现在这里用的是AssetReference 类型,它是一个弱引用,场景并不会真的依赖Cube预制体。


1.4 同步加载

在1.17.4版本之后的可寻址系统,通过在 AsyncOperationHandleWaitForCompletion 方法来实现同步加载。 WaitForCompletion 的作用是会让系统阻拦代码执行,直到资源加载完成。

同步加载GameObject的基本用法,代码如下:

using UnityEngine;
using UnityEngine.AddressableAssets; // 引用命名空间
using UnityEngine.ResourceManagement.AsyncOperations;

public class LoadManager : MonoBehaviour
{
    // Asset的弱引用
    public AssetReference cubeRef;
    
    void Start()
    {
        Addressables.InitializeAsync();
        // 同步加载
        InstantiatePrefab();
    }

    void InstantiatePrefab()
    {
        // 实例化加载到的游戏物体
        Instantiate(LoadPrefab(), transform);
    }
    
    // 强制同步加载GameObject的基本用法
    GameObject LoadPrefab()
    {
        var op = Addressables.LoadAssetAsync<GameObject>("Cube");
        GameObject go = op.WaitForCompletion();
        return go;
    }
}

多数情况下同步加载的性能和异步加载基本一致,但偶尔也会出现更快或更慢的情况。

WaitForCompletion 运算必须在引擎的任务队列中依次完成,如果小型运算的前面有一些大型运算,则只有前面的完成,系统才能完成队列后方的运算。这种情况下会出现加载慢的现象。


二,可寻址系统分标签加载

2.1 场景搭建

创建一个RawImage和一个Button摆放位置如下图:

找两张图片将其放入到工程中,并将其设置为可寻址资源,然后把地址都修改为Logo,最后分别为其创建标签Black,White 设置如下:

PS:这里创建的地址Logo和标签Black,White是下面代码中要用到的,若有变化,则代码中的值需要对应修改。


2.2 代码示例

创建如下代码并将其挂载到上面创建的RawImage物体上,并初始化如下:

下面代码,分别使用了AssetLabelReference面板赋值的形式和寻址+标签两种形式进行了分标签加载代码的示例。根据实际情况决定使用哪一种形式即可。

using System.Collections.Generic;
using UnityEngine;
// 引用命名空间
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.UI;

public class LoadByLabelManager : MonoBehaviour
{
    // 可寻址系统标签的引用
    public AssetLabelReference prefabsLabel;

    public Button loadWhiteLogoBtn;

    private RawImage _rawImage;

    void Start()
    {
        _rawImage = GetComponent<RawImage>();
        
        loadWhiteLogoBtn.onClick.AddListener(() =>
        {
            LoadTextureByKeyLabel("Logo", "Black");
        });
        
        LoadGameObjectByLabel();
    }

    /// <summary>
    /// 根据标签加载
    /// </summary>
    void LoadGameObjectByLabel()
    {
        Addressables.LoadAssetsAsync<Texture2D>(prefabsLabel, (texture) =>
        {
            // 每加载完成一个,就回调一次。 标签下有几个就会执行几次
            Debug.Log("加载完成一个资源: " + texture.name);
            _rawImage.texture = texture;
            _rawImage.SetNativeSize();
        });
    }

    /// <summary>
    /// 根据地址和标签加载
    /// </summary>
    /// <param name="key">可寻址资源地址</param>
    /// <param name="label">资源标签</param>
    void LoadTextureByKeyLabel(string key, string label)
    {
        Addressables.LoadAssetsAsync<Texture2D>(new List<string> {key, label},
            null, Addressables.MergeMode.Intersection).Completed += TextureLoaded;        
    }

    void TextureLoaded(AsyncOperationHandle<IList<Texture2D>> texture)
    {
        _rawImage.texture = texture.Result[0];
        _rawImage.SetNativeSize();
    }
}

根据寻址和标签加载的代码中Addressables.MergeMode:用于合并请求结果 --> 可以理解为,可寻址系统依次从key和label加载两组结果,合并结果有四个枚举值:若查询结果分别为:A,B,C 和 B,C,D

  1. None和UseFirst 都会取第一组结果 --> A,B,C
  2. Union 取两组结果的并集 --> A,B,C,D
  3. Intersection 取两组结果的交集 --> B,C

2.3 效果展示

运行效果:


三,代码加载可寻址的解释

代码加载逻辑基本上这几个,写法也都在上面。逻辑和使用AB包加载逻辑基本一致,只是写法略有不同,所以稍微有经验的同学,应该可以很快上手。只需要熟悉熟悉写法即可。

上面代码加载用的都是key加载的,我们也可以使用它的绝对路径加载,比如上面的Cube,可以这样写:

Addressables.LoadAssetAsync<GameObject>("Cube")

Addressables.LoadAssetAsync<GameObject>("Assets/Prefab/Cube.prefab")

使用key的方式加载的好处在于不管我们之后如何修改路径或者是修改预制体名称,都不会影响到上面代码的加载逻辑,这就是可寻址。只要key不修改就不用修改代码,若AddressablesGroups中存在相同的key,则代码会返回列表中靠上的资源。

若资源不在本地而在服务器上,可寻址系统会帮助我们先下载等待下载完成在执行后面的加载逻辑,所以代码上也不需要有任何的修改。


除了我们常用的资源类型(预制,贴图,音频,配置文件)这些可以使用可寻址代码加载,场景也可以使用可寻址代码加载代码Addressables.LoadSceneAsync("Assets/Scenes/Game.unity");

在后面介绍资源更新的文章中会介绍一个检测更新的脚本,用以游戏开始前下载我们的需要的资源,这样就不会再等到使用时现下载,也就解决了资源过大或者网络稳定的情况下,现下载卡顿的问题。


TODO:上一篇 --> Unity 之 Addressable可寻址系统 – 可寻址系统使用介绍 – 入门(三)

TODO:下一篇 --> Unity 之 Addressable可寻址系统 – 资源加载和释放 – 进阶(二)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/167508.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Cadence OrCAD: 跨页符和电源符号命名优先级的一个小问题

Cadence OrCAD: 跨页符和电源符号命名优先级的一个小问题 遇到的问题 最近项目中&#xff0c;有个电源需要做负载端的反馈&#xff0c;类似下图的signal1和signal1N&#xff0c;反馈使用类似伪差分线&#xff0c;把电压信号和负载端的GND都连到DC-DC控制器。DC-DC对应的反馈引…

字节跳动青训营--前端day1

文章目录前言一、 前端1 前端的技术栈2. 前端的边界3. 前端的关注点二、 HTML1. HTML常用标签及语义化2. HTML 语法3. 谁在使用我们写的HTML前言 仅以此文章记录学习历程 一、 前端 解决GUI人机交互问题 1 前端的技术栈 2. 前端的边界 nodejs–服务器端应用 electron… --客…

【数据结构】6.1 图的基本概念和术语

文章目录前言6.1 图的定义和术语前言 图是一种比线性表和树更为复杂的数据结构。 在线性结构中&#xff0c;结点之间的关系属于一个对一个&#xff1b;数据元素之间有着线性关系&#xff0c;每个数据元素只有一个直接前趋和一个直接后继&#xff0c; 在树形结构中&#xff0c;…

算法设计与分析课程

算法的由来 算法的定义 算法的定义&#xff1a;给定计算问题&#xff0c;算法是一系列良定义的计算步骤&#xff0c;逐一执行计算步骤可得到预期的输出。 良定义&#xff1a;定义明确无歧义 计算步骤&#xff1a;计算机可以实现的指令 有了良定义的计算步骤&#xff0c;计算机就…

Java基础篇01-运算符的使用

01| Java中的数据类型 ) 1. 数值型&#xff1a; 序号类型空间占用说明最小值最大值默认值优缺点对比举例1byte8位有符号整数-128(-2^7)127 (2^7-1)0byte 类型用在大型数组中节约空间&#xff0c;主要代替整数&#xff0c;因为 byte 变量占用的空间只有 int 类型的四分之一by…

6、Denoising Diffusion Probabilistic Models(扩散模型)

简介 主页&#xff1a;https://hojonathanho.github.io/diffusion/ 扩散模型 &#xff08;diffusion models&#xff09;是深度生成模型中新的SOTA。 扩散模型在图片生成任务中超越了原SOTA&#xff1a;GAN&#xff0c;并且在诸多应用领域都有出色的表现&#xff0c;如计算机…

【docker概念和实践 1】 基本概念和组成原理

一、说明 初学Docker就一个字&#xff1a;乱&#xff01;这是因为Docker是一个庞大体系&#xff0c;初学时不了解全貌&#xff0c;处于“盲人摸象”状态&#xff0c;因不能通晓要领&#xff0c;学了一点&#xff0c;过后就忘了。而了解Docker全貌并非易事&#xff0c;官方文档也…

前端学习记录-Javascript

pink JS基础语法JavaScript核心教程阮一峰JS基础 JS基础语法 初识JS JS三种书写位置&#xff1a;行内、内嵌、引入式。单行注释 ctrl/ 多行注释 shift alt a输入输出语句 声明变量 var age;变量命名规范&#xff1a;字母、数字、下划线、美元符号组成&#xff0c;区分大小写…

质量体系搭建

测试团队的发展历程 初期阶段 特点&#xff1a;提供“保姆式”服务&#xff0c;以发现BUG为主要任务。 工作主要&#xff1a;以功能测试、兼容行测试为主的手工测试&#xff0c;每天进行大量的、重复性的工作&#xff0c;即便如此依然会有遗漏。刚起步的测试团队基本处于这个阶…

LeetCode分类刷题----哈希表篇

哈希表哈希表1.有效的字母异位词242.有效的字母异位词383.赎金信49.字母异位词分组438.找到字符串中所有字母异位词2.两个数组的交集349.两个数组的交集350.两个数组的交集||3.快乐数202202.快乐数4.两数之和1.两数之和5.四数相加454.四数相加||6.三数之和15.三数之和7.四数之和…

ARM 实时时钟 RTC

一、何为实时时钟 (1) real time clock&#xff0c;真实时间&#xff0c;就是所谓的xx年x月x日x时x分x秒星期x. (2) RTC是 SoC 中一个内部外设&#xff0c;RTC 有自己独立的晶振提供 RTC 时钟源&#xff08;32.768KHz&#xff09;&#xff0c;内部有一些寄存器用来记录时间&am…

微信小程序登陆,后端接口实现 - springboot

登录流程 1、通过调用wx.login获取登录凭证&#xff08;code&#xff09; uni-app通过调用uni.login 2、前端将code提交给服务器&#xff0c;springboot访问 auth.code2Session&#xff0c;使用 code 换取 openid、unionid、session_key 等信息。 3、完成登录操作&#xff0…

数学计算-C语言实现

任务描述 计算如下公式的值: 其中π=3.1415926 本关知识 C语言常用数学函数及其用法 在使用C语言数学函数时候,应该在该源文件中使用以下命令行包含库文件: #include <math.h> 或 #include "math.h" 本题中用到的C语言数学函数如下: abs函数: 求整型…

Pytorch模型自定义数据集训练流程

文章目录Pytorch模型自定义数据集训练流程1、任务描述2、导入各种需要用到的包3、分割数据集4、将数据转成pytorch标准的DataLoader输入格式5、导入预训练模型&#xff0c;并修改分类层6、开始模型训练7、利用训好的模型做预测Pytorch模型自定义数据集训练流程 我们以kaggle竞…

交互与前端20 APIFunc.DataBase监控

说明 APIFunc.DataBase的第一版有一个监控一直在做agg,造成数据库的无谓消耗,所以一定得修补。在修补的同时,做了一些主要的修改: 1 【自增ID】给Mongo的In和Out增加了数据的自动编号和随机数生成。2 【使用缓存】通过Redis缓存,极大的的减轻了Mongo(主库)的负担这样,使得…

Kruskal重构树学习笔记(C++)

Kruskal重构树学习笔记 提示&#xff1a; 学习Kruskal重构树之前建议先了解一下Kruskal算法&#xff0c;虽然不了解这个影响不会很大 但一定要了解一下并查集的算法 接下来如果想要应用Kruskal重构树&#xff0c;一定要了解一下LCA算法 什么是Kruskal重构树 这里先简单说…

exec函数族详解

文章目录exec介绍exec族execl函数execlp函数execv函数exec介绍 通过命令查看帮助&#xff1a;man 3 exec exec 函数族的作用是根据指定的文件名找到可执行文件&#xff0c;并用它来取代调用进程的内容&#xff0c;换句话说&#xff0c;就是在调用进程内部执行一个可执行文件。…

基于多线程版本的定时器

定时器 1)咱们前面学习过的阻塞队列&#xff0c;相比于普通的队列线程安全&#xff0c;相比于普通的队列起到一个更好的阻塞效果 2)虽然使用阻塞队列&#xff0c;可以达到销峰填谷这样的一个效果&#xff0c;但是峰值中有大量的数据涌入到队列中&#xff0c;如果后续的服务器消…

教程:Flutter 和 Rust混合编程,使用flutter_rust_bridge自动生成ffi代码

实践环境&#xff1a;Arch Linuxflutter_rust_bridge官方文档Flutter环境配置教程 | Rust环境配置教程记录使用flutter_rust_bridge遇到的一些坑。假设已经我们配置了Fluuter与Rust环境现在直接使用flutter_rust_bridge模板创建自己的项目运行&#xff1a;git clone https://gi…

W13Scan 扫描器挖掘漏洞实践

一、背景 这段时间总想捣鼓扫描器&#xff0c;发现自己的一些想法很多前辈已经做了东西&#xff0c;让我有点小沮丧同时也有点小兴奋&#xff0c;说明思路是对的&#xff0c;我准备站在巨人的肩膀去二次开发&#xff0c;加入一些自己的想法&#xff0c;从freebuf中看到W13Scan…