当前位置:网站首页>A method of sequentially loading Unity Resources
A method of sequentially loading Unity Resources
2022-07-06 18:20:00 【ttod】
unity Provides the basis for UnityWebRequest Class to load some columns of resources , But if you simply load the content to be loaded at the same time, it will cause instant jamming , At the same time, the order of resource loading cannot be controlled . Because sometimes we need to control the loading order of resources , So I made a script to solve this problem . Not that this script must be good , Because there may be many schemes much better than this , perhaps unity It provides better functions , But I don't know how to use it . Anyway? , Online code bar .
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);
}
}
}
}
These codes mean that a resource is loaded ( Or loading failed ) Then the next resource can be loaded , Multiple resources are not allowed to load at the same time . At the same time, the references of the loaded resources are saved , If the resources that need to be loaded have actually been loaded , Just get it directly from these references , No need to download again . As for which resources are loaded first , Which resources are loaded later , It is mainly determined by the order in which the resource loading information is added to the queue . The following script uses yield return null To determine how many frames to delay before adding the loading information to the queue , This seems not a very good way , But at least for now it works , There should be a better way , Let's do that first. .
Here is the sample code :
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;
}
}
}
}
The above is some interesting content in recent work .
边栏推荐
- Release of the sample chapter of "uncover the secrets of asp.net core 6 framework" [200 pages /5 chapters]
- STM32按键状态机2——状态简化与增加长按功能
- 2019 Alibaba cluster dataset Usage Summary
- 模板于泛型编程之declval
- MSF horizontal MSF port forwarding + routing table +socks5+proxychains
- Grafana 9.0 is officially released! It's the strongest!
- 【.NET CORE】 请求长度过长报错解决方案
- Jielizhi obtains the customized background information corresponding to the specified dial [chapter]
- echart简单组件封装
- 容器里用systemctl运行服务报错:Failed to get D-Bus connection: Operation not permitted(解决方法)
猜你喜欢
STM32按键状态机2——状态简化与增加长按功能
The integrated real-time HTAP database stonedb, how to replace MySQL and achieve nearly a hundredfold performance improvement
Why does wechat use SQLite to save chat records?
当保存参数使用结构体时必备的开发技巧方式
Windows connects redis installed on Linux
阿里云国际版ECS云服务器无法登录宝塔面板控制台
Recursive way
std::true_type和std::false_type
關於這次通信故障,我想多說幾句…
I want to say more about this communication failure
随机推荐
MSF horizontal MSF port forwarding + routing table +socks5+proxychains
Grafana 9.0 is officially released! It's the strongest!
Will openeuler last long
最新财报发布+天猫618双榜第一,耐克蓄力领跑下个50年
AFNetworking框架_上传文件或图像server
Jerry's watch deletes the existing dial file [chapter]
Four processes of program operation
用友OA漏洞学习——NCFindWeb 目录遍历漏洞
celery最佳实践
Jerry's setting currently uses the dial. Switch the dial through this function [chapter]
【LeetCode第 300 场周赛】
Virtual machine VirtualBox and vagrant installation
第三季百度网盘AI大赛盛夏来袭,寻找热爱AI的你!
Open source and safe "song of ice and fire"
【剑指 Offer】 60. n个骰子的点数
declval(指导函数返回值范例)
使用block实现两个页面之间的传统价值观
STM32按键状态机2——状态简化与增加长按功能
Codeforces Round #803 (Div. 2)
Compilation principle - top-down analysis and recursive descent analysis construction (notes)