一、對象池
一種通用型的技術(shù),在其他語言中也會用到
1. 池
線程池、網(wǎng)絡(luò)連接池,池是一個思想,將不用的東西暫時用池存起來,等到再次使用的時候再調(diào)出來用,節(jié)省CPU的調(diào)度
2. 對象
C#的任何一個類都可以實例化一個對象Object
Unity中的游戲?qū)ο驡ameObject
3. 思路
最開始的時候,池中沒有對象,需要生成。用完之后放到池中。再次使用的時候再從池中獲取
3.1 回收對象
把對象放到池中
3.2 獲取對象
從池中獲取對象
3.3 代碼實現(xiàn)
using System.Collections.Generic;
using UnityEngine;
public class ObjectPool
{
#region 單例
// 聲明單例
private static ObjectPool Instance;
/// <summary>
/// 獲取單例
/// </summary>
/// <returns>The instance.</returns>
/// <param name="resPath">Res path.</param>
public static ObjectPool GetInstance(string resPath = "")
{
if (Instance == null)
{
if (resPath != "")
Instance = new ObjectPool(resPath);
else
Instance = new ObjectPool();
}
Instance.UpdateResourcePath(resPath);
return Instance;
}
// 構(gòu)造函數(shù)
private ObjectPool()
{
prefabs = new Dictionary<string, GameObject>();
pools = new Dictionary<string, List<GameObject>>();
}
private ObjectPool(string resPath)
{
prefabs = new Dictionary<string, GameObject>();
pools = new Dictionary<string, List<GameObject>>();
resourcePath = resPath;
}
#endregion
#region 對象預(yù)設(shè)體資源管理
// 資源加載路徑
private string resourcePath;
// 用字典存儲所有的預(yù)設(shè)體
private Dictionary<string, GameObject> prefabs;
// 更新預(yù)設(shè)體加載路徑
private void UpdateResourcePath(string resPath)
{
resourcePath = resPath;
}
// 獲取預(yù)設(shè)體
private GameObject GetPrefab(string prefabName)
{
// 如果包含預(yù)設(shè)體,直接返回
if (prefabs.ContainsKey(prefabName))
return prefabs[prefabName];
// 如果不包含預(yù)設(shè)體,添加新的預(yù)設(shè)體,并返回
return LoadPrefab(prefabName);
}
// 加載預(yù)設(shè)體
private GameObject LoadPrefab(string prefabName)
{
// 拼接路徑
string path = "";
if (resourcePath != "")
{
path += resourcePath;
}
// 加載預(yù)設(shè)體
GameObject obj = Resources.Load<GameObject>(path + prefabName);
// 存入字典
if (obj != null)
prefabs.Add(prefabName, obj);
// 返回
return obj;
}
#endregion
#region 對象池
// 對象池
private Dictionary<string, List<GameObject>> pools;
/// <summary>
/// 回收對象
/// </summary>
/// <param name="obj">Object.</param>
public void RecycleObject(GameObject obj)
{
// 非激活
obj.SetActive(false);
// 獲取對象名稱
string objName = obj.name.Replace("(Clone)", "");
// 判斷有無該類對象池
// 如果沒有,實例化一個子池
if (!pools.ContainsKey(objName))
pools.Add(objName, new List<GameObject>());
// 存入
pools[objName].Add(obj);
}
/// <summary>
/// 獲取對象
/// </summary>
/// <returns>The object.</returns>
/// <param name="objName">Object name.</param>
/// <param name="poolEvent">Pool event.</param>
public GameObject SpawnObject(string objName, System.Action<GameObject> poolEvent = null)
{
// 聲明一個輸出結(jié)果
GameObject result = null;
// 如果有池,并且池中有對象
if (pools.ContainsKey(objName) && pools[objName].Count > 0)
{
result = pools[objName][0];
pools[objName].Remove(result);
}
// 如果沒有池,或者池中沒有對象,需要生成
else
{
// 拿到預(yù)設(shè)體
GameObject prefab = GetPrefab(objName);
if (prefab != null)
result = GameObject.Instantiate(prefab);
}
// 激活
result.SetActive(true);
// 執(zhí)行事件
if (result && poolEvent != null)
poolEvent(result);
// 返回結(jié)果
return result;
}
#endregion
}
二、AssetBundle
- 游戲中少部分必要的資源會隨游戲打包,其余的貼圖等資源在安裝好游戲后再獲得。AssetBundle就是資源的壓縮包,放在服務(wù)器。
1. 資源管理流程
- 資源版本號:
- 客戶端
游戲開啟后,進行資源版本號校驗,如果發(fā)現(xiàn)版本號不同,就會從服務(wù)器下載 - 服務(wù)器
接收版本號之后,進行校驗,發(fā)現(xiàn)不同時,發(fā)送資源下載地址
- 客戶端
- 下載資源
拿到下載地址,使用WWW進行下載,下載后得到AssetBundle,然后解壓加載資源 - 資源校驗
- 遇到新文件,則保存
- 發(fā)現(xiàn)有文件重復(fù),則使用MD5校驗,一樣則沒有改變,不一樣就將客戶端的資源更新為從服務(wù)器上下載下來的資源
- 資源的熱更新
不需要重新下載安裝包,在游戲中將更新應(yīng)用
2. 資源管理操作
- AssetBundle的打包(服務(wù)器操作)
- Unity不支持一種AssetBundle多平臺用,只能一個平臺一個AssetBundle
- 給需要打包的資源命名(名字AssetBundle名都是小寫)
- 使用插件,Editor文件夾中
- 菜單中打包
- 資源依賴關(guān)系 Dependencies
代碼實現(xiàn)
BuildAssetBundle.cs
using UnityEngine;
using UnityEditor; // 用于寫插件的命名空間
using System.IO;
// 在菜單中創(chuàng)建一個選項
public class BuildAssetBundle : Editor
{
[MenuItem("AssetBundle/Build/OSX")]
public static void BuildOSX()
{
// AssetBundle的存儲路徑
string path = Application.dataPath + "/AssetBundles/OSX";
// 判斷路徑是否存在
if (!File.Exists(path))
{
// 如果路徑不存在,創(chuàng)建路徑
Directory.CreateDirectory(path);
}
// 打包
BuildPipeline.BuildAssetBundles(path, BuildAssetBundleOptions.None, BuildTarget.StandaloneOSXIntel64);
Debug.Log("打包成功!");
}
}
-
AssetBundle的加載(客戶端操作)
- 不帶有依賴的加載
using System.Collections; using UnityEngine; using UnityEngine.UI; public class LoadTexture : MonoBehaviour { // 路徑 private string path; // AssetBundle名字 public string bundleName; // 資源名字 public string assetName; private void Awake() { path = "file://" + Application.dataPath + "/AssetBundles/OSX/"; } // 只加載一張圖,沒有依賴 private IEnumerator Start() { WWW www = new WWW(path + bundleName); // 下載 yield return www; // 獲取AssetBundle AssetBundle assetBundle = www.assetBundle; // 加載資源 Texture texture = assetBundle.LoadAsset<Texture>(assetName); // 使用資源 GetComponent<RawImage>().texture = texture; // 釋放資源 assetBundle.Unload(false); } }- 帶有依賴的加載
using System.Collections; using UnityEngine; public class LoadCube : MonoBehaviour { private string path; public string bundleName; public string assetName; public int version = 0; private void Awake() { path = "file://" + Application.dataPath + "/AssetBundles/OSX/"; } // Cube有依賴,需要先查一下都依賴了什么 private IEnumerator Start() { // 下載整個資源管理系統(tǒng)的說明文件(.manifast)先檢查緩存,存在則加載,不存在則下載 WWW www = WWW.LoadFromCacheOrDownload(path + "OSX", version); // 下載 yield return www; // 獲取到說明文件的bundle AssetBundle mani = www.assetBundle; // 加載mani文件 AssetBundleManifest assetBundleManifest = mani.LoadAsset<AssetBundleManifest>("AssetBundleManifest"); // 查當(dāng)前要加載的資源的所有依賴 string[] dependencies = assetBundleManifest.GetAllDependencies(bundleName); // 釋放AssetBundle mani.Unload(false); // 聲明依賴的Bundle數(shù)組 AssetBundle[] depBundles = new AssetBundle[dependencies.Length]; // 依次下載依賴 for (int i = 0; i < dependencies.Length; i++) { // 下載依賴 www = WWW.LoadFromCacheOrDownload(path + dependencies[i], version); // 等待下載 yield return www; // 獲取,內(nèi)存中有就可以,不需要加載出來 depBundles[i] = www.assetBundle; } // 下載最終的資源Bundle www = WWW.LoadFromCacheOrDownload(path + bundleName, version); // 等待下載 yield return www; // 獲取Bundle AssetBundle assetBundle = www.assetBundle; // 獲取資源 GameObject cubePre = assetBundle.LoadAsset<GameObject>(assetName); // 生成對象 Instantiate(cubePre); // 釋放Bundle assetBundle.Unload(false); for (int i = 0; i < dependencies.Length; i++) { depBundles[i].Unload(false); } } }