From e3a47393f4c5b7b6c340da9ace84003d1bc14b3b Mon Sep 17 00:00:00 2001 From: ALEXTANG <574809918@qq.com> Date: Tue, 5 Sep 2023 14:35:19 +0800 Subject: [PATCH] =?UTF-8?q?[+]=20=E6=96=B0=E5=A2=9E=E6=B8=B8=E6=88=8F?= =?UTF-8?q?=E7=89=A9=E4=BD=93=E7=BC=93=E5=AD=98=E6=B1=A0ResourcePool?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [+] 新增游戏物体缓存池ResourcePool --- .../Runtime/GameFramework/Resource/Pool.meta | 3 + .../Resource/Pool/CreatePoolOperation.cs | 70 +++++ .../Resource/Pool/CreatePoolOperation.cs.meta | 3 + .../Resource/Pool/GameObjectPool.cs | 234 ++++++++++++++++ .../Resource/Pool/GameObjectPool.cs.meta | 3 + .../Resource/Pool/ResourcePool.cs | 253 +++++++++++++++++ .../Resource/Pool/ResourcePool.cs.meta | 3 + .../Resource/Pool/SpawnHandle.cs | 152 ++++++++++ .../Resource/Pool/SpawnHandle.cs.meta | 3 + .../GameFramework/Resource/Pool/Spawner.cs | 262 ++++++++++++++++++ .../Resource/Pool/Spawner.cs.meta | 3 + .../GameFramework/Resource/ResourceManager.cs | 112 ++++---- 12 files changed, 1053 insertions(+), 48 deletions(-) create mode 100644 Assets/TEngine/Runtime/GameFramework/Resource/Pool.meta create mode 100644 Assets/TEngine/Runtime/GameFramework/Resource/Pool/CreatePoolOperation.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/Resource/Pool/CreatePoolOperation.cs.meta create mode 100644 Assets/TEngine/Runtime/GameFramework/Resource/Pool/GameObjectPool.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/Resource/Pool/GameObjectPool.cs.meta create mode 100644 Assets/TEngine/Runtime/GameFramework/Resource/Pool/ResourcePool.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/Resource/Pool/ResourcePool.cs.meta create mode 100644 Assets/TEngine/Runtime/GameFramework/Resource/Pool/SpawnHandle.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/Resource/Pool/SpawnHandle.cs.meta create mode 100644 Assets/TEngine/Runtime/GameFramework/Resource/Pool/Spawner.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/Resource/Pool/Spawner.cs.meta diff --git a/Assets/TEngine/Runtime/GameFramework/Resource/Pool.meta b/Assets/TEngine/Runtime/GameFramework/Resource/Pool.meta new file mode 100644 index 00000000..ce80b4ec --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Resource/Pool.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 62c8f710f3264758a89eaefc4ae88921 +timeCreated: 1693831262 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Resource/Pool/CreatePoolOperation.cs b/Assets/TEngine/Runtime/GameFramework/Resource/Pool/CreatePoolOperation.cs new file mode 100644 index 00000000..00328027 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Resource/Pool/CreatePoolOperation.cs @@ -0,0 +1,70 @@ +using YooAsset; + +namespace TEngine +{ + public class CreatePoolOperation : GameAsyncOperation + { + private enum ESteps + { + None, + Waiting, + Done, + } + + private readonly AssetOperationHandle _handle; + private ESteps _steps = ESteps.None; + + internal CreatePoolOperation(AssetOperationHandle handle) + { + _handle = handle; + } + protected override void OnStart() + { + _steps = ESteps.Waiting; + } + protected override void OnUpdate() + { + if (_steps == ESteps.None || _steps == ESteps.Done) + return; + + if (_steps == ESteps.Waiting) + { + if (_handle.IsValid == false) + { + _steps = ESteps.Done; + Status = EOperationStatus.Failed; + Error = $"{nameof(AssetOperationHandle)} is invalid."; + return; + } + + if (_handle.IsDone == false) + return; + + if (_handle.AssetObject == null) + { + _steps = ESteps.Done; + Status = EOperationStatus.Failed; + Error = $"{nameof(AssetOperationHandle.AssetObject)} is null."; + return; + } + + _steps = ESteps.Done; + Status = EOperationStatus.Succeed; + } + } + + /// + /// 等待异步实例化结束。 + /// + public void WaitForAsyncComplete() + { + if (_handle != null) + { + if (_steps == ESteps.Done) + return; + _handle.WaitForAsyncComplete(); + OnUpdate(); + } + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Resource/Pool/CreatePoolOperation.cs.meta b/Assets/TEngine/Runtime/GameFramework/Resource/Pool/CreatePoolOperation.cs.meta new file mode 100644 index 00000000..dada4ead --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Resource/Pool/CreatePoolOperation.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4e1853e7ff624c639fba990079beeee5 +timeCreated: 1693831296 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Resource/Pool/GameObjectPool.cs b/Assets/TEngine/Runtime/GameFramework/Resource/Pool/GameObjectPool.cs new file mode 100644 index 00000000..aecf11bf --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Resource/Pool/GameObjectPool.cs @@ -0,0 +1,234 @@ +using System.Collections.Generic; +using UnityEngine; +using YooAsset; + +namespace TEngine +{ + /// + /// 游戏物体对象池。 + /// + internal class GameObjectPool + { + private readonly GameObject _root; + private readonly Queue _cacheOperations; + private readonly bool _dontDestroy; + private readonly int _initCapacity; + private readonly int _maxCapacity; + private readonly float _destroyTime; + private float _lastRestoreRealTime = -1f; + + /// + /// 资源句柄。 + /// + public AssetOperationHandle AssetHandle { private set; get; } + + /// + /// 资源定位地址。 + /// + public string Location { private set; get; } + + /// + /// 内部缓存总数。 + /// + public int CacheCount => _cacheOperations.Count; + + /// + /// 外部使用总数。 + /// + public int SpawnCount { private set; get; } = 0; + + /// + /// 是否常驻不销毁。 + /// + public bool DontDestroy => _dontDestroy; + + /// + /// 游戏物体对象池。 + /// + /// 对象池根节点。 + /// 资源定位地址。 + /// 是否常驻不销毁。 + /// 初始容量。 + /// 最大容量。 + /// 对象池销毁时间。 + public GameObjectPool(GameObject poolingRoot, string location, bool dontDestroy, int initCapacity, int maxCapacity, float destroyTime) + { + _root = new GameObject(location); + _root.transform.parent = poolingRoot.transform; + Location = location; + + _dontDestroy = dontDestroy; + _initCapacity = initCapacity; + _maxCapacity = maxCapacity; + _destroyTime = destroyTime; + + // 创建缓存池 + _cacheOperations = new Queue(initCapacity); + } + + /// + /// 创建对象池。 + /// + /// 资源包。 + public void CreatePool(ResourcePackage package) + { + // 加载游戏对象 + AssetHandle = package.LoadAssetAsync(Location); + + // 创建初始对象 + for (int i = 0; i < _initCapacity; i++) + { + var operation = AssetHandle.InstantiateAsync(_root.transform); + operation.Completed += Operation_Completed; + _cacheOperations.Enqueue(operation); + } + } + private void Operation_Completed(AsyncOperationBase obj) + { + if (obj.Status == EOperationStatus.Succeed) + { + var op = obj as InstantiateOperation; + if (op.Result != null) + op.Result.SetActive(false); + } + } + + /// + /// 销毁游戏对象池。 + /// + public void DestroyPool() + { + // 卸载资源对象 + AssetHandle.Release(); + AssetHandle = null; + + // 销毁游戏对象 + Object.Destroy(_root); + _cacheOperations.Clear(); + + SpawnCount = 0; + } + + /// + /// 查询静默时间内是否可以销毁。 + /// + public bool CanAutoDestroy() + { + if (_dontDestroy) + return false; + if (_destroyTime < 0) + return false; + + if (_lastRestoreRealTime > 0 && SpawnCount <= 0) + return (Time.realtimeSinceStartup - _lastRestoreRealTime) > _destroyTime; + else + return false; + } + + /// + /// 游戏对象池是否已经销毁。 + /// + public bool IsDestroyed() + { + return AssetHandle == null; + } + + /// + /// 回收操作。 + /// + /// 资源实例化操作句柄。 + public void Restore(InstantiateOperation operation) + { + if (IsDestroyed()) + { + DestroyInstantiateOperation(operation); + return; + } + + SpawnCount--; + if (SpawnCount <= 0) + _lastRestoreRealTime = Time.realtimeSinceStartup; + + // 如果外部逻辑销毁了游戏对象 + if (operation.Status == EOperationStatus.Succeed) + { + if (operation.Result == null) + return; + } + + // 如果缓存池还未满员 + if (_cacheOperations.Count < _maxCapacity) + { + SetRestoreGameObject(operation.Result); + _cacheOperations.Enqueue(operation); + } + else + { + DestroyInstantiateOperation(operation); + } + } + + /// + /// 丢弃操作。 + /// + /// 资源实例化操作句柄。 + public void Discard(InstantiateOperation operation) + { + if (IsDestroyed()) + { + DestroyInstantiateOperation(operation); + return; + } + + SpawnCount--; + if (SpawnCount <= 0) + _lastRestoreRealTime = Time.realtimeSinceStartup; + + DestroyInstantiateOperation(operation); + } + + /// + /// 获取一个游戏对象。 + /// + /// 父节点位置。 + /// 位置。 + /// 旋转。 + /// 是否强制克隆。 + /// 用户自定义数据。 + /// Spawn操作句柄。 + public SpawnHandle Spawn(Transform parent, Vector3 position, Quaternion rotation, bool forceClone, params System.Object[] userDatas) + { + InstantiateOperation operation; + if (forceClone == false && _cacheOperations.Count > 0) + operation = _cacheOperations.Dequeue(); + else + operation = AssetHandle.InstantiateAsync(); + + SpawnCount++; + SpawnHandle handle = new SpawnHandle(this, operation, parent, position, rotation, userDatas); + YooAssets.StartOperation(handle); + return handle; + } + + private void DestroyInstantiateOperation(InstantiateOperation operation) + { + // 取消异步操作 + operation.Cancel(); + + // 销毁游戏对象 + if (operation.Result != null) + { + Object.Destroy(operation.Result); + } + } + private void SetRestoreGameObject(GameObject gameObj) + { + if (gameObj != null) + { + gameObj.SetActive(false); + gameObj.transform.SetParent(_root.transform); + gameObj.transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity); + } + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Resource/Pool/GameObjectPool.cs.meta b/Assets/TEngine/Runtime/GameFramework/Resource/Pool/GameObjectPool.cs.meta new file mode 100644 index 00000000..e1ae77bb --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Resource/Pool/GameObjectPool.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3f0c9bab3d1243fda94ec9a08844ceee +timeCreated: 1693831296 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Resource/Pool/ResourcePool.cs b/Assets/TEngine/Runtime/GameFramework/Resource/Pool/ResourcePool.cs new file mode 100644 index 00000000..5b54398b --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Resource/Pool/ResourcePool.cs @@ -0,0 +1,253 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using YooAsset; + +namespace TEngine +{ + /// + /// 游戏对象池系统。 + /// 用法 SpawnHandle handle = ResourcePool.SpawnAsync("Cube"); + /// + public static class ResourcePool + { + private static bool _isInitialize = false; + private static readonly List Spawners = new List(); + private static GameObject _root; + private static Spawner _defaultSpawner = null; + + /// + /// 默认Package对象生成器。 + /// + private static Spawner DefaultSpawner => _defaultSpawner ??= CreateSpawner(); + + /// + /// 初始化游戏对象池系统 + /// + internal static void Initialize(GameObject root) + { + if (_isInitialize) + throw new Exception($"{nameof(ResourcePool)} is initialized !"); + + if (_isInitialize == false) + { + _root = root; + _isInitialize = true; + Log.Info($"{nameof(ResourcePool)} Initialize !"); + } + } + + /// + /// 销毁游戏对象池系统 + /// + internal static void Destroy() + { + if (_isInitialize) + { + foreach (var spawner in Spawners) + { + spawner.Destroy(); + } + Spawners.Clear(); + + _isInitialize = false; + + Log.Debug($"{nameof(ResourcePool)} destroy all !"); + } + } + + /// + /// 更新游戏对象池系统 + /// + internal static void Update() + { + if (_isInitialize) + { + foreach (var spawner in Spawners) + { + spawner.Update(); + } + } + } + + /// + /// 创建游戏对象生成器 + /// + /// 资源包名称 + public static Spawner CreateSpawner(string packageName = "") + { + if (string.IsNullOrEmpty(packageName)) + { + packageName = GameModule.Resource.packageName; + } + // 获取资源包 + var assetPackage = YooAssets.GetPackage(packageName); + if (assetPackage == null) + throw new Exception($"Not found asset package : {packageName}"); + + // 检测资源包初始化状态 + if (assetPackage.InitializeStatus == EOperationStatus.None) + throw new Exception($"Asset package {packageName} not initialize !"); + if (assetPackage.InitializeStatus == EOperationStatus.Failed) + throw new Exception($"Asset package {packageName} initialize failed !"); + + if (HasSpawner(packageName)) + return GetSpawner(packageName); + + Spawner spawner = new Spawner(_root, assetPackage); + Spawners.Add(spawner); + return spawner; + } + + /// + /// 获取游戏对象生成器。 + /// + /// 资源包名称。 + public static Spawner GetSpawner(string packageName = "") + { + if (string.IsNullOrEmpty(packageName)) + { + packageName = GameModule.Resource.packageName; + } + foreach (var spawner in Spawners) + { + if (spawner.PackageName == packageName) + return spawner; + } + + Log.Warning($"Not found spawner : {packageName}"); + return null; + } + + /// + /// 检测游戏对象生成器是否存在。 + /// + /// 资源包名称。 + public static bool HasSpawner(string packageName = "") + { + if (string.IsNullOrEmpty(packageName)) + { + packageName = GameModule.Resource.packageName; + } + foreach (var spawner in Spawners) + { + if (spawner.PackageName == packageName) + return true; + } + return false; + } + + #region 操作接口 + + /// + /// 销毁所有对象池及其资源。 + /// + /// 销毁所有对象池,包括常驻对象池。 + public static void DestroyAll(bool includeAll) + { + DefaultSpawner.DestroyAll(includeAll); + } + + + /// + /// 异步创建指定资源的游戏对象池。 + /// + /// 资源定位地址。 + /// 资源常驻不销毁。 + /// 对象池的初始容量。 + /// 对象池的最大容量。 + /// 静默销毁时间(注意:小于零代表不主动销毁)。 + public static CreatePoolOperation CreateGameObjectPoolAsync(string location, bool dontDestroy = false, int initCapacity = 0, int maxCapacity = int.MaxValue, float destroyTime = -1f) + { + return DefaultSpawner.CreateGameObjectPoolAsync(location, dontDestroy, initCapacity, maxCapacity, destroyTime); + } + + /// + /// 同步创建指定资源的游戏对象池。 + /// + /// 资源定位地址。 + /// 资源常驻不销毁。 + /// 对象池的初始容量。 + /// 对象池的最大容量。 + /// 静默销毁时间(注意:小于零代表不主动销毁)。 + public static CreatePoolOperation CreateGameObjectPoolSync(string location, bool dontDestroy = false, int initCapacity = 0, int maxCapacity = int.MaxValue, float destroyTime = -1f) + { + return DefaultSpawner.CreateGameObjectPoolSync(location, dontDestroy, initCapacity, maxCapacity, destroyTime); + } + + /// + /// 异步实例化一个游戏对象。 + /// + /// 资源定位地址。 + /// 强制克隆游戏对象,忽略缓存池里的对象。 + /// 用户自定义数据。 + public static SpawnHandle SpawnAsync(string location, bool forceClone = false, params System.Object[] userDatas) + { + return DefaultSpawner.SpawnAsync(location, null, Vector3.zero, Quaternion.identity, forceClone, userDatas); + } + + /// + /// 异步实例化一个游戏对象。 + /// + /// 资源定位地址。 + /// 父物体。 + /// 强制克隆游戏对象,忽略缓存池里的对象。 + /// 用户自定义数据。 + public static SpawnHandle SpawnAsync(string location, Transform parent, bool forceClone = false, params System.Object[] userDatas) + { + return DefaultSpawner.SpawnAsync(location, parent, Vector3.zero, Quaternion.identity, forceClone, userDatas); + } + + /// + /// 异步实例化一个游戏对象。 + /// + /// 资源定位地址。 + /// 父物体。 + /// 世界坐标。 + /// 世界角度。 + /// 强制克隆游戏对象,忽略缓存池里的对象。 + /// 用户自定义数据。 + public static SpawnHandle SpawnAsync(string location, Transform parent, Vector3 position, Quaternion rotation, bool forceClone = false, params System.Object[] userDatas) + { + return DefaultSpawner.SpawnAsync(location, parent, position, rotation, forceClone, userDatas); + } + + /// + /// 同步实例化一个游戏对象。 + /// + /// 资源定位地址。 + /// 强制克隆游戏对象,忽略缓存池里的对象。 + /// 用户自定义数据。 + public static SpawnHandle SpawnSync(string location, bool forceClone = false, params System.Object[] userDatas) + { + return DefaultSpawner.SpawnSync(location, null, Vector3.zero, Quaternion.identity, forceClone, userDatas); + } + + /// + /// 同步实例化一个游戏对象。 + /// + /// 资源定位地址。 + /// 父物体 + /// 强制克隆游戏对象,忽略缓存池里的对象。 + /// 用户自定义数据。 + public static SpawnHandle SpawnSync(string location, Transform parent, bool forceClone = false, params System.Object[] userDatas) + { + return DefaultSpawner.SpawnAsync(location, parent, forceClone, userDatas); + } + + /// + /// 同步实例化一个游戏对象。 + /// + /// 资源定位地址。 + /// 父物体。 + /// 世界坐标。 + /// 世界角度。 + /// 强制克隆游戏对象,忽略缓存池里的对象。 + /// 用户自定义数据。 + public static SpawnHandle SpawnSync(string location, Transform parent, Vector3 position, Quaternion rotation, bool forceClone = false, params System.Object[] userDatas) + { + return DefaultSpawner.SpawnSync(location,parent,position,rotation,forceClone,userDatas); + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Resource/Pool/ResourcePool.cs.meta b/Assets/TEngine/Runtime/GameFramework/Resource/Pool/ResourcePool.cs.meta new file mode 100644 index 00000000..de4fb438 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Resource/Pool/ResourcePool.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: be208ee3e46e4243b9a68d4c7212015e +timeCreated: 1693831058 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Resource/Pool/SpawnHandle.cs b/Assets/TEngine/Runtime/GameFramework/Resource/Pool/SpawnHandle.cs new file mode 100644 index 00000000..5fc230e0 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Resource/Pool/SpawnHandle.cs @@ -0,0 +1,152 @@ +using UnityEngine; +using YooAsset; + +namespace TEngine +{ + public sealed class SpawnHandle : GameAsyncOperation + { + private enum ESteps + { + None, + Waiting, + Done, + } + + private readonly GameObjectPool _pool; + private InstantiateOperation _operation; + private readonly Transform _parent; + private readonly Vector3 _position; + private readonly Quaternion _rotation; + private ESteps _steps = ESteps.None; + + /// + /// 实例化的游戏对象。 + /// + public GameObject GameObj + { + get + { + if (_operation == null) + { + Log.Warning("The spawn handle is invalid !"); + return null; + } + + return _operation.Result; + } + } + + /// + /// 用户自定义数据集。 + /// + public System.Object[] UserDatas { private set; get; } + + private SpawnHandle() + { + } + + internal SpawnHandle(GameObjectPool pool, InstantiateOperation operation, Transform parent, Vector3 position, Quaternion rotation, + params System.Object[] userDatas) + { + _pool = pool; + _operation = operation; + _parent = parent; + _position = position; + _rotation = rotation; + UserDatas = userDatas; + } + + protected override void OnStart() + { + _steps = ESteps.Waiting; + } + + protected override void OnUpdate() + { + if (_steps == ESteps.None || _steps == ESteps.Done) + return; + + if (_steps == ESteps.Waiting) + { + if (_operation.IsDone == false) + return; + + if (_operation.Status != EOperationStatus.Succeed) + { + _steps = ESteps.Done; + Status = EOperationStatus.Failed; + Error = _operation.Error; + return; + } + + if (_operation.Result == null) + { + _steps = ESteps.Done; + Status = EOperationStatus.Failed; + Error = $"Clone game object is null."; + return; + } + + // 设置参数 + _operation.Result.transform.SetParent(_parent); + _operation.Result.transform.SetPositionAndRotation(_position, _rotation); + _operation.Result.SetActive(true); + + _steps = ESteps.Done; + Status = EOperationStatus.Succeed; + } + } + + /// + /// 回收对象。 + /// + public void Restore() + { + if (_operation != null) + { + ClearCompletedCallback(); + CancelHandle(); + _pool.Restore(_operation); + _operation = null; + } + } + + /// + /// 丢弃对象。 + /// + public void Discard() + { + if (_operation != null) + { + ClearCompletedCallback(); + CancelHandle(); + _pool.Discard(_operation); + _operation = null; + } + } + + /// + /// 等待异步实例化结束。 + /// + public void WaitForAsyncComplete() + { + if (_operation != null) + { + if (_steps == ESteps.Done) + return; + _operation.WaitForAsyncComplete(); + OnUpdate(); + } + } + + private void CancelHandle() + { + if (IsDone == false) + { + _steps = ESteps.Done; + Status = EOperationStatus.Failed; + Error = $"User cancelled !"; + } + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Resource/Pool/SpawnHandle.cs.meta b/Assets/TEngine/Runtime/GameFramework/Resource/Pool/SpawnHandle.cs.meta new file mode 100644 index 00000000..e0f0e6ec --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Resource/Pool/SpawnHandle.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: eb2cdce8eb814f7c92c74199d065688a +timeCreated: 1693831296 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Resource/Pool/Spawner.cs b/Assets/TEngine/Runtime/GameFramework/Resource/Pool/Spawner.cs new file mode 100644 index 00000000..c7754e82 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Resource/Pool/Spawner.cs @@ -0,0 +1,262 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using YooAsset; + +namespace TEngine +{ + /// + /// 对象生成器。 + /// + public class Spawner + { + private readonly List _gameObjectPools = new List(100); + private readonly List _removeList = new List(100); + private readonly GameObject _spawnerRoot; + private readonly ResourcePackage _package; + + public string PackageName + { + get + { + return _package.PackageName; + } + } + + + private Spawner() + { + } + internal Spawner(GameObject poolingRoot, ResourcePackage package) + { + _spawnerRoot = new GameObject($"{package.PackageName}"); + _spawnerRoot.transform.SetParent(poolingRoot.transform); + _package = package; + } + + /// + /// 更新游戏对象池系统。 + /// + internal void Update() + { + _removeList.Clear(); + foreach (var pool in _gameObjectPools) + { + if (pool.CanAutoDestroy()) + _removeList.Add(pool); + } + + foreach (var pool in _removeList) + { + _gameObjectPools.Remove(pool); + pool.DestroyPool(); + } + } + + /// + /// 销毁游戏对象池系统。 + /// + internal void Destroy() + { + DestroyAll(true); + } + + /// + /// 销毁所有对象池及其资源。 + /// + /// 销毁所有对象池,包括常驻对象池。 + public void DestroyAll(bool includeAll) + { + if (includeAll) + { + foreach (var pool in _gameObjectPools) + { + pool.DestroyPool(); + } + _gameObjectPools.Clear(); + } + else + { + List removeList = new List(); + foreach (var pool in _gameObjectPools) + { + if (pool.DontDestroy == false) + removeList.Add(pool); + } + foreach (var pool in removeList) + { + _gameObjectPools.Remove(pool); + pool.DestroyPool(); + } + } + } + + + /// + /// 异步创建指定资源的游戏对象池。 + /// + /// 资源定位地址。 + /// 资源常驻不销毁。 + /// 对象池的初始容量。 + /// 对象池的最大容量。 + /// 静默销毁时间(注意:小于零代表不主动销毁)。 + public CreatePoolOperation CreateGameObjectPoolAsync(string location, bool dontDestroy = false, int initCapacity = 0, int maxCapacity = int.MaxValue, float destroyTime = -1f) + { + return CreateGameObjectPoolInternal(location, dontDestroy, initCapacity, maxCapacity, destroyTime); + } + + /// + /// 同步创建指定资源的游戏对象池。 + /// + /// 资源定位地址。 + /// 资源常驻不销毁。 + /// 对象池的初始容量。 + /// 对象池的最大容量。 + /// 静默销毁时间(注意:小于零代表不主动销毁)。 + public CreatePoolOperation CreateGameObjectPoolSync(string location, bool dontDestroy = false, int initCapacity = 0, int maxCapacity = int.MaxValue, float destroyTime = -1f) + { + var operation = CreateGameObjectPoolInternal(location, dontDestroy, initCapacity, maxCapacity, destroyTime); + operation.WaitForAsyncComplete(); + return operation; + } + + /// + /// 创建指定资源的游戏对象池。 + /// + private CreatePoolOperation CreateGameObjectPoolInternal(string location, bool dontDestroy = false, int initCapacity = 0, int maxCapacity = int.MaxValue, float destroyTime = -1f) + { + if (maxCapacity < initCapacity) + throw new Exception("The max capacity value must be greater the init capacity value."); + + GameObjectPool pool = TryGetGameObjectPool(location); + if (pool != null) + { + Log.Warning($"GameObject pool is already existed : {location}"); + var operation = new CreatePoolOperation(pool.AssetHandle); + YooAssets.StartOperation(operation); + return operation; + } + else + { + pool = new GameObjectPool(_spawnerRoot, location, dontDestroy, initCapacity, maxCapacity, destroyTime); + pool.CreatePool(_package); + _gameObjectPools.Add(pool); + + var operation = new CreatePoolOperation(pool.AssetHandle); + YooAssets.StartOperation(operation); + return operation; + } + } + + + /// + /// 异步实例化一个游戏对象。 + /// + /// 资源定位地址。 + /// 强制克隆游戏对象,忽略缓存池里的对象。 + /// 用户自定义数据。 + public SpawnHandle SpawnAsync(string location, bool forceClone = false, params System.Object[] userDatas) + { + return SpawnInternal(location, null, Vector3.zero, Quaternion.identity, forceClone, userDatas); + } + + /// + /// 异步实例化一个游戏对象。 + /// + /// 资源定位地址。 + /// 父物体。 + /// 强制克隆游戏对象,忽略缓存池里的对象。 + /// 用户自定义数据。 + public SpawnHandle SpawnAsync(string location, Transform parent, bool forceClone = false, params System.Object[] userDatas) + { + return SpawnInternal(location, parent, Vector3.zero, Quaternion.identity, forceClone, userDatas); + } + + /// + /// 异步实例化一个游戏对象。 + /// + /// 资源定位地址。 + /// 父物体。 + /// 世界坐标。 + /// 世界角度。 + /// 强制克隆游戏对象,忽略缓存池里的对象。 + /// 用户自定义数据。 + public SpawnHandle SpawnAsync(string location, Transform parent, Vector3 position, Quaternion rotation, bool forceClone = false, params System.Object[] userDatas) + { + return SpawnInternal(location, parent, position, rotation, forceClone, userDatas); + } + + /// + /// 同步实例化一个游戏对象。 + /// + /// 资源定位地址。 + /// 强制克隆游戏对象,忽略缓存池里的对象。 + /// 用户自定义数据。 + public SpawnHandle SpawnSync(string location, bool forceClone = false, params System.Object[] userDatas) + { + SpawnHandle handle = SpawnInternal(location, null, Vector3.zero, Quaternion.identity, forceClone, userDatas); + handle.WaitForAsyncComplete(); + return handle; + } + + /// + /// 同步实例化一个游戏对象。 + /// + /// 资源定位地址。 + /// 父物体 + /// 强制克隆游戏对象,忽略缓存池里的对象。 + /// 用户自定义数据。 + public SpawnHandle SpawnSync(string location, Transform parent, bool forceClone = false, params System.Object[] userDatas) + { + SpawnHandle handle = SpawnInternal(location, parent, Vector3.zero, Quaternion.identity, forceClone, userDatas); + handle.WaitForAsyncComplete(); + return handle; + } + + /// + /// 同步实例化一个游戏对象。 + /// + /// 资源定位地址。 + /// 父物体。 + /// 世界坐标。 + /// 世界角度。 + /// 强制克隆游戏对象,忽略缓存池里的对象。 + /// 用户自定义数据。 + public SpawnHandle SpawnSync(string location, Transform parent, Vector3 position, Quaternion rotation, bool forceClone = false, params System.Object[] userDatas) + { + SpawnHandle handle = SpawnInternal(location, parent, position, rotation, forceClone, userDatas); + handle.WaitForAsyncComplete(); + return handle; + } + + /// + /// 实例化一个游戏对象。 + /// + private SpawnHandle SpawnInternal(string location, Transform parent, Vector3 position, Quaternion rotation, bool forceClone, params System.Object[] userDatas) + { + var pool = TryGetGameObjectPool(location); + if (pool != null) + { + return pool.Spawn(parent, position, rotation, forceClone, userDatas); + } + + // 如果不存在创建游戏对象池 + pool = new GameObjectPool(_spawnerRoot, location, false, 0, int.MaxValue, -1f); + pool.CreatePool(_package); + _gameObjectPools.Add(pool); + return pool.Spawn(parent, position, rotation, forceClone, userDatas); + } + + + private GameObjectPool TryGetGameObjectPool(string location) + { + foreach (var pool in _gameObjectPools) + { + if (pool.Location == location) + return pool; + } + return null; + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Resource/Pool/Spawner.cs.meta b/Assets/TEngine/Runtime/GameFramework/Resource/Pool/Spawner.cs.meta new file mode 100644 index 00000000..947f1af0 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Resource/Pool/Spawner.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 570ac9abe8a04e77b49c42719a9fa93b +timeCreated: 1693831272 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Resource/ResourceManager.cs b/Assets/TEngine/Runtime/GameFramework/Resource/ResourceManager.cs index 102d26c3..59dae045 100644 --- a/Assets/TEngine/Runtime/GameFramework/Resource/ResourceManager.cs +++ b/Assets/TEngine/Runtime/GameFramework/Resource/ResourceManager.cs @@ -9,9 +9,10 @@ namespace TEngine /// /// 资源管理器。 /// - internal partial class ResourceManager: GameFrameworkModule,IResourceManager + internal partial class ResourceManager : GameFrameworkModule, IResourceManager { #region Propreties + /// /// 资源包名称。 /// @@ -21,21 +22,21 @@ namespace TEngine /// 获取当前资源适用的游戏版本号。 /// public string ApplicableGameVersion => m_ApplicableGameVersion; - + private string m_ApplicableGameVersion; /// /// 获取当前内部资源版本号。 /// public int InternalResourceVersion => m_InternalResourceVersion; - + private int m_InternalResourceVersion; - + /// /// 同时下载的最大数目。 /// public int DownloadingMaxNum { get; set; } - + /// /// 失败重试最大数目。 /// @@ -45,50 +46,56 @@ namespace TEngine /// 获取资源只读区路径。 /// public string ReadOnlyPath => m_ReadOnlyPath; - + private string m_ReadOnlyPath; /// /// 获取资源读写区路径。 /// public string ReadWritePath => m_ReadWritePath; - + private string m_ReadWritePath; - + /// /// 资源系统运行模式。 /// public EPlayMode PlayMode { get; set; } - + /// /// 下载文件校验等级。 /// public EVerifyLevel VerifyLevel { get; set; } - + /// /// 设置异步系统参数,每帧执行消耗的最大时间切片(单位:毫秒)。 /// public long Milliseconds { get; set; } - + /// /// 获取游戏框架模块优先级。 /// /// 优先级较高的模块会优先轮询,并且关闭操作会后进行。 internal override int Priority => 4; + #endregion #region 生命周期 + internal override void Update(float elapseSeconds, float realElapseSeconds) { + ResourcePool.Update(); } internal override void Shutdown() { YooAssets.Destroy(); + ResourcePool.Destroy(); } + #endregion #region 设置接口 + /// /// 设置资源只读区路径。 /// @@ -116,6 +123,7 @@ namespace TEngine m_ReadWritePath = readWritePath; } + #endregion public void Initialize() @@ -133,6 +141,7 @@ namespace TEngine defaultPackage = YooAssets.CreatePackage(packageName); YooAssets.SetDefaultPackage(defaultPackage); } + ResourcePool.Initialize(GameModule.Resource.gameObject); } public InitializationOperation InitPackage() @@ -154,7 +163,7 @@ namespace TEngine //运行时使用。 EPlayMode playMode = PlayMode; #endif - + // 编辑器下的模拟模式 InitializationOperation initializationOperation = null; if (playMode == EPlayMode.EditorSimulateMode) @@ -185,7 +194,7 @@ namespace TEngine return initializationOperation; } - + public void UnloadAsset(object asset) { throw new System.NotImplementedException(); @@ -207,14 +216,14 @@ namespace TEngine { throw new GameFrameworkException("Asset name is invalid."); } - + AssetInfo assetInfo = YooAssets.GetAssetInfo(assetName); - + if (!CheckLocationValid(assetName)) { return HasAssetResult.Valid; } - + if (assetInfo == null) { return HasAssetResult.NotExist; @@ -296,6 +305,7 @@ namespace TEngine } #endregion + /// /// 同步加载资源。 /// @@ -309,6 +319,7 @@ namespace TEngine Log.Error("Asset name is invalid."); return default; } + AssetOperationHandle handle = YooAssets.LoadAssetSync(assetName); if (typeof(T) == typeof(GameObject)) @@ -339,6 +350,7 @@ namespace TEngine Log.Error("Asset name is invalid."); return default; } + AssetOperationHandle handle = YooAssets.LoadAssetSync(assetName); if (typeof(T) == typeof(GameObject)) @@ -362,7 +374,7 @@ namespace TEngine /// 要加载资源的名称。 /// 要加载资源的类型。 /// 资源实例。 - public T LoadAsset(string assetName,out AssetOperationHandle handle) where T : Object + public T LoadAsset(string assetName, out AssetOperationHandle handle) where T : Object { handle = YooAssets.LoadAssetSync(assetName); @@ -391,7 +403,7 @@ namespace TEngine /// 父节点位置。 /// 要加载资源的类型。 /// 资源实例。 - public T LoadAsset(string assetName, Transform parent,out AssetOperationHandle handle) where T : Object + public T LoadAsset(string assetName, Transform parent, out AssetOperationHandle handle) where T : Object { handle = YooAssets.LoadAssetSync(assetName); @@ -453,7 +465,7 @@ namespace TEngine { return YooAssets.LoadSubAssetsAsync(location: location); } - + /// /// 同步加载子资源对象 /// @@ -471,9 +483,10 @@ namespace TEngine /// 加载完毕时是否主动激活 /// 优先级 /// 异步加载场景句柄。 - public SceneOperationHandle LoadSceneAsync(string location, LoadSceneMode sceneMode = LoadSceneMode.Single, bool activateOnLoad = true, int priority = 100) + public SceneOperationHandle LoadSceneAsync(string location, LoadSceneMode sceneMode = LoadSceneMode.Single, bool activateOnLoad = true, + int priority = 100) { - return YooAssets.LoadSceneAsync(location,sceneMode,activateOnLoad,priority); + return YooAssets.LoadSceneAsync(location, sceneMode, activateOnLoad, priority); } /// @@ -484,12 +497,13 @@ namespace TEngine /// 加载完毕时是否主动激活 /// 优先级 /// 异步加载场景句柄。 - public SceneOperationHandle LoadSceneAsync(AssetInfo assetInfo, LoadSceneMode sceneMode = LoadSceneMode.Single, bool activateOnLoad = true, int priority = 100) + public SceneOperationHandle LoadSceneAsync(AssetInfo assetInfo, LoadSceneMode sceneMode = LoadSceneMode.Single, bool activateOnLoad = true, + int priority = 100) { - return YooAssets.LoadSceneAsync(assetInfo,sceneMode,activateOnLoad,priority); + return YooAssets.LoadSceneAsync(assetInfo, sceneMode, activateOnLoad, priority); } - - + + /// /// 异步加载资源实例。 /// @@ -499,20 +513,20 @@ namespace TEngine public async UniTask LoadAssetAsync(string assetName, CancellationToken cancellationToken) where T : Object { AssetOperationHandle handle = LoadAssetAsyncHandle(assetName); - + bool cancelOrFailed = await handle.ToUniTask().AttachExternalCancellation(cancellationToken).SuppressCancellationThrow(); if (cancelOrFailed) { return null; } - + if (typeof(T) == typeof(GameObject)) { GameObject ret = handle.InstantiateSync(); - + AssetReference.BindAssetReference(ret, handle, assetName); - + return ret as T; } else @@ -530,7 +544,7 @@ namespace TEngine public async UniTask LoadGameObjectAsync(string assetName, CancellationToken cancellationToken) { AssetOperationHandle handle = LoadAssetAsyncHandle(assetName); - + bool cancelOrFailed = await handle.ToUniTask().AttachExternalCancellation(cancellationToken).SuppressCancellationThrow(); if (cancelOrFailed) @@ -539,12 +553,12 @@ namespace TEngine } GameObject ret = handle.InstantiateSync(); - + AssetReference.BindAssetReference(ret, handle, assetName); - + return ret; } - + /// /// 异步加载游戏物体。 /// @@ -554,7 +568,7 @@ namespace TEngine /// 异步游戏物体实例。 public async UniTask LoadGameObjectAsync(string location, Transform parent, CancellationToken cancellationToken) { - GameObject gameObject = await LoadGameObjectAsync(location,cancellationToken); + GameObject gameObject = await LoadGameObjectAsync(location, cancellationToken); if (parent != null) { gameObject.transform.SetParent(parent); @@ -563,6 +577,7 @@ namespace TEngine { Log.Error("Set Parent Failed"); } + return gameObject; } @@ -575,11 +590,11 @@ namespace TEngine public async UniTask LoadRawAssetAsync(string location, CancellationToken cancellationToken) { RawFileOperationHandle handle = YooAssets.LoadRawFileAsync(location); - + bool cancelOrFailed = await handle.ToUniTask().AttachExternalCancellation(cancellationToken).SuppressCancellationThrow(); handle.Dispose(); - + return cancelOrFailed ? null : handle; } @@ -591,7 +606,7 @@ namespace TEngine /// 取消操作Token。 /// 资源实例类型。 /// 原生文件资源实例。 - public async UniTask LoadSubAssetAsync(string location,string assetName, CancellationToken cancellationToken) where T : Object + public async UniTask LoadSubAssetAsync(string location, string assetName, CancellationToken cancellationToken) where T : Object { var assetInfo = GetAssetInfo(location); if (assetInfo == null) @@ -599,16 +614,16 @@ namespace TEngine Log.Fatal($"AssetsInfo is null"); return null; } - + SubAssetsOperationHandle handle = YooAssets.LoadSubAssetsAsync(assetInfo); - + bool cancelOrFailed = await handle.ToUniTask().AttachExternalCancellation(cancellationToken).SuppressCancellationThrow(); handle.Dispose(); - + return cancelOrFailed ? null : handle.GetSubAssetObject(assetName); } - + /// /// 异步加载子文件。 /// @@ -616,7 +631,7 @@ namespace TEngine /// 取消操作Token。 /// 资源实例类型。 /// 原生文件资源实例。 - public async UniTask LoadAllSubAssetAsync(string location,CancellationToken cancellationToken) where T : Object + public async UniTask LoadAllSubAssetAsync(string location, CancellationToken cancellationToken) where T : Object { var assetInfo = GetAssetInfo(location); if (assetInfo == null) @@ -624,13 +639,13 @@ namespace TEngine Log.Fatal($"AssetsInfo is null"); return null; } - + SubAssetsOperationHandle handle = YooAssets.LoadSubAssetsAsync(assetInfo); - + bool cancelOrFailed = await handle.ToUniTask().AttachExternalCancellation(cancellationToken).SuppressCancellationThrow(); handle.Dispose(); - + return cancelOrFailed ? null : handle.GetSubAssetObjects(); } @@ -643,11 +658,12 @@ namespace TEngine /// 加载完毕时是否主动激活. /// 优先级. /// 场景资源实例。 - public async UniTask LoadSceneAsyncByUniTask(string location,CancellationToken cancellationToken,LoadSceneMode sceneMode = LoadSceneMode.Single, + public async UniTask LoadSceneAsyncByUniTask(string location, CancellationToken cancellationToken, + LoadSceneMode sceneMode = LoadSceneMode.Single, bool activateOnLoad = true, int priority = 100) { - SceneOperationHandle handle = YooAssets.LoadSceneAsync(location,sceneMode,activateOnLoad,priority); - + SceneOperationHandle handle = YooAssets.LoadSceneAsync(location, sceneMode, activateOnLoad, priority); + bool cancelOrFailed = await handle.ToUniTask().AttachExternalCancellation(cancellationToken).SuppressCancellationThrow(); return cancelOrFailed ? default : handle.SceneObject;