当前位置:网站首页>Unity资源顺序加载的一个方法
Unity资源顺序加载的一个方法
2022-07-06 10:25:00 【ttod】
unity提供了基于UnityWebRequest类进行加载资源的一些列做法,但是如果简单的对所要加载的内容同时加载会导致瞬间卡顿,同时也不能控制资源加载的顺序。因为有时候我们需要控制资源的加载顺序,所以做了一个脚本希望能解决这个问题。并不是说这个脚本一定好,因为可能有很多比这个好得多的方案,或者unity本身提供了更好的功能,但是我不知道利用而已。不管怎么说,线上代码吧。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;
public class LoadFromStreamingAssets : MonoBehaviour
{
class ObjHolder
{
public readonly string fullPath;
public readonly object obj;
public ObjHolder(string fullPath, object obj)
{
this.fullPath = fullPath;
this.obj = obj;
}
}
public class LoadInfo
{
public readonly string fullPath;
public LoadInfo(string relativePath)
{
fullPath = Application.streamingAssetsPath + "/" + relativePath;
}
}
public class ObjLoadInfo : LoadInfo
{
public readonly string objName;
public ObjLoadInfo(string relativePath, string objName) : base(relativePath)
{
this.objName = objName;
}
}
public class GameObjectLoadInfo : ObjLoadInfo
{
public UnityAction<GameObject> onLoaded;
public GameObjectLoadInfo(string relativePath, string objName, UnityAction<GameObject> onLoaded) : base(relativePath, objName)
{
this.onLoaded = onLoaded;
}
}
public class CubemapLoadInfo : ObjLoadInfo
{
public UnityAction<Cubemap> onLoaded;
public CubemapLoadInfo(string relativePath, string objName, UnityAction<Cubemap> onLoaded) : base(relativePath, objName)
{
this.onLoaded = onLoaded;
}
}
public class TextLoadInfo : LoadInfo
{
public UnityAction<string> onLoaded;
public TextLoadInfo(string relativePath, UnityAction<string> onLoaded) : base(relativePath)
{
this.onLoaded = onLoaded;
}
}
public class Texture2DLoadInfo : LoadInfo
{
public UnityAction<Texture2D> onLoaded;
public Texture2DLoadInfo(string relativePath, UnityAction<Texture2D> onLoaded) : base(relativePath)
{
this.onLoaded = onLoaded;
}
}
public class AudioLoadInfo : LoadInfo
{
public UnityAction<AudioClip> onLoaded;
public readonly AudioType audioType;
public AudioLoadInfo(string relativePath, AudioType audioType, UnityAction<AudioClip> onLoaded) : base(relativePath)
{
this.audioType = audioType;
this.onLoaded = onLoaded;
}
}
static public LoadFromStreamingAssets instance;
void Awake()
{
instance = this;
}
Queue<LoadInfo> loadInfoQueue = new Queue<LoadInfo>();
public void AddLoadInfo(LoadInfo loadInfo)
{
loadInfoQueue.Enqueue(loadInfo);
}
List<ObjHolder> objHolderList = new List<ObjHolder>();
void AddObjHolder(ObjHolder objHolder)
{
objHolderList.Add(objHolder);
}
ObjHolder GetObjHolder(string fullPath)
{
foreach (ObjHolder h in objHolderList)
{
if (h.fullPath == fullPath) return h;
}
return null;
}
void Start()
{
StartWaitLoad();
}
void StartWaitLoad()
{
StartCoroutine(WaitLoad());
}
IEnumerator WaitLoad()
{
yield return new WaitUntil(() => loadInfoQueue.Count > 0);
LoadByQueue();
}
void LoadByQueue()
{
LoadInfo info = loadInfoQueue.Dequeue();
if (info == null)
{
ContinueLoad();
}
else
{
ObjHolder objHolder = GetObjHolder(info.fullPath);
if (info is GameObjectLoadInfo)
{
GameObjectLoadInfo loadInfo = info as GameObjectLoadInfo;
if (loadInfo != null)
{
if (objHolder != null)
{
GameObject o = objHolder.obj as GameObject;
loadInfo.onLoaded(o);
ContinueLoad();
}
else
{
loadInfo.onLoaded += (GameObject o) => { if (o) { AddObjHolder(new ObjHolder(loadInfo.fullPath, o)); } };
loadInfo.onLoaded += delegate { ContinueLoad(); };
LoadGameObject(loadInfo);
}
}
else
{
ContinueLoad();
}
}
else if(info is CubemapLoadInfo)
{
CubemapLoadInfo loadInfo = info as CubemapLoadInfo;
if (loadInfo != null)
{
if (objHolder != null)
{
Cubemap c = objHolder.obj as Cubemap;
loadInfo.onLoaded(c);
ContinueLoad();
}
else
{
loadInfo.onLoaded += (Cubemap c) => { if (c) { AddObjHolder(new ObjHolder(loadInfo.fullPath, c)); } };
loadInfo.onLoaded += delegate { ContinueLoad(); };
LoadCubemap(loadInfo);
}
}
else
{
ContinueLoad();
}
}
else if (info is TextLoadInfo)
{
TextLoadInfo loadInfo = info as TextLoadInfo;
if (loadInfo != null)
{
if (objHolder != null)
{
string s = objHolder.obj as string;
loadInfo.onLoaded(s);
ContinueLoad();
}
else
{
loadInfo.onLoaded += (string s) => { if (s!=null) { AddObjHolder(new ObjHolder(loadInfo.fullPath, s)); } };
loadInfo.onLoaded += delegate { ContinueLoad(); };
LoadText(loadInfo);
}
}
else
{
ContinueLoad();
}
}
else if (info is Texture2DLoadInfo)
{
Texture2DLoadInfo loadInfo = info as Texture2DLoadInfo;
if (loadInfo != null)
{
if (objHolder != null)
{
Texture2D t = objHolder.obj as Texture2D;
loadInfo.onLoaded(t);
ContinueLoad();
}
else
{
loadInfo.onLoaded += (Texture2D t) => { if (t != null) { AddObjHolder(new ObjHolder(loadInfo.fullPath, t)); } };
loadInfo.onLoaded += delegate { ContinueLoad(); };
LoadTexture2D(loadInfo);
}
}
else
{
ContinueLoad();
}
}
else if (info is AudioLoadInfo)
{
AudioLoadInfo loadInfo = info as AudioLoadInfo;
if (loadInfo != null)
{
if (objHolder != null)
{
AudioClip a = objHolder.obj as AudioClip;
loadInfo.onLoaded(a);
ContinueLoad();
}
else
{
loadInfo.onLoaded += (AudioClip a) => { if (a != null) { AddObjHolder(new ObjHolder(loadInfo.fullPath, a)); } };
loadInfo.onLoaded += delegate { ContinueLoad(); };
LoadAudioClip(loadInfo);
}
}
else
{
ContinueLoad();
}
}
}
}
void ContinueLoad()
{
if (loadInfoQueue.Count == 0)
{
StartWaitLoad();
}
else
{
LoadByQueue();
}
}
void LoadGameObject(GameObjectLoadInfo LoadInfo)
{
StartCoroutine(GetAssetBundle(LoadInfo.fullPath, (AssetBundle ab) => { AssetbundleToGameObject(ab, LoadInfo.objName, LoadInfo.onLoaded); }));
}
void AssetbundleToGameObject(AssetBundle ab, string objName, UnityAction<GameObject> onLoaded)
{
if (ab)
{
GameObject obj = ab.LoadAsset<GameObject>(objName);
onLoaded?.Invoke(obj);
}
else
{
onLoaded?.Invoke(null);
}
}
void LoadCubemap(CubemapLoadInfo loadInfo)
{
StartCoroutine(GetAssetBundle(loadInfo.fullPath, (AssetBundle ab) => { AssetbundleToCubemap(ab, loadInfo.objName, loadInfo.onLoaded); }));
}
void AssetbundleToCubemap(AssetBundle ab, string objName, UnityAction<Cubemap> onLoaded)
{
if (ab)
{
Cubemap cubemap = ab.LoadAsset<Cubemap>(objName);
onLoaded?.Invoke(cubemap);
}
else
{
onLoaded?.Invoke(null);
}
}
IEnumerator GetAssetBundle(string path, UnityAction<AssetBundle> onGetAssetBundle)
{
using (UnityWebRequest webRequest = UnityWebRequestAssetBundle.GetAssetBundle(path))
{
yield return webRequest.SendWebRequest();
if (webRequest.result == UnityWebRequest.Result.Success)
{
AssetBundle ab = (webRequest.downloadHandler as DownloadHandlerAssetBundle).assetBundle;
onGetAssetBundle?.Invoke(ab);
ab.Unload(false);
}
else
{
Debug.Log("error = " + webRequest.error + "\n Load Path = " + path);
onGetAssetBundle?.Invoke(null);
}
}
}
void LoadText(TextLoadInfo loadInfo)
{
StartCoroutine(GetText(loadInfo.fullPath, loadInfo.onLoaded));
}
IEnumerator GetText(string path, UnityAction<string> onGetJson)
{
using (UnityWebRequest webRequest = UnityWebRequest.Get(path))
{
yield return webRequest.SendWebRequest();
if (webRequest.result == UnityWebRequest.Result.Success)
{
string json = webRequest.downloadHandler.text;
onGetJson?.Invoke(json);
}
else
{
Debug.Log("error = " + webRequest.error + "\n Load Path = " + path);
onGetJson?.Invoke(null);
}
}
}
void LoadTexture2D(Texture2DLoadInfo loadInfo)
{
StartCoroutine(GetTexture2D(loadInfo.fullPath, loadInfo.onLoaded));
}
IEnumerator GetTexture2D(string path, UnityAction<Texture2D> onGetTexture2D)
{
using (UnityWebRequest webRequest = UnityWebRequestTexture.GetTexture(path))
{
yield return webRequest.SendWebRequest();
if (webRequest.result == UnityWebRequest.Result.Success)
{
Texture2D tex2d = DownloadHandlerTexture.GetContent(webRequest);
onGetTexture2D?.Invoke(tex2d);
}
else
{
Debug.Log("error = " + webRequest.error + "\n Load Path = " + path);
onGetTexture2D?.Invoke(null);
}
}
}
void LoadAudioClip(AudioLoadInfo loadInfo)
{
StartCoroutine(GetAudio(loadInfo.fullPath, loadInfo.audioType, loadInfo.onLoaded));
}
IEnumerator GetAudio(string path, AudioType type, UnityAction<AudioClip> onGetAudio)
{
using (UnityWebRequest webRequest = UnityWebRequestMultimedia.GetAudioClip(path, type))
{
yield return webRequest.SendWebRequest();
if (webRequest.result == UnityWebRequest.Result.Success)
{
AudioClip clip = DownloadHandlerAudioClip.GetContent(webRequest);
onGetAudio?.Invoke(clip);
}
else
{
Debug.Log("error = " + webRequest.error + "\n Load Path = " + path);
onGetAudio?.Invoke(null);
}
}
}
}这些代码的意思就是一个资源被加载完成(或者加载失败)之后才能进行下一个资源的加载,不允许多个资源同时加载。同时已经被加载的资源的引用被保存起来,如果需要加载的资源实际上已经被加载了,只要从这些引用中直接获取就可以了,不需要再去下载了。至于说具体哪些资源先加载,哪些资源后加载,主要是由资源加载信息添加到队列里面的顺序决定的。下面的脚本里面使用了yield return null的次数来决定延迟多少帧再把加载信息添加到队列里面,这貌似不是太好的方法,不过至少到目前来说还算管用,应该有更好的方法,先这样吧。
下面是示范代码:
using System.Collections;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.Events;
public class LoadGameObject : MonoBehaviour
{
[SerializeField]
string loadPath;
[SerializeField]
string objName;
[SerializeField]
bool replaceLitShader = true;
[SerializeField]
bool replaceObstacle = true;
[SerializeField]
bool replaceCollider = true;
[SerializeField]
string layerName = "CamCol";
[SerializeField]
bool isTree = false;
[SerializeField]
int delayFrame = 0;
IEnumerator Start()
{
for (int i = 0; i < delayFrame; i++)
{
yield return null;
}
string relativePath = loadPath + "/" + objName.ToLower() + ".ab";
LoadFromStreamingAssets.GameObjectLoadInfo info = new LoadFromStreamingAssets.GameObjectLoadInfo(relativePath, objName, InstantiateObject);
LoadFromStreamingAssets.instance.AddLoadInfo(info);
}
GameObject _obj = null;
public GameObject loadedObj { get { return _obj; } }
UnityAction<GameObject> onLoadedObj;
public void AddActLoadedObj(UnityAction<GameObject> act) { onLoadedObj -= act; onLoadedObj += act; }
public void RemoveActLoadedObj(UnityAction<GameObject> act) { onLoadedObj -= act; }
void InstantiateObject(GameObject prefab)
{
if (prefab)
{
GameObject obj = Instantiate(prefab);
if (obj)
{
obj.transform.SetParent(transform);
obj.transform.localPosition = Vector3.zero;
obj.transform.localRotation = Quaternion.identity;
obj.transform.localScale = Vector3.one;
MeshRenderer[] renders = obj.GetComponentsInChildren<MeshRenderer>();
if (replaceLitShader)
{
foreach (MeshRenderer render in renders)
{
foreach (Material mat in render.materials)
{
mat.shader = isTree ? ShaderHolder.instance.SpeedTree8 : ShaderHolder.instance.URPLit;
}
}
}
if (replaceObstacle)
{
NavMeshObstacle obstacle = obj.GetComponentInChildren<NavMeshObstacle>();
if (obstacle)
{
if (replaceCollider)
{
BoxCollider col = obstacle.GetComponent<BoxCollider>();
if (!col)
{
col = obj.AddComponent<BoxCollider>();
col.size = obstacle.size;
col.center = obstacle.center;
}
}
}
}
obj.layer = LayerMask.NameToLayer(layerName);
onLoadedObj?.Invoke(obj);
_obj = obj;
}
}
}
}以上就是最近的一些工作中有趣的内容。
边栏推荐
- J'aimerais dire quelques mots de plus sur ce problème de communication...
- 具体说明 Flume介绍、安装和配置
- 传输层 拥塞控制-慢开始和拥塞避免 快重传 快恢复
- UDP protocol: simple because of good nature, it is inevitable to encounter "city can play"
- C语言通过指针交换两个数
- Virtual machine VirtualBox and vagrant installation
- MSF horizontal MSF port forwarding + routing table +socks5+proxychains
- 解读云原生技术
- SAP Fiori 应用索引大全工具和 SAP Fiori Tools 的使用介绍
- Rb157-asemi rectifier bridge RB157
猜你喜欢

【.NET CORE】 请求长度过长报错解决方案

SAP Fiori 应用索引大全工具和 SAP Fiori Tools 的使用介绍

J'aimerais dire quelques mots de plus sur ce problème de communication...

2019 Alibaba cluster dataset Usage Summary

FMT open source self driving instrument | FMT middleware: a high real-time distributed log module Mlog

Olivetin can safely run shell commands on Web pages (Part 1)

【Swoole系列2.1】先把Swoole跑起来

Coco2017 dataset usage (brief introduction)

Today in history: the mother of Google was born; Two Turing Award pioneers born on the same day

Alibaba cloud international ECS cannot log in to the pagoda panel console
随机推荐
Excel usage record
Principle and usage of extern
ADB common commands
C语言指针*p++、*(p++)、*++p、*(++p)、(*p)++、++(*p)对比实例
Maixll-Dock 摄像头使用
Jerry's watch reading setting status [chapter]
MarkDown语法——更好地写博客
Jielizhi obtains the customized background information corresponding to the specified dial [chapter]
Four processes of program operation
重磅硬核 | 一文聊透对象在 JVM 中的内存布局,以及内存对齐和压缩指针的原理及应用
简单易用的PDF转SVG程序
Compilation Principle -- C language implementation of prediction table
F200 - UAV equipped with domestic open source flight control system based on Model Design
【.NET CORE】 请求长度过长报错解决方案
STM32+ESP8266+MQTT协议连接OneNet物联网平台
Easy to use PDF to SVG program
Wchars, coding, standards and portability - wchars, encodings, standards and portability
编译原理——自上而下分析与递归下降分析构造(笔记)
【Swoole系列2.1】先把Swoole跑起来
Interesting - questions about undefined