公司终于使用AssetBundle来开发游戏了。虽然AssetBunle对项目的资源更新方面起到了很大的便利。但是在开发过程中却多有不变。主要体现在一下几个方面。
1:资源加载过程,直接使用资源,本地bundle资源,网上bundle资源这三种模式切换很麻烦。
2:加载具体资源,需要传入相应的资源字符串,字符拼写容易出错。
针对以上两种问题,我想出以下的解决方案。
1:无论是,本地资源,本地bundle资源,网上bundle资源,都使用同一套接口来调用。模式之间的切换,通过预先指定就行了。
2:加载本地资源时,资源并没有在resource下,因此主要用于编辑器模式,所以通过配置相关的路径信息来加载资源,我获取了所有的bundle资源的路径,并以json表来保存。
3:针对设置的bundle,相应的bundle资源,通过生成相应的枚举,和类变量,通过引用。就可以避免字符串之间的传入错误。
下面是相关的代码,可以参考:
1:配置bundle资源的路径信息
2:生成所有bundle资源的枚举
3:以bundle名+“_”+”prefab”为类名,生成类名,资源名用字段保存,用const来修饰
[MenuItem("LazerSelect/Bundle/Config")]
private static void BundlePathConfig()
{
//准备一个代码编译器单元
CodeCompileUnit unit = new CodeCompileUnit();
//准备必要的命名空间(这个是指要生成的类的空间)
CodeNamespace sampleNamespace = new CodeNamespace();
CodeTypeDeclaration bundleEnum = new CodeTypeDeclaration("EBundleName");
bundleEnum.IsEnum = true;
bundleEnum.TypeAttributes = TypeAttributes.Public;
sampleNamespace.Types.Add(bundleEnum);
unit.Namespaces.Add(sampleNamespace);
CodeMemberField field;
CodeTypeDeclaration bundleClass;
int bundleIndex = 0;
Dictionary<string, PrefabPath> prefabDict = new Dictionary<string, PrefabPath>();
string[] assetBundleNames = AssetDatabase.GetAllAssetBundleNames();
Dictionary<ObjPoolItem, string> pool_item_bundle_dict = new Dictionary<ObjPoolItem, string>();
foreach (string bundleName in assetBundleNames)
{
Debug.Log("bundle name:" + bundleName);
field = new CodeMemberField(typeof(System.Enum), bundleName);
field.InitExpression = new CodePrimitiveExpression(bundleIndex);
bundleEnum.Members.Add(field);
++bundleIndex;
bundleClass = new CodeTypeDeclaration(string.Format("{0}_Prefab", bundleName));
bundleClass.IsClass = true;
bundleClass.TypeAttributes = TypeAttributes.Public;
sampleNamespace.Types.Add(bundleClass);
PrefabPath prefabPathInfo = new PrefabPath();
prefabPathInfo.PrefabPathDict = new Dictionary<string, string>();
string[] prefabPaths = AssetDatabase.GetAssetPathsFromAssetBundle(bundleName);
foreach (string prefabPath in prefabPaths)
{
if (prefabPath.Contains(".mat")) { continue;}
if (Helper.IsChinaString(prefabPath)){continue;}
if (prefabPath.Contains(".prefab"))
{
ObjPoolItem poolItem = (UnityEditor.AssetDatabase.LoadAssetAtPath(prefabPath, typeof(GameObject)) as GameObject).GetComponent<ObjPoolItem>();
if (poolItem)
{
pool_item_bundle_dict[poolItem] = bundleName;
}
}
string[] splitStrings = prefabPath.Split('/', '.');
string prefabName = splitStrings[splitStrings.Length - 2];
Debug.Log("prefabPath:" + prefabPath + " prefabname:" + prefabName);
int prefabIndex;
if (int.TryParse(prefabName, out prefabIndex))
{
continue;
}
if (prefabName.Contains("-"))
{
continue;
}
if (!prefabPathInfo.PrefabPathDict.ContainsKey(prefabName))
{
field = new CodeMemberField(typeof(String), prefabName);
field.Attributes = MemberAttributes.Public | MemberAttributes.Const;
field.InitExpression = new CodePrimitiveExpression(prefabName);
bundleClass.Members.Add(field);
prefabPathInfo.PrefabPathDict[prefabName] = prefabPath;
}
else
{
if (prefabPath.Contains(".prefab"))//优先保存预设
{
prefabPathInfo.PrefabPathDict[prefabName] = prefabPath;
}
}
}
prefabDict[bundleName] = prefabPathInfo;
}
string json = JsonConvert.SerializeObject(prefabDict, Newtonsoft.Json.Formatting.Indented);//JsonMapper.ToJson(prefabDict);
Debug.Log(json);
byte[] bytes = System.Text.UTF8Encoding.UTF8.GetBytes(json);
File.WriteAllBytes(Application.dataPath + "/" + "Project_Fish/BundleBuilder/BundleConfigJson/BundleConfig.json", bytes);
string outputFile = Application.dataPath + "/" + "Project_Fish/BundleBuilder/BundleConfigJson/BundleConfig.cs";// "Customer.cs";
CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
CodeGeneratorOptions options = new CodeGeneratorOptions();
options.BracingStyle = "C";
options.BlankLinesBetweenMembers = false;
using (System.IO.StreamWriter sw = new System.IO.StreamWriter(outputFile))
{
provider.GenerateCodeFromCompileUnit(unit, sw, options);
}
AssetDatabase.Refresh();
foreach (KeyValuePair<ObjPoolItem, string> key_value in pool_item_bundle_dict)
{
Debug.Log(key_value.Value);
//标记预设为修改状态 这步很重要,不执行这步操作。关闭Unity后。预设并不会保存。
EditorUtility.SetDirty(key_value.Key.gameObject);
//key_value.Key.Bundle = key_value.Value;// (EBundleName)Enum.Parse(typeof(EBundleName), key_value.Value);
key_value.Key.BundleName = (EBundleName)Enum.Parse(typeof(EBundleName), key_value.Value);
}
AssetDatabase.Refresh();
AssetDatabase.SaveAssets();
pool_item_bundle_dict = null;
GC.Collect();
}
1:此文件,是三种资源加载的通用接口
2:如果是编辑器模式,先加载本地资源路径信息json表(上面代码生成的)
3:如果是本地bundle模式,需要生成相关的bundle,保存在streamassets文件夹localbundle下
4:如果是服务器bundle模式,保证在调用相关的接口调用之前,将下载的bundle保存在字典即可。
public class BundleResourceManager :CommonSingleton<BundleResourceManager>
{
//bundle 字典
public Dictionary<string, AssetBundle> AssetBundlePool = new Dictionary<string, AssetBundle>();
//资源字典
public Dictionary<string, PrefabPath> ResourceDict;
public override void Initialize()
{
if(AppConst.BundleType == EBundleType.NoBundle)
{
#if UNITY_EDITOR
ResourceDict = JsonMapper.ToObject<Dictionary<string,PrefabPath>>((UnityEditor.AssetDatabase.LoadAssetAtPath("Assets/Project_Fish/BundleBuilder/BundleConfigJson/BundleConfig.json", typeof(TextAsset)) as TextAsset).text);
//SDebug.Log(ResourceDict.Count);
#endif
}
else if(AppConst.BundleType == EBundleType.LocalBundle)
{
GetBundle(EBundleName.fishuiatlas);// typeof(EBundleName),"fishuiatlas"));
GetBundle(EBundleName.fishingameuiatlas);// (EBundleName)Enum.Parse(typeof(EBundleName), "fishingameuiatlas"));
GetBundle(EBundleName.common); //(EBundleName)Enum.Parse(typeof(EBundleName), "common"));
}
}
public T CreateObj<T>(EBundleName bundleName,string prefabName) where T :Object
{
SDebug.Log("need create bundle:"+bundleName+" prefab name:"+prefabName);
if(prefabName.Contains("."))//去除后缀
{
prefabName = prefabName.Split('.')[0];
}
return CreateObj(bundleName, prefabName,typeof(T)) as T;
}
public Object CreateObj(EBundleName bundleName, string prefabName)
{
return CreateObj(bundleName, prefabName, typeof(GameObject));
}
//实例化预设
public UnityEngine.Object CreateObj(EBundleName bundleName, string prefabName,Type type)
{
SDebug.Log("need create bundle:" + bundleName + " prefab name:" + prefabName);
if (AppConst.BundleType == EBundleType.NoBundle)
{
string prefabPath = ResourceDict[bundleName.ToString()].PrefabPathDict[prefabName];
SDebug.Log("path:" + prefabPath);
#if UNITY_EDITOR
return UnityEditor.AssetDatabase.LoadAssetAtPath(prefabPath, type);
#endif
return null;
}
else if (AppConst.BundleType == EBundleType.LocalBundle)
{
AssetBundle assetBundle;
if (AssetBundlePool.TryGetValue(bundleName.ToString(), out assetBundle))
{
return assetBundle.LoadAsset(prefabName);
}
assetBundle = GetBundle(bundleName);
if (assetBundle != null)
{
return assetBundle.LoadAsset(prefabName);
}
Debug.LogError("the prefab name:" + prefabName + " is not exist!");
return null;
}
else
{
AssetBundle assetBundle;
if (AssetBundlePool.TryGetValue(bundleName.ToString(), out assetBundle))
{
return assetBundle.LoadAsset(prefabName);
}
Debug.LogError("the prefab name:" + prefabName + " is not exist!");
return null;
}
}
public void SetBundle(string bundleName,AssetBundle bundle)
{
AssetBundlePool[bundleName] = bundle;
}
public AssetBundle GetBundle(EBundleName bundleName)
{
AssetBundle result;
if(AssetBundlePool.TryGetValue(bundleName.ToString(),out result))
{
return result;
}
if(AppConst.BundleType == EBundleType.LocalBundle)
{
result = AssetBundle.LoadFromFile(string.Format("{0}/{1}", Util.LocalBundlePath, bundleName));
SetBundle(bundleName.ToString(), result);
return result;
}
Debug.LogError(string.Format("the bundle name :{0} is not exist!", bundleName));
return null;
}
}