一、对象池
一种通用型的技术,在其他语言中也会用到
1. 池
线程池、网络连接池,池是一个思想,将不用的东西暂时用池存起来,等到再次使用的时候再调出来用,节省CPU的调度
2. 对象
C#的任何一个类都可以实例化一个对象Object
Unity中的游戏对象GameObject
3. 思路
最开始的时候,池中没有对象,需要生成。用完之后放到池中。再次使用的时候再从池中获取
3.1 回收对象
把对象放到池中
3.2 获取对象
从池中获取对象
3.3 代码实现
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;
}
// 构造函数
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 对象预设体资源管理
// 资源加载路径
private string resourcePath;
// 用字典存储所有的预设体
private Dictionary<string, GameObject> prefabs;
// 更新预设体加载路径
private void UpdateResourcePath(string resPath)
{
resourcePath = resPath;
}
// 获取预设体
private GameObject GetPrefab(string prefabName)
{
// 如果包含预设体,直接返回
if (prefabs.ContainsKey(prefabName))
return prefabs[prefabName];
// 如果不包含预设体,添加新的预设体,并返回
return LoadPrefab(prefabName);
}
// 加载预设体
private GameObject LoadPrefab(string prefabName)
{
// 拼接路径
string path = "";
if (resourcePath != "")
{
path += resourcePath;
}
// 加载预设体
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)
{
// 声明一个输出结果
GameObject result = null;
// 如果有池,并且池中有对象
if (pools.ContainsKey(objName) && pools[objName].Count > 0)
{
result = pools[objName][0];
pools[objName].Remove(result);
}
// 如果没有池,或者池中没有对象,需要生成
else
{
// 拿到预设体
GameObject prefab = GetPrefab(objName);
if (prefab != null)
result = GameObject.Instantiate(prefab);
}
// 激活
result.SetActive(true);
// 执行事件
if (result && poolEvent != null)
poolEvent(result);
// 返回结果
return result;
}
#endregion
}
二、AssetBundle
- 游戏中少部分必要的资源会随游戏打包,其余的贴图等资源在安装好游戏后再获得。AssetBundle就是资源的压缩包,放在服务器。
1. 资源管理流程
- 资源版本号:
- 客户端
游戏开启后,进行资源版本号校验,如果发现版本号不同,就会从服务器下载 - 服务器
接收版本号之后,进行校验,发现不同时,发送资源下载地址
- 客户端
- 下载资源
拿到下载地址,使用WWW进行下载,下载后得到AssetBundle,然后解压加载资源 - 资源校验
- 遇到新文件,则保存
- 发现有文件重复,则使用MD5校验,一样则没有改变,不一样就将客户端的资源更新为从服务器上下载下来的资源
- 资源的热更新
不需要重新下载安装包,在游戏中将更新应用
2. 资源管理操作
- AssetBundle的打包(服务器操作)
- Unity不支持一种AssetBundle多平台用,只能一个平台一个AssetBundle
- 给需要打包的资源命名(名字AssetBundle名都是小写)
- 使用插件,Editor文件夹中
- 菜单中打包
- 资源依赖关系 Dependencies
代码实现
BuildAssetBundle.cs
using UnityEngine;
using UnityEditor; // 用于写插件的命名空间
using System.IO;
// 在菜单中创建一个选项
public class BuildAssetBundle : Editor
{
[MenuItem("AssetBundle/Build/OSX")]
public static void BuildOSX()
{
// AssetBundle的存储路径
string path = Application.dataPath + "/AssetBundles/OSX";
// 判断路径是否存在
if (!File.Exists(path))
{
// 如果路径不存在,创建路径
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() { // 下载整个资源管理系统的说明文件(.manifast)先检查缓存,存在则加载,不存在则下载 WWW www = WWW.LoadFromCacheOrDownload(path + "OSX", version); // 下载 yield return www; // 获取到说明文件的bundle AssetBundle mani = www.assetBundle; // 加载mani文件 AssetBundleManifest assetBundleManifest = mani.LoadAsset<AssetBundleManifest>("AssetBundleManifest"); // 查当前要加载的资源的所有依赖 string[] dependencies = assetBundleManifest.GetAllDependencies(bundleName); // 释放AssetBundle mani.Unload(false); // 声明依赖的Bundle数组 AssetBundle[] depBundles = new AssetBundle[dependencies.Length]; // 依次下载依赖 for (int i = 0; i < dependencies.Length; i++) { // 下载依赖 www = WWW.LoadFromCacheOrDownload(path + dependencies[i], version); // 等待下载 yield return www; // 获取,内存中有就可以,不需要加载出来 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); } } }