逻辑:解析版本文件,保存文件信息——>获取文件信息——>加载Bundle(——>检查依赖——>获取依赖文件信息再次递归加载bundle)——>加载资源——>完成后回调
可能一个bundle需要另一个bundle(而这个有需要别的bundle),需要递归加载。
必须加载完依赖bundle和自身bundle都,才能加载资源,,,最后在通知应用层加载完成。
这种异步加载方式,在商业中比较常用。
C# internal解析_愤怒的YYZ的博客-CSDN博客_c# internal
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System;
//改名
using UObject = UnityEngine.Object;
///
/// 资源管理器类:
/// 定义了所有使用bundle信息可用的bundleinfo类,该类用于加载bundle时使用,,,另外字典m_BundleInfos存放资源名与所保存该资源的bundle信息
/// ParseVersionFile解析版本文件:获得版本文件中所有要加载的资源名和其所在的bundle信息以及该资源又依赖哪些bundle,,并把lua文件的信息传给luaManager保存
/// 加载资源:加载前需要解析版本文件,LoadBundleAsync通过m_BundleInfos查找资源递归(因为有的bundle依赖别的bundle)的异步加载bundle资源,或在编辑器模式下使用EditorLoadAsset加载资源
/// 封装了loadxx的方法,用来加载指定类型资源,例如loadUI,从UI目录加载UI类型资源
///
public class ResourceManager : MonoBehaviour
{
//定义一个类,用来解析版本信息
internal class BundleInfo
{
public string AssetName;
public string BundleName;
public List<string> Dependeces;
}
//存放bundle信息的集合
private Dictionary<string, BundleInfo> m_BundleInfos = new Dictionary<string, BundleInfo>();
///
/// 解析版本文件
///
private void ParseVersionFile()
{
//拿到版本文件路径
string url = Path.Combine(PathUtil.BundleResourcePath, AppConst.FileListName);
//对文件进行读取
string[] data = File.ReadAllLines(url);
//解析文件信息
for (int i = 0; i < data.Length; i++)
{
BundleInfo bundleInfo = new BundleInfo();
string[] info = data[i].Split('|');
bundleInfo.AssetName = info[0];
bundleInfo.BundleName = info[1];
//list特性:本质是数组,可动态扩容
bundleInfo.Dependeces = new List<string>(info.Length - 2);
for (int j = 2; j < info.Length; j++)
{
bundleInfo.Dependeces.Add(info[j]);
}
m_BundleInfos.Add(bundleInfo.AssetName, bundleInfo);
}
}
//异步加载bundle,使用回调通知应用层加载完毕,,,需要通过Action回调把加载好的Object返回回去
//Object在UnityEngine和System会重名,使用using改名UObject
///
/// 异步加载资源
///
/// 资源名
/// 完成回调,默认null,不传值默认为空
///
IEnumerator LoadBundleAsync(string assetName,Action<UObject> action = null)
{
string bundleName = m_BundleInfos[assetName].BundleName;
//这个是小写的bundle.ab的路径名
string bundlePath = Path.Combine(PathUtil.BundleResourcePath, bundleName);
List<string> dependences = m_BundleInfos[assetName].Dependeces;
if(dependences != null && dependences.Count > 0)
{
//递归加载依赖bundle,因为依赖的资源目录名就是bundle资源名
for (int i = 0; i < dependences.Count; i++)
{
yield return LoadBundleAsync(dependences[i]);
}
}
//创建异步加载bundle申请
AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(bundlePath);
yield return request;
//从bundle申请加载指定路径名的文件,例如prefab
AssetBundleRequest bundleRequest = request.assetBundle.LoadAssetAsync(assetName);
yield return bundleRequest;
//如果回调和request都不为空,语法糖
action?.Invoke(bundleRequest?.asset);
}
//直接上面异步的方法向外面提供接口使用StartCoroutine不方便,提供一个接口
public void LoadAsset(string assetName, Action<UObject> action)
{
StartCoroutine(LoadBundleAsync(assetName, action));
}
void Start()
{
ParseVersionFile();
LoadAsset("Assets/BuildResources/UI/Prefabs/TestUI.prefab", OnComplete);
}
private void OnComplete(UObject obj)
{
GameObject go = Instantiate(obj) as GameObject;
go.transform.SetParent(this.transform);
go.SetActive(true);
go.transform.localPosition = Vector3.zero;
}
}
需要把每一个资源按照这个文件夹的类型写一个接口
public static string GetLuaPath(string name)
{
return string.Format("Assets/BuildResources/LuaScripts/{0}.bytes", name);
}
public static string GetUIPath(string name)
{
return string.Format("Assets/BuildResources/UI/Prefabs/{0}.prefab", name);
}
public static string GetMusicPath(string name)
{
return string.Format("Assets/BuildResources/Audio/Music/{0}", name);
}
public static string GetSoundPath(string name)
{
return string.Format("Assets/BuildResources/Audio/Sound/{0}", name);
}
public static string GetEffectPath(string name)
{
return string.Format("Assets/BuildResources/Effect/Prefabs/{0}.prefab", name);
}
public static string GetModelPath(string name)
{
return string.Format("Assets/BuildResources/Model/Prefabs/{0}.prefab", name);
}
//例如图片音乐等,后缀名也要传进来
public static string GetSpritePath(string name)
{
return string.Format("Assets/BuildResources/Sprites/{0}", name);
}
public static string GetScenePath(string name)
{
return string.Format("Assets/BuildResources/Scenes/{0}.unity", name);
}
需要让编辑器在不需要打Bundle的时候,直接使用Unity内部的资源加载来测试逻辑。
#if UNITY_EDITOR
///
/// 在编辑器环境下使用,虽然是同步加载,但是模拟成前面的异步加载函数
///
///
///
void EditorLoadAsset(string assetName, Action<UObject> action = null)
{
UObject obj = UnityEditor.AssetDatabase.LoadAssetAtPath(assetName, typeof(UObject));
if(obj = null)
{
Debug.LogError("asset name is not exist:" + assetName);
action?.Invoke(obj);
}
}
#endif
//直接上面异步的方法向外面提供接口使用StartCoroutine不方便,提供一个接口
private void LoadAsset(string assetName, Action<UObject> action)
{
//如果是编辑器模式,就加载Assets/BuildResources/UI/Prefabs/TestUI.prefab
#if UNITY_EDITOR
if (AppConst.GameMode == GameMode.EditorMode)
EditorLoadAsset(assetName, action);
else //否则加载StreamingAssets/ui/prefabs/testui.prefab.ab
#endif
StartCoroutine(LoadBundleAsync(assetName, action));
}
:::info
GameMode主要是ResourceManager和LuaManager运行时用来加载文件进行判断当前的运行模式,但是只判断EditorMode和else(PackageMode和UpdateMode),因为两者的加载方法不一样,前者从文件路径直接加载,后者需要从ab包加载。。第三处用到GameMode的是PathUtil的BundleResourcePath属性,需要根据到底是PackageMode还是UpdateMode返回到底是只读目录还是可读写目录。
:::
public enum GameMode
{
//编辑器模式,包模式,更新模式
EditorMode,
PackageBundle,
UpdateMode,
}
public class AppConst
{
public const string BundleExtension = ".ab";
public const string FileListName = "filelist.txt";
//为什么不写成const?因为要在inspector中手动修改状态,而不能每次进来修改代码
public static GameMode GameMode = GameMode.EditorMode;
}
在Hierarchy中添加框架的入口(空物体)root,添加新脚本GameStart
public class GameStart : MonoBehaviour
{
public GameMode GameMode;
// Start is called before the first frame update
void Awake()
{
AppConst.GameMode = this.GameMode;
}
}
Unity 出现error CS0103: The name ‘AssetDatabase‘ does not exist in the current context_旭God的舔狗的博客-CSDN博客_unity报错cs0103
问题描述
在Unity场景中,在进行build操作时出现这种报错,导致资源bundle无法正常生成,出现以下问题:
error CS0103: The name ‘AssetDatabase’ does not exist in the current context
error CS0234: The type or namespace name ‘AssetDatabase’ does not exist in the namespace ‘UnityEditor’ (are you missing an assembly reference?)
ps:上面两种错误都是同一种问题造成的,报错不一样的原因是由于UnityEditor在代码中的位置不同造成的:
前者在开头声明了using UnityEditor,在方法中使用AssetDatabase.LoadAssetAtPath;
后者未声明using UnityEditor,在方法中使用了UnityEditor.AssetDatabase.LoadAssetAtPath
原因分析
在非Editor文件夹下的脚本中,存在着有关UnityEditor方法的使用
方法中第一行使用了UnityEditor中的AssetDatabase.LoadAssetAtPath方法,并且该方法所在的文件并非是在Editor文件夹下,导致build操作时出现报错
解决方案
添加
#if UNITY_EDITOR
#endif
注意
注:需要把调用该方法的地方也要用#if #endif包括起来
因为该方法时需要被调用的,然后测试的时候出现了以下问题
error CS0103: The name ‘EditorLoadAsset’ does not exist in the current context
出现问题的原因是调用此方法的地方未用#if #endif包含进去,在正式运行状态下,他会认为该方法不存在,找不到该方法导出出现报错。所以要将调用该方法的地方也要用#if #endif包括进来,让正式运行状态下也不用执行调用该方法的语句