目录
一、Assets序列化和反序列化
/// <summary>
/// 使用如下特性即可在Create->找到CreateAssets菜单,创建一个Assets可格式化的存储文件
/// </summary>
[CreateAssetMenu(fileName = "TestAssets", menuName = "CreateAssets", order = 0)]
public class AssetsSerialize : ScriptableObject
{
public int Id;
public string Name;
public List<string> TestList;
}
/// <summary>
/// 读取.asset文件
/// </summary>
void ReadTestAssets()
{
AssetsSerialize assets = UnityEditor.AssetDatabase.LoadAssetAtPath<AssetsSerialize>("Assets/Scripts/MyTestAssets.asset");
Debug.Log(assets.Id);
Debug.Log(assets.Name);
foreach(var v in assets.TestList)
{
Debug.Log(v);
}
}
二、Xml序列化和反序列化
[System.Serializable]
public class TestSerialize {
[XmlAttribute("Id")]
public int Id { get; set; }
[XmlAttribute("Name")]//单属性int string bool float double ...
public string Name { get; set; }
[XmlElement("List")] //[XmlArray("List")] 数组 列表
public List<int> List { get; set; }
}
void SerializeTest()
{
TestSerialize testSerialize = new TestSerialize();
testSerialize.Id = 1;
testSerialize.Name = "test";
testSerialize.List = new List<int>();
testSerialize.List.Add(2);
testSerialize.List.Add(3);
XmlSerialize(testSerialize);
}
/// <summary>
/// 序列化xml
/// </summary>
/// <param name="testSerialize"></param>
void XmlSerialize(TestSerialize testSerialize)
{
//文件流
FileStream fileStream = new FileStream(Application.dataPath + "/test.xml", FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
//写入流
StreamWriter sw = new StreamWriter(fileStream, System.Text.Encoding.UTF8);
//创建testSerialized同类型的Xml序列化对象
XmlSerializer xml = new XmlSerializer(testSerialize.GetType());
//使用xml序列化testSerialized对象存入sw指定的文件流
xml.Serialize(sw, testSerialize);
//关闭写入流
sw.Close();
//关闭文件流
fileStream.Close();
}
/// <summary>
/// 反序列化xml
/// </summary>
/// <returns></returns>
TestSerialize XmlDeSerialize()
{
FileStream fs = new FileStream(Application.dataPath + "/test.xml", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
XmlSerializer xs = new XmlSerializer(typeof(TestSerialize));
TestSerialize testSerialize = (TestSerialize)xs.Deserialize(fs);
fs.Close();
return testSerialize;
}
三、二进制形式序列化和反序列化
/// <summary>
/// 将对象序列化为二进制文件
/// </summary>
/// <param name="testSerialize"></param>
void BinarySerialize(TestSerialize testSerialize)
{
//创建二进制文件
FileStream fs = new FileStream(Application.dataPath + "/test.bytes", FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
BinaryFormatter bf = new BinaryFormatter();
//序列化testSerialize保存入fs指定文件
bf.Serialize(fs, testSerialize);
//关闭流
fs.Close();
}
/// <summary>
/// 二进制文件反序列化
/// </summary>
/// <returns></returns>
TestSerialize BinaryDeserialize()
{
//使用编辑器加载方式二进制文件
TextAsset textAsset = UnityEditor.AssetDatabase.LoadAssetAtPath<TextAsset>("Assets/test.bytes");
//缓存流(将二进制文件缓存入缓存区域)
MemoryStream stream = new MemoryStream(textAsset.bytes);
//使用BinaryFormatter反序列化二进制文件
BinaryFormatter bf = new BinaryFormatter();
TestSerialize ts = (TestSerialize)bf.Deserialize(stream);
//关闭流
stream.Close();
return ts;
}
四、AssetBundle
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
public class BundleEditor{
[MenuItem("Tools/打包!")]
public static void Build()
{
//注意:StreamingAssets文件夹需手动创建
//参数:打包路径(Assets/StreamingAssets文件夹)、打包方式、打包平台(运行平台)
BuildPipeline.BuildAssetBundles(Application.streamingAssetsPath, BuildAssetBundleOptions.ChunkBasedCompression,
EditorUserBuildSettings.activeBuildTarget);
//刷新编辑器
AssetDatabase.Refresh();
}
}
上面这个文件是编辑器文件,放在Editor文件夹下(自己建立个Editor文件夹)
准备打包的对象如下随便找个预制体,设置AssetBundle名称
同时这个预制体 身上的所有依赖组件都必须打包,才可以让预制体通过AB包加载出来时是正常的,这里我这个Cube还用了M1材质球,打包出来后StreamingAssets会对应地出现文件,最关键的是要看懂这下面2个图
这是apple包的manifest文件内容,真正程序要加载的是没有后缀的,名为apple的文件,真正程序要加载的是没有后缀的,名为apple的文件,真正程序要加载的是没有后缀的,名为apple的文件(这一点要清楚,所以我说3次)
可见有Assets下面有2个预制体文件路径,这表明这个AB包有2个预制体,而依赖文件是m1和m2。
在要使用到Spp或者Cqq之前,要先加载完成依赖文件才能让资源显示正常,下面给出比较通用的加载方法。
Dictionary<string, GameObject> resourcesDict = new Dictionary<string, GameObject>();
void Start()
{
GameObject goPrefab = LoadResourceByResourceNameFromAb("apple", "Cqq");
if (goPrefab != null)
{
GameObject go = GameObject.Instantiate(goPrefab);
}
}
private GameObject LoadResourceByResourceNameFromAb(string abName, string resourceName)
{
if (!resourcesDict.ContainsKey(abName + "|" + resourceName))
{
//加载ab包 本地加载方式 一个名为apple的ab包文件
AssetBundle assetBundle = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + abName);
if (assetBundle != null)
{
//加载源资源时先加载依赖资源
//1.加载位于StreamingAssets目录下的StreamingAssets包
AssetBundle streamingAssetsBundle = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/StreamingAssets");
if (streamingAssetsBundle == null)
{
Debug.Log("streamingAssetsBundle is not exist!");
return null;
}
//加载StreamingAssets包下的AssetBundleManifest文件(可打开StreamingAssets.manifest文件看第三行的名字就是它)
AssetBundleManifest assetBundleManifest = streamingAssetsBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
//获取abName包的全部依赖名称!即StreamingAssets.manifest文件的指定Info为abName的Dependencies的数组内容
string[] deps = assetBundleManifest.GetAllDependencies(abName);
//加载abName包的全部依赖资源
for(int i=0;i< deps.Length; i++)
{
//Debug.Log("依赖资源名称" + deps[i]);
//因为依赖名称是一个相对路径,例如:StreamingAssets下的pp文件夹的m2资源是写为 pp/m2,所以加载是没问题
AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + deps[i]);
}
//加载源资源
GameObject go = assetBundle.LoadAsset<GameObject>(resourceName);
if (go != null)
{
resourcesDict.Add(abName + "|" + resourceName, go);
return go;
}
else
{
Debug.LogWarning("abNmae:" + abName + ",resourceName:" + resourceName + " is not exist!");
return null;
}
}
else
{
Debug.LogWarning("abNmae:" + abName + " is not exist!");
return null;
}
}
else
{
GameObject go = null;
resourcesDict.TryGetValue(abName + "|" + resourceName, out go);
return go;
}
}
注意:我加载AB包的全部依赖资源的方式,是通过StreamingAssets包的manifest文件获取该AB包的全部依赖资源名称的。
StreamingAssets包是自动生成的,它也是位于StreamingAssets文件夹下,内容如下:
可见apple包有2个依赖分别为m1和pp/m2 注意这里是相对于StreamingAssets的依赖资源包路径。
所以上面代码的加载方式是可以这么写的,这个pp/m2是怎么产生的呢?其实AssetBundle如下就会自动生成。
上面代码缺点有很多,
问题一:当我 加载了apple包的Cqq后,就已经加载了一次apple包的全部 依赖资源m1和pp/m2都加载了,但是Cqq只用pp/m2 ,不需要m1,这就是加载了多余的依赖资源,所以我们最好将Cqq和Spp预制体分开2个包打包比较合适,也就是将依赖于不同资源的资源分开打包,将依赖于同样资源的资源一起打包,多余加载问题。
问题二:重复加载问题,上面的代码当我加载一次apple包内的Cqq预制体后,apple包的全部依赖资源都加载过了,当第二次加载apple包的Spp预制体时,我又再次去加载了apple包的全部依赖资源。。很傻吧这操作。。怎么解决我想可能就是加个标志位去判断,这个ab包的依赖资源是否已被加载过,这样,还没真正考虑那么详细,百度可能有人写过这样的解决方法吧