资源管理器,顾名思义,就是管理游戏中的所有资源,包括加载资源,回收资源,销毁资源等等。下面这个资源管理器主要提供了对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

}


肯定还有更好的和更完整的资源管理方案,在此跪求大神分享。