1.18 从0开始学习Unity游戏开发--资源加载

news/2024/11/28 3:50:00/

在上一篇文章中,我们大约是开始接触到资源加载的事情了,场景资源则是一个比较特殊的资源,我们只要添加到Build Settings里面,那么我们就可以通过API直接加载。

但是其他类型的资源怎么办呢?比如我们制作一个网络游戏,接收到后台的返回数据要求给人物装上一把枪,但是我们也不可能把所有枪都作为成员数据赋值上去,肯定是希望用到哪个就加载哪个,所以这个时候就需要支持通过类似名字或者路径的方式加载资源。

因此本章我们将会讲解Unity中资源到底是什么,如何进行加载的,以及几种加载方式适用的场景。

引用形式的加载

之前的篇章里面我们要使用某个资源,都是在代码中加入成员变量,借助Unity的序列化能力显示在编辑器面板上,然后我们通过拖拽等方式,将场景内的物体或者Project窗口里面的资源赋值到对应的变量上去,这种加载资源的方法是通过引用来进行加载的。

之前分析实际场景文件的时候我们有分析过,我们的GameObject也好,组件类也罢,在序列化的时候都会生成一个唯一的id,这个id就是用来存储引用关系,在场景启动的时候就会加载场景里面已有的GameObject,然后根据文件内的id数据来索引所有的引用关系。

但是有一个问题就是,我们之前其实有使用Prefab赋值给场景内物体的组件变量上,那这个引用关系又是如何呢?

我们不妨以文本方式打开Demo.unity文件,找到我们赋值bullet的FireController这个物体的FireController组件:

可以看到这里我们又见到了fileID,但是很显然这个fileID我们是无法在场景文件内搜索到第二个使用的地方,毕竟这里赋值的Prefab是存在于Project窗口里面而非场景里面,那么我们注意到后面还有一个guid,擅长全局搜索的朋友可能会尝试把整个Assets目录都搜索一遍,很快就能找到其实这个guid存在于Bullet.prefab.meta文件中:

打开看内容就是这样:

可以看到这个meta文件里面记录的guid跟我们场景里面引用Prefab的变量上存的guid一致,并且如果你再打开Bullet.prefab就能找到fileID是对应的Prefab上的那个组件。

这个时候其实也就理解了,对于非场景内直接可以引用的资源,场景外的资源文件,都会带有一个.meta文件,这个文件内存储了这个资源的guid,而引用场景外资源的方法就是通过这个guid进行检索。

可以仔细观察,Assets文件夹下所有文件都会带上一个.meta文件,也就是说其实所有文件包括文件夹,在Unity中都是可以被引用的。

这也顺带解答了如果我们的游戏工程要上传git或者svn,p4这样的版本控制系统,.meta文件是一定需要上传的,如果没有上传,其他人打开工程时,Unity会自动生成新的.meta文件,那个时候你的所有通过guid引用的资源都将失效。

当然.meta文件不仅仅存了guid这个信息,还有很多其他的信息,这些我们会在后面每种资源类型进阶教程讲到的时候再详细说明。

在理解清楚Unity针对场景内和场景外资源的引用如何处理之后,其实也就能理解为什么我们的东西打包也能拿到,因为Unity打包是从Build Settings里面加入的场景查找所有资源的引用,所有用到的资源都会打包到最终游戏包中。

Resources文件夹

当我们没有在任何代码成员里面直接引用组件或者GameObject或者Prefab资源的时候,我们如何动态的加载一个Prefab或者什么的资源呢?Unity提供了一个特殊的潜规则,一个名字叫做Resources的文件夹,在你的Assets目录下,任何一个叫Resources的文件夹都可以,可以同时存在多个在不同目录下的Resources文件夹,这些文件夹会被Unity统一识别到,然后你就可以用Resources.Load 这个API加载里面的资源:

Unity - Scripting API: Resources.Load​docs.unity3d.com/ScriptReference/Resources.Load.html

有点类似我们加载新的场景,我们可以通过相对路径来加载Resources文件夹下的内容,注意路径一定需要使用/而不是\来作为路径的分隔符,

例如:

  1. Assets/Test1/Resources/a.prefab,那么你加载的时候用的是a,不带文件后缀名
  2. Assets/Test1/Resources/BB/a.prefab,那么你加载的时候用的是BB/a
  3. Assets/Test2/Resources/BB/a.prefab,你会和2冲突,Unity会告诉你所有Resources文件夹里面的文件不能有一样的相对路径

OK,那我们来试一下,之前我们的Bullet是通过直接赋值给成员变量,我们现在把Prefab资源放到Resources文件夹里面,我们目前没有,所以我们新建一个Resources文件夹,还是Project窗口右键Create->Folder,然后把我们的Bullet prefab文件直接拖进去,相当于剪切。

注意如果你是直接在windows资源管理器里面来做这个操作的话,应该把对应的.meta文件也一起剪切走,之前也说了这个.meta文件带有这个资源的guid信息,如果你不剪切走,那么就会导致原有的引用关系找不到了(虽然现在是直接用Resources来加载,可能不会有影响)。在Unity的Project窗口中看不到.meta文件,因为你剪切资源的时候会自动帮你处理。

然后我们需要修改一下之前创建子弹的FireController的代码:

using UnityEngine;
using Object = UnityEngine.Object;public class FireController : MonoBehaviour
{private bool isMouseDown = false;private float lastFireTime = 0f;private Vector3 fireDirection;private AddVelocity bullet;public string bulletResourcesPath;public float fireInterval = 0.1f;public Transform fireBeginPosition;private void Start(){bullet = Resources.Load<AddVelocity>(bulletResourcesPath);}void Update(){if (Input.GetButton("Fire1")){if (!isMouseDown){isMouseDown = true;lastFireTime = Time.time;Fire();}else if (Time.time - lastFireTime > fireInterval){lastFireTime = Time.time;Fire();}}else{isMouseDown = false;}}void Fire(){// 在这里实现每次触发的逻辑// 创建新的子弹,每次都是从模板bullet复制一个出来AddVelocity newBullet = Object.Instantiate(bullet);newBullet.transform.position = fireBeginPosition.position;newBullet.SetDirection(fireDirection);}public void SetDirection(Vector3 direction){fireDirection = direction;}
}

我们的修改主要是将之前的public成员bullet换成了private成员,我们不再需要序列化这个成员,取而代之的是用了一个string成员来让编辑器上可以填入资源的路径。

然后我们在Start方法里面通过Resources.Load来加载一次这个资源,后续就和之前的使用方法一样。Resources.Load方法是带有泛型,或者用C++的话来讲就是模板类型,填入<>里面的类型将会用类似GetComponent的方法从加载上来的GameObject中获取,当然你想加载上来就是GameObject也完全可以在<>里面填GameObject这个类型。

OK,我们直接在编辑器里面填入我们子弹的资源路径Bullet:

可以再跑起来看看,我们也一样可以创建子弹,获得和之前一样的效果。

这样的加载方法有个缺点:放在Resources文件夹下的资源不会管有没有引用,在打包游戏的时候会全部打包进去,毕竟Unity也不知道你到底要用哪个,所以这个办法比较适合小体量的资源。

AssetDatabase

编辑器模式下专用的加载资源方法,主要用于编辑器下的扩展和功能操作,跟正常游戏的运行逻辑是独立开的,由于涉及到了编辑器的游戏运行时的概念,本章不会讲解,会留到专门讲解编辑器扩展开发的时候再讲。

AssetBundle

Unity提供的正统的游戏打包发布后的正规资源加载方式,但是很多概念理解起来相当麻烦,本大章系列文章还只是初步入门的阶段,暂时不讲这部分,会留到后面详细讲解游戏打包时的资源管理再讲。

思考题

  1. Resources.Load会同步加载资源,阻塞代码执行,显然官方的API有异步版本,如果改成异步应该怎么写?

下一章

上面我们讲解了两个资源加载的方式,一个是通过引用自动加载,一个是通过路径来动态加载。虽然Resources.Load比较挫,但是现阶段足够我们初步学习Unity的资源加载。

下一章我们将会简单入门一下编辑器扩展,Unity编辑器并非一尘不变,而是可以通过我们的代码随心所欲的调整,这也是现代商业游戏引擎提供的编辑器扩展能力,通过扩展能力,我们能更好的让引擎服务于我们的定制化需求。当然了,在其中也会学习到编辑器所跑的逻辑和游戏跑的逻辑这两个概念的区别。


http://www.ppmy.cn/news/48820.html

相关文章

JavaScript加解密

加密是一种将信息转换为其他形式的过程&#xff0c;使得只有授权人才能访问它。解密是将已经加密的信息转换回原始形式的过程。在互联网上&#xff0c;加密和解密通常是通过密码实现的&#xff0c;这些密码称为密钥。 JavaScript是一种非常流行的编程语言&#xff0c;用于在网…

2023年淮阴工学院五年一贯制专转本大学语文考试大纲

2023年淮阴工学院五年一贯制专转本大学语文考试大纲 一、考试目标 淮阴工学院五年一贯制高职专转本入学考试秘书学专业《大学语文》考试是我校为招收五年一贯制高职专转本学生设置的具有选拔性质的考试科目。其目的是科学、公平、有效地测试考生是否具备攻读秘书学本科学位所…

C/C++每日一练(20230419)

目录 1. 插入区间 &#x1f31f;&#x1f31f;&#x1f31f; 2. 单词拆分 &#x1f31f;&#x1f31f; 3. 不同路径 &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日…

原型设计工具即时设计、Axure、Figma、Sketch,哪个更好用?

在线网页原型图设计软件的使用与桌面端相比具备优势&#xff0c;因为在线网页原型图设计软件的使用全程不需要安装&#xff0c;而且在线网页原型图设计软件也没有任何地点上的限制&#xff0c;更主要的是在线网页原型图设计软件在操作系统上也没有限制&#xff0c;不论是现在使…

运维需要懂产品和运营吗

研发团队对运维团队的诉求&#xff0c;以及运维呈现的价值已经发生了变化&#xff0c;我们更加需要能够帮助团队建设出高效运维体系的角色&#xff0c;而不是能够被动响应更多问题的角色。 打造一个运维体系&#xff0c;我们完全可以把它类比为一个产品业务体系。公司的组织架…

字节跳动正式开源分布式训练调度框架 Primus

动手点关注 干货不迷路 项目地址&#xff1a;https://github.com/bytedance/primus 随着机器学习的发展&#xff0c;模型及训练模型所需的数据量越来越大&#xff0c;也都趋向于通过分布式训练实现。而算法工程师通常需要对这些分布式框架涉及到的底层文件存储和调度系统有较深…

4.4 使用分组聚合进行组内计算

4.4 使用分组聚合进行组内计算 4.4.1 使用groupby方法拆分数据groupby方法的参数及其说明&#xff1a;groupby对象常用的描述性统计方法如下&#xff1a; 4.4.2 使用agg方法聚合数据agg函数和aggregate函数的参数说明1、使用agg求出当前数据对应的统计量2、使用agg分别求字段的…

InnoSetup 安装程序设置环境变量

InnoSetup 通过写入注册表值实现安装程序设置系统全局环境变量 环境变量以字符串值存储在注册表中&#xff0c;因此可以使用 [Registry] 区段操作它们。系统范围的环境变量位于: "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment"…