制作assetbundle是手游的必备技能,作者在这里分享一下自己写的用的工具类根据当前项目选择的平台,打包对应的资源包,并保存assetsbundle的相关信息进json文件里可以在tool这里选择打包resource下的资源(代码可以修改为其他目录,已经作为常量,便于修改)也可以在project面板里选择具体的文件夹右键那么他就会打包在StreamingAssets里贴一下代码,主要的类,这个类需要放在Assets/Editor的目录下using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using NUnit.Framework.Constraints;
using UnityEditor;
using UnityEngine;
using LitJson;
public class BuildAssetsBundle : Editor
{
private const string BuildAssetsBundleMenuAll = "Tools/BuildAssetsBundle";
private const string BuildAssetsBundleMenu = "Assets/BuildAssetsBundle";
private static List
///
/// 打包assetsbundle的源文件的文件夹
///
private static string OriginalDirectory = Application.dataPath + "/Resources/";
///
/// 打出包的输出文件夹
///
private static string OutDirectory = "Assets/StreamingAssets/AssetsBundle/";
private const string ConfigFile = "Assets/StreamingAssets/AssetsBundle/AssetsBundleConfig.Config";
//配置文件json,对应的对象实例
private static AssetsBundleConfig config = null;
#region 打assets包
///
/// 打包Assets/package目录下的所有文件为bundle包
///
[MenuItem(BuildAssetsBundleMenuAll)]
public static void BuildAssetsBundleAll()
{
DirectoryInfo info = new DirectoryInfo(OriginalDirectory);
DirectoryInfo[] infos = info.GetDirectories();
GetConfig();
for (int i = 0; i < infos.Length; i++)
{
var name = infos[i].Name;
FindAllFile(OriginalDirectory + name + "/", name);
}
OutPutConfig();
}
//根据具体文件夹打包assetsbundle
[MenuItem(BuildAssetsBundleMenu, false, 1)]
public static void BuildAssetsB()
{
var paths = Selection.assetGUIDs.Select(AssetDatabase.GUIDToAssetPath).Where(AssetDatabase.IsValidFolder).ToList();
if (paths.Count > 1)
{
EditorUtility.DisplayDialog("", "不能同时选择多个目录进行该操作!", "确定");
return;
}
var strName = paths[0].Split('/');
GetConfig();
FindAllFile(paths[0], strName[strName.Length - 1]);
OutPutConfig();
}
static void FindAllFile(string path, string assetsBundlename)
{
assetNames.Clear();
FindFile(path);
CreateAssetBunlde(assetsBundlename);
}
static void FindFile(string assetBundleName)
{
DirectoryInfo di = new DirectoryInfo(assetBundleName);
FileInfo[] fis = di.GetFiles();
for (int i = 0; i < fis.Length; i++)
{
if (fis[i].Extension != ".meta")
{
string str = fis[i].FullName;
assetNames.Add(str.Substring(str.LastIndexOf("Assets")));
}
}
DirectoryInfo[] dis = di.GetDirectories();
for (int j = 0; j < dis.Length; j++)
{
FindFile(dis[j].FullName);
}
}
///
/// 每次打包assetsbundle的前要获取整个配置文件
///
static void GetConfig()
{
config = null;
var bytes = FileTools.TryReadFile(ConfigFile);
config = (bytes == null) ? new AssetsBundleConfig() : JsonMapper.ToObject
}
///
/// 打包好了,要输出配置文件
///
static void OutPutConfig()
{
if (config != null && config.AssetsBundles != null & config.AssetsBundles.Count > 0)
{
var content = JsonMapper.ToJson(config);
FileTools.TryWriteFile(ConfigFile, content);
}
AssetDatabase.Refresh();
}
///
/// 打包资源到StreamingAssets下对应的平台,对应的资源名
///
///
static void CreateAssetBunlde(string assetBundleName)
{
string[] assetBundleNames = assetNames.ToArray();
AssetBundleBuild[] abs = new AssetBundleBuild[1];
abs[0].assetNames = assetBundleNames;
abs[0].assetBundleName = assetBundleName + ".assetBundle";
BuildTarget buildTarget;
string outPutPlat;
#if UNITY_ANDROID //安卓
buildTarget = BuildTarget.Android;
outPutPlat = "Android";
#elif UNITY_IOS
buildTarget = BuildTarget.iOS;
outPutPlat = "IOS";
#else
buildTarget = BuildTarget.StandaloneWindows;
outPutPlat = "Windows";
#endif
var dicName = OutDirectory + outPutPlat;
FileTools.EnsureDirectoryExist(dicName);
AssetBundleManifest mainfest = BuildPipeline.BuildAssetBundles(dicName, abs, BuildAssetBundleOptions.None, buildTarget);
var filepath = dicName + "/" + abs[0].assetBundleName;
var size = (int)FileTools.TryGetFileSize(filepath);
var assets = CreateAssets(assetBundleName, outPutPlat, size);
if (config.AssetsBundles != null && config.AssetsBundles.Count > 0)
{
for (int i = 0; i < config.AssetsBundles.Count; i++)
{
var havedAssets = config.AssetsBundles[i];
if ((havedAssets.name == assets.name) && (havedAssets.platform == assets.platform))
{
config.AssetsBundles.Remove(havedAssets);
break;
}
}
}
config.AssetsBundles.Add(assets);
Debug.LogError(assetBundleName + "打包完成");
}
///
/// 新建assetbundle对应的对象实例
///
///
///
///
static Assets CreateAssets(string assetBundleName, string platform,int size)
{
Assets assets = new Assets();
assets.name = assetBundleName;
assets.platform = platform;
assets.size = size;
return assets;
}
#endregion
}
assetsbundle的实体配置类using System.Collections.Generic;
using UnityEngine;
///
/// AssetsBundleConfig配置文件的映射对象
///
[SerializeField]
public class AssetsBundleConfig
{
[SerializeField]
public List
public AssetsBundleConfig()
{
AssetsBundles = new List
}
}
[SerializeField]
public class Assets
{
///
/// AssetsBundle的名字
///
[SerializeField]
public string name;
///
/// AssetsBundle的大小
///
[SerializeField]
public int size;
///
/// AssetsBundle的所属平台0-window,1-ios,2-android
///
[SerializeField]
public string platform;
public Assets()
{
}
public Assets(string name, int size, string platform)
{
this.name = name;
this.size = size;
this.platform = platform;
}
}
操作文件的工具类
转自:https://blog.csdn.net/qq_22911163/article/details/79097462
上一篇讲到了如何打包assetsbundle,紧跟而来的就是加载assetsbundle的问题。资源的动态的加载用到Resources和AssetsBundle这两种方式,而后一种方式无论是在热更还是大小,安全等方面更具优势。下面要贴出作者一直在用的资源管理方式。大概是利用枚举区分这两种方式,在unity编辑器里可以随意切换,打包发布时也可以根据自己的需求切换。利用assetsbundle加载资源时,需要在启动游戏的时候,将所需的assetsbundle加载进内存,再通过具体方法加载assets bundle的资源。这里我设置了一个最大资源加载数的选项, 因为游戏要加载的,动态生成的prefab等往往不会频繁使用,通过queue队列的方式,可以有效减少动态加载量很少的资源,常驻内存的情况。废话不多说了,贴两张效果图下面贴一下代码using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Object = UnityEngine.Object;
using LitJson;
using System.Linq;
enum LoadAssetsType
{
AssetsBundle = 0,
Resources = 1
}
public class AssetsManager : MonoBehaviour
{
///
/// 单例
///
private static AssetsManager _instance;
[SerializeField]
[Tooltip("加载资源的方式")]
private LoadAssetsType _loadAssetsType;
[SerializeField]
[Tooltip("最大资源缓存数")]
private int _maxResourcesCount = 30;
///
/// 数据字典
///
private Dictionary
///
/// 资源key值的队列
///
private Queue
///
/// AssetBundle字典集合
///
private Dictionary
private List
//不同平台下StreamingAssets的路径是不同的,这里需要注意一下。
private static string _streamingAssetsPath;
private static string _resourcesPath;
//配置文件的路径
private static string _configPath;
private void Awake()
{
SetResourcesPath();
StartCoroutine(Initassetsbundle());
}
private static void SetResourcesPath()
{
_streamingAssetsPath =
#if UNITY_ANDROID
"jar:file://" + Application.dataPath + "!/assets/AssetsBundle/";
#elif UNITY_IOS
Application.dataPath + "/Raw/AssetsBundle/";
#elif UNITY_STANDALONE_WIN || UNITY_EDITOR
"file://" + Application.dataPath + "/StreamingAssets/AssetsBundle/";
#else
string.Empty;
#endif
if (Application.platform == RuntimePlatform.Android)
{
_resourcesPath = _streamingAssetsPath + "Android/";
}
else if (Application.platform == RuntimePlatform.IPhonePlayer)
{
_resourcesPath = _streamingAssetsPath + "IOS/";
}
else
{
_resourcesPath = _streamingAssetsPath + "Windows/";
}
_configPath = _streamingAssetsPath + "AssetsBundleConfig.Config";
}
///
/// 单例
///
///
public static AssetsManager GetInstance()
{
if (_instance == null)
{
var go = GameObject.Find("game");
if (go == null)
{
go = new GameObject("game");
}
var assetsManager = go.GetComponent
if (assetsManager == null)
{
assetsManager = go.AddComponent
}
_instance = assetsManager;
if (string.IsNullOrEmpty(_resourcesPath))
{
SetResourcesPath();
}
}
return _instance;
}
///
/// 初始化assetsbundle
///
///
IEnumerator Initassetsbundle()
{
WWW w = new WWW(_configPath);
yield return w;
string content = null;
if (string.IsNullOrEmpty(w.error))
{
content = w.text;
}
else
{
yield break;
}
_AssetsList.Clear();
var config = JsonMapper.ToObject
//判断平台,只加载相应平台的assetbundle
for (int i = 0; i < config.AssetsBundles.Count; i++)
{
string outPutPlat;
#if UNITY_ANDROID
outPutPlat = "Android";
#elif UNITY_IOS
outPutPlat = "IOS";
#else
outPutPlat = "Windows";
#endif
var assets = config.AssetsBundles[i];
if (assets.platform == outPutPlat)
{
_AssetsList.Add(assets);
}
}
_AssetsList.Reverse();
LoadAllAssetsBundle();
}
///
/// 加载所有assetsbundle进字典
///
private void LoadAllAssetsBundle()
{
if (_AssetsList != null && _AssetsList.Count > 0)
{
_AssetsList.ForEach((m) =>
{
StartCoroutine(LoadOneAssetsBundle(m));
});
}
}
///
/// 加载单个assetsbundle进字典
///
///
///
IEnumerator LoadOneAssetsBundle(Assets assets)
{
WWW www = new WWW(_resourcesPath + assets.name + ".assetbundle");
yield return www;
if (string.IsNullOrEmpty(www.error))
{
var asset = www.assetBundle;
_dicAssetBundles.Add(assets.name, asset);
float p = ((float)_dicAssetBundles.Count / _AssetsList.Count);
//是否加载完了所有的assetsbundle,TODO 通知游戏启动项,为了完成demo,先这样写
if (p==1)
{
GameObject.Find("game").AddComponent
}
}
}
public T LoadAssets
{
if (string.IsNullOrEmpty(path))
{
Debug.LogError("传进的路径不合法");
return null;
}
string[] paths = null;
if (_dicAssets.ContainsKey(path))
{
return (T)_dicAssets[path];
}
switch (_loadAssetsType)
{
case LoadAssetsType.AssetsBundle:
paths = path.Split('/');
path = paths[paths.Length - 1];
return AssetsBundleLoad
case LoadAssetsType.Resources:
paths = path.Split('.');
path = paths[0];
return ResourcesLoad
default:
throw new ArgumentOutOfRangeException();
}
}
private T AssetsBundleLoad
{
foreach (var key in _dicAssetBundles.Keys)
{
var a = _dicAssetBundles[key];
var data = (T)a.LoadAsset(path, typeof(T));
if (data != null)
{
AddResouce(path, data);
return data;
}
}
Debug.LogError("根据这个路径什么都没有找到" + path);
return null;
}
private T ResourcesLoad
{
var data = Resources.Load
if (data != null)
{
AddResouce(path, data);
}
else
{
Debug.LogError("根据这个路径什么都没有找到" + path);
}
return data;
}
///
/// 往字典里添加资源
///
///
///
private void AddResouce(string path, Object obj)
{
if (_dicAssets.Count >= _maxResourcesCount)
{
var lastResourceName = _assetsName.Dequeue();
_dicAssets.Remove(lastResourceName);
}
_assetsName.Enqueue(path);
_dicAssets.Add(path, obj);
}
}
这里加载assetsbundle需要根据上次打包生成的json配置文件,去读到具体的assetbundle,然后加载,很好的支持了热更todo这里,需要用事件机制告诉其他组件,已经准备好了assetsbundle。下节讲解事件机制吧,敬请期待。using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test : MonoBehaviour {
void Start () {
//加载资源,参数需要这样传,Resources下面的第一个目录开始,需要后缀结尾
var cube = AssetsManager.GetInstance().LoadAssets
Debug.LogError(cube.name);
}
}
---------------------
本文来自 苏江水 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/qq_22911163/article/details/79106692?utm_source=copy