当前位置:网站首页>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;
}
}
}
}
以上就是最近的一些工作中有趣的内容。
边栏推荐
- Prophet模型的简介以及案例分析
- 测试1234
- Five data structures of redis
- STM32+HC05串口蓝牙设计简易的蓝牙音箱
- std::true_ Type and std:: false_ type
- FMT open source self driving instrument | FMT middleware: a high real-time distributed log module Mlog
- d绑定函数
- 1700C - Helping the Nature
- Interesting - questions about undefined
- F200——搭载基于模型设计的国产开源飞控系统无人机
猜你喜欢
【.NET CORE】 请求长度过长报错解决方案
F200——搭载基于模型设计的国产开源飞控系统无人机
[Android] kotlin code writing standardization document
Prophet模型的简介以及案例分析
趣-关于undefined的问题
Scratch epidemic isolation and nucleic acid detection Analog Electronics Society graphical programming scratch grade examination level 3 true questions and answers analysis June 2022
The third season of Baidu online AI competition is coming in midsummer, looking for you who love AI!
從交互模型中蒸餾知識!中科大&美團提出VIRT,兼具雙塔模型的效率和交互模型的性能,在文本匹配上實現性能和效率的平衡!...
[.Net core] solution to error reporting due to too long request length
declval(指导函数返回值范例)
随机推荐
celery最佳实践
High precision operation
Fleet tutorial 13 basic introduction to listview's most commonly used scroll controls (tutorial includes source code)
Prophet模型的简介以及案例分析
Codeforces Round #803 (Div. 2)
The difference between parallelism and concurrency
第三季百度网盘AI大赛盛夏来袭,寻找热爱AI的你!
MarkDown语法——更好地写博客
Grafana 9.0 正式发布!堪称最强!
The latest financial report release + tmall 618 double top, Nike energy leads the next 50 years
C语言高校实验室预约登记系统
《ASP.NET Core 6框架揭秘》样章发布[200页/5章]
C语言指针*p++、*(p++)、*++p、*(++p)、(*p)++、++(*p)对比实例
30 minutes to understand PCA principal component analysis
Coco2017 dataset usage (brief introduction)
J'aimerais dire quelques mots de plus sur ce problème de communication...
模板于泛型编程之declval
Jerry's watch deletes the existing dial file [chapter]
2019 Alibaba cluster dataset Usage Summary
TOP命令详解