资源管理器,顾名思义,就是管理游戏中的所有资源,包括加载资源,回收资源,销毁资源等等。下面这个资源管理器主要提供了对assetbundle异步加载功能,Resources的加载没有放在里面。
一.使用方法
1.在进入游戏前调用Init(),加载一个资源的名称列表
2.调用AsynGetAsset(string name, Action<UnityEngine.Object> callback)方法(异步)
说明,这里的资源列表是从bundle_list列表中解析出来的,并且有一个manifest文件用来查找加载依赖的资源,所以,如果要使用这个资源管理器需要将您的资源进行依赖打包assetbundle,可以参考我以前的文章。http://chenshuhb.blog.51cto.com/6087203/1836562
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using System.IO;
using LitJson;
using UnityEngine.SceneManagement;
/**
* 资源管理器
*
* 功能:
* 1.加载基础配置数据
* 2.为各个模块提供基础数据接口,供各个模块调用
* 3. assetbundle,prefab资源的加载
*
**/
public class ResourceManager : Singleton<ResourceManager>
{
//版本文件名
private static readonly string VERSION_FILE = "resource_version";
//assetbundle 路径
private string BUNDLE_PATH = "";
//本地Resource路径
private string LOCAL_RES_PATH = "";
private const string assetTail = ".unity3d";
//版本文件
private JsonData jdVersionFile = null;
/// <summary>
/// 资源文件字典
/// key:资源名,value:资源路径
/// </summary>
private Dictionary<string, string> dicRes = new Dictionary<string, string>();
/// <summary>
/// 资源目录字典,需要获取目录下的所有资源
/// </summary>
private Dictionary<string, string> dicResDir = new Dictionary<string, string>();
/// <summary>
/// 用来存放加载出来的依赖资源的AssetBundle
/// </summary>
Dictionary<string, AssetBundle> dicDepenceAssetBundles = new Dictionary<string, AssetBundle>();
/// <summary>
/// 依赖资源名称(路径)list
/// </summary>
List<string> dependenceBundleNames = new List<string>();
/// <summary>
/// bundle资源的总manifest
/// </summary>
private AssetBundleManifest m_manifest = null;
/// <summary>
/// 已经加载完成的依赖资源的数量
/// </summary>
int finishedDependenceCount = 0;
/// <summary>
/// 所有依赖资源的数量
/// </summary>
int allDependenceCount = 0;
#region LoadAssetBundle
/// <summary>
/// 初始化
/// </summary>
public void Init()
{
#if UNITY_EDITOR && UNITY_ANDROID
BUNDLE_PATH = "file:///" + Application.persistentDataPath + "/res/";
#elif UNITY_EDITOR && UNITY_IOS
BUNDLE_PATH = Application.streamingAssetsPath + "/ios/";
#elif UNITY_EDITOR_WIN
BUNDLE_PATH = "file:///" + Application.streamingAssetsPath + "/windows/";
#elif UNITY_ANDROID
BUNDLE_PATH = "file://" + Application.persistentDataPath + "/res/";
#elif UNITY_IOS
BUNDLE_PATH = Application.persistentDataPath + "/res/";
#endif
LOCAL_RES_PATH = Application.persistentDataPath + "/res/";
loadResVersion();
}
/// <summary>
/// 加载资源版本文件
/// </summary>
private void loadResVersion()
{
string version;
if (File.Exists(LOCAL_RES_PATH + VERSION_FILE))
version = File.ReadAllText(LOCAL_RES_PATH + VERSION_FILE);
else
return;
jdVersionFile = JsonMapper.ToObject(version);
if (null == jdVersionFile)
Debug.LogError("Config file is null");
parse(jdVersionFile);
}
#region 加载资源方法
/// <summary>
/// 获取资源
/// </summary>
/// <param name="name">资源名</param>
/// <param name="callback"></param>
public void AsynGetAsset(string name, Action<UnityEngine.Object> callback)
{
name = name + assetTail;
if (!dicRes.ContainsKey(name))
{
Debug.Log("AsynGetAsset: The asset " + name + " does not exist!");
if (null != callback)
callback(null);
}
string path = dicRes[name];
Game.Instance.StartCoroutine(LoadAssetBundle(path, (obj) =>
{
callback(obj);
}));
}
/// <summary>
/// 释放依赖资源
/// </summary>
public void UnloadUnusedAssetbundle()
{
foreach (KeyValuePair<string,AssetBundle> kvp in dicDepenceAssetBundles)
{
AssetBundle ab = kvp.Value;
if (null != ab)
ab.Unload(false);
}
dicDepenceAssetBundles.Clear();
}
#endregion
/// <summary>
/// 要加载的资源名称列表
/// </summary>
List<string> assetNameList = new List<string>();
/// <summary>
/// 资源是否已经加载完成
/// </summary>
bool isOk = true;
/// <summary>
/// 加载目标资源
/// </summary>
/// <param name="name"></param>
/// <param name="callback"></param>
IEnumerator LoadAssetBundle(string name, Action<UnityEngine.Object> callback)
{
assetNameList.Add(name);
Action<Dictionary<string, AssetBundle>> action = (depenceAssetBundles) =>
{
string realName = name;// this.GetRuntimePlatform() + "/" + name;//eg:Windows/ui/panel.unity3d
LoadResReturnWWW(realName, (www) =>
{
int index = realName.LastIndexOf("/");
string assetName = realName.Substring(index + 1);
//去掉".unity3d"后缀
assetName = assetName.Replace(assetTail, "");
//去掉扩展名
//int extIndex = assetName.LastIndexOf(".");
//string extName = assetName.Substring(extIndex);
//assetName = assetName.Replace(extName, "");
AssetBundle assetBundle = www.assetBundle;
//重新给material 的Shader赋值
resetShader(assetBundle);
UnityEngine.Object obj = null;
if (null != assetBundle)
obj = assetBundle.LoadAsset(assetName);//LoadAsset(name),这个name没有后缀,eg:panel
else
Debug.Log("assetBundle is null assetName is " + assetName);
//重新给material 的Shader赋值
resetShader(obj);
if (null != assetBundle)
assetBundle.Unload(false);//卸载资源内存
//加载目标资源完成的回调
callback(obj);
});
isOk = true;
};
while (assetNameList.Count > 0)
{
if (isOk)
{
isOk = false;
string _name = assetNameList[0];
assetNameList.RemoveAt(0);
LoadDependenceAssets(_name, action);
}
yield return null;
}
}
/// <summary>
/// 加载目标资源的依赖资源
/// </summary>
/// <param name="targetAssetName"></param>
/// <param name="action"></param>
private void LoadDependenceAssets(string targetAssetName, Action<Dictionary<string, AssetBundle>> action)
{
//Debug.Log("LoadDependenceAssets : targetAssetName is " + targetAssetName);
Action<AssetBundleManifest> dependenceAction = (manifest) =>
{
if (null == m_manifest)
m_manifest = manifest;
excuteLoad(targetAssetName, manifest, action);
};
if (null != m_manifest)
excuteLoad(targetAssetName, m_manifest, action);
else
LoadAssetBundleManifest(dependenceAction);
}
/// <summary>
/// 递归加载依赖
/// </summary>
void excuteLoad(string targetAssetName, AssetBundleManifest manifest, Action<Dictionary<string, AssetBundle>> action)
{
finishedDependenceCount = 0;
dependenceBundleNames.Clear();
string[] de = manifest.GetAllDependencies(targetAssetName);
allDependenceCount = de.Length;
//如果要加载的资源没有依赖资源,直接回调返回
if (allDependenceCount == 0)
{
action(null);
}
else
loadSubDependence(targetAssetName, manifest, action, allDependenceCount);//, ref depenceAssetBundles, ref dependenceBundleNames);
}
/// <summary>
/// 加载依赖
/// </summary>
/// <param name="targetAssetName"></param>
/// <param name="manifest"></param>
/// <param name="action"></param>
void loadSubDependence(string targetAssetName, AssetBundleManifest manifest, Action<Dictionary<string, AssetBundle>> action,
int allDependenceCount)//,ref Dictionary<string, AssetBundle> depenceAssetBundles,ref List<string> dependenceBundleNames)
{
string[] dependences = manifest.GetDirectDependencies(targetAssetName);
int length = dependences.Length;
if (length == 0)
{
//没有依赖
return;
}
else
{
//有依赖,加载所有依赖资源
for (int i = 0; i < length; i++)
{
string dependenceAssetName = dependences[i];
//递归
loadSubDependence(dependenceAssetName, manifest, action, allDependenceCount);//,ref depenceAssetBundles,ref dependenceBundleNames);
//判断资源是否已经被加载过
if (dependenceBundleNames.Contains(dependenceAssetName))
continue;
dependenceBundleNames.Add(dependenceAssetName);
if (!dicDepenceAssetBundles.ContainsKey(dependenceAssetName))
{
//加载
LoadResReturnWWW(dependenceAssetName, (www) =>
{
int index = dependenceAssetName.LastIndexOf("/");
string assetName = dependenceAssetName.Substring(index + 1);
//去掉".unity3d"后缀
assetName = assetName.Replace(assetTail, "");
//去掉扩展名
//int extIndex = assetName.LastIndexOf(".");
//string extName = assetName.Substring(extIndex);
//assetName = assetName.Replace(extName, "");
AssetBundle assetBundle = www.assetBundle;
//重新给material 的Shader赋值
resetShader(assetBundle);
if (null == assetBundle)
Debug.LogError("null == assetBundle : " + dependenceAssetName);
UnityEngine.Object obj = null;
//try
//{
obj = assetBundle.LoadAsset(assetName);
if (dicAtlasObj.ContainsKey(assetName))
{
if (obj == null)
{
#if REALMA_ONGUI_DEBUG
OnGUIDebug.AddMsg("atlas null name is " + assetName);
#endif
Debug.LogError("atlas null name is " + assetName);
}
else
dicAtlasObj[assetName] = obj;
}
//重新给material 的Shader赋值
resetShader(obj);
if (!dicDepenceAssetBundles.ContainsKey(dependenceAssetName))
dicDepenceAssetBundles.Add(dependenceAssetName, assetBundle);
finishedDependenceCount++;
//依赖都加载完了
if (finishedDependenceCount == allDependenceCount)
{
action(dicDepenceAssetBundles);
}
});
}
else
{
finishedDependenceCount++;
//依赖都加载完了
if (finishedDependenceCount == allDependenceCount)
{
action(dicDepenceAssetBundles);
}
}
}
}
}
public UnityEngine.Object GetAtlasObj(string name)
{
if (dicAtlasObj.ContainsKey(name))
return dicAtlasObj[name];
return null;
}
//UnityEditor 在 Android 下打出来的 Android 的 Assetbundle,加载之后 Shader 不能正确的执行!!!
//解决办法就是,在 Assetbundle Load完之后,遍历 Material ,把所有的 Shader 都重新 Load 赋值一次。
//通过Assetbundle加载出来的资源有两种类型需要处理:
//1.如果加载出来的是材质,那么可以对shader进行赋值
//2.如果加载出来的是GameObject,那么需要获取到Renderer组件下的所有材质,再对每个材质的shader赋值
/// </summary>
void resetShader(UnityEngine.Object obj)
{
List<Material> listMat = new List<Material>();
listMat.Clear();
if (obj is Material)
{
Material m = obj as Material;
listMat.Add(m);
}
else if (obj is GameObject)
{
GameObject go = obj as GameObject;
Renderer rend = go.GetComponent<Renderer>();
if (null != rend)
{
Material[] materialsArr = rend.sharedMaterials;
foreach (Material m in materialsArr)
listMat.Add(m);
}
}
for (int i = 0; i < listMat.Count; i++)
{
Material m = listMat[i];
if (null == m)
continue;
var shaderName = m.shader.name;
var newShader = Shader.Find(shaderName);
if (newShader != null)
m.shader = newShader;
else
Debug.LogWarning("unable to refresh shader: " + shaderName + " in material " + m.name);
}
}
/// <summary>
/// 给assetBundle中的材质的shader重新赋值
/// </summary>
/// <param name="assetBundle"></param>
void resetShader(AssetBundle assetBundle)
{
if (null == assetBundle)
return;
var materials = assetBundle.LoadAllAssets(typeof(Material));
foreach (Material m in materials)
{
var shaderName = m.shader.name;
var newShader = Shader.Find(shaderName);
if (newShader != null)
m.shader = newShader;
else
Debug.LogWarning("unable to refresh shader: " + shaderName + " in material " + m.name);
}
}
/// <summary>
/// 加载AssetBundleManifest
/// </summary>
/// <param name="action"></param>
private void LoadAssetBundleManifest(Action<AssetBundleManifest> action)
{
string manifestName = this.GetRuntimePlatform();
LoadResReturnWWW(manifestName, (www) =>
{
AssetBundle assetBundle = www.assetBundle;
UnityEngine.Object obj = assetBundle.LoadAsset("AssetBundleManifest");
assetBundle.Unload(false);
AssetBundleManifest manif = obj as AssetBundleManifest;
action(manif);
});
}
#endregion
#region ExcuteLoader
public void LoadResReturnWWW(string name, Action<WWW> callback)
{
string path = "";
#if UNITY_EDITOR_WIN
path = BUNDLE_PATH + name;
#elif UNITY_ANDROID
path = BUNDLE_PATH + name;
#endif
Game.Instance.StartCoroutine(LoaderRes(path, callback));
}
IEnumerator LoaderRes(string path, Action<WWW> callback)
{
WWW www = new WWW(path);
yield return www;
if (!string.IsNullOrEmpty(www.error))
{
Debug.Log("www.error is " + www.error);
}
if (www.isDone)
{
callback(www);
}
}
#endregion
/// <summary>
/// 解析版本文件
/// </summary>
void parse(JsonData jd)
{
JsonData resInfos = null;
if (jd.Keys.Contains("resource"))
resInfos = jd["resource"];
if (null == resInfos)
{
Debug.LogError("解析资源版本文件出错");
return;
}
string[] resNames = new string[resInfos.Count];
resInfos.Keys.CopyTo(resNames, 0);
for (int i = 0; i < resNames.Length; i++)
{
string name = resNames[i].Substring(resNames[i].LastIndexOf("/") + 1);
if (!dicRes.ContainsKey(name))
dicRes.Add(name, resNames[i]);
}
}
#region Util
/// <summary>
/// 平台对应文件夹
/// </summary>
/// <returns></returns>
private string GetRuntimePlatform()
{
string platform = "";
if (Application.platform == RuntimePlatform.WindowsPlayer || Application.platform == RuntimePlatform.WindowsEditor)
{
//platform = "Windows";
platform = "android";
}
else if (Application.platform == RuntimePlatform.Android)
{
platform = "android";
}
else if (Application.platform == RuntimePlatform.IPhonePlayer)
{
platform = "ios";
}
else if (Application.platform == RuntimePlatform.OSXPlayer || Application.platform == RuntimePlatform.OSXEditor)
{
platform = "osx";
}
return platform;
}
#endregion
}
肯定还有更好的和更完整的资源管理方案,在此跪求大神分享。