Compare commits

..

13 Commits

Author SHA1 Message Date
ALEXTANG
be6a19c26a 修正Sound轨道不受SoundVolume的影响,调整AudioMixer音频Group命名
修正Sound轨道不受SoundVolume的影响,调整AudioMixer音频Group命名
2023-11-30 13:18:20 +08:00
ALEXTANG
e7f0636f30 拓展细分内存对象生命周期。
拓展细分内存对象生命周期。
2023-11-30 00:13:23 +08:00
ALEXTANG
fb38e96e9e 修正循环列表根据下标获取索引Item
修正循环列表根据下标获取索引Item
2023-11-29 19:48:18 +08:00
ALEXTANG
dd658c7e1d 对象池支持ShutDown
对象池支持ShutDown
2023-11-29 15:39:44 +08:00
ALEXTANG
4d7cb7641d 修正LoadGameObjectAsync接口传参
修正LoadGameObjectAsync接口传参
2023-11-29 15:24:02 +08:00
ALEXTANG
ae075b1fad 修正ProcedureDownloadFile更新时网速计算
修正ProcedureDownloadFile更新时网速计算
2023-11-29 12:55:36 +08:00
ALEXTANG
02827ce3b8 优化/新增超牛逼且很方便使用的对象池。
优化/新增超牛逼且很方便使用的对象池。
2023-11-28 19:36:26 +08:00
ALEXTANG
1a0e3f91e0 优化/新增超牛逼且很方便使用的对象池。
优化/新增超牛逼且很方便使用的对象池。
2023-11-28 15:27:34 +08:00
ALEXTANG
6d376b0e07 增加安全定时器GameTimerTick
增加安全定时器GameTimerTick
2023-11-28 14:25:25 +08:00
ALEXTANGXIAO
cb73c9a9eb 升级HybridCLR 4.0.12=>4.0.13 强烈建议升级,修复了若干bug
升级HybridCLR 4.0.12=>4.0.13 强烈建议升级,修复了若干bug
2023-11-27 22:47:30 +08:00
ALEXTANG
7dda73a7ac 增加泛用加载资源并绑定资源引用到GameObject上。
增加泛用加载资源并绑定资源引用到GameObject上。
2023-11-23 13:34:33 +08:00
ALEXTANG
9bcb636ed7 修正SetSprite接口绑定资源引用关系,避免0引用导致AssetBundle被释放使图片丢失。
修正SetSprite接口绑定资源引用关系,避免0引用导致AssetBundle被释放使图片丢失。
2023-11-23 11:44:43 +08:00
ALEXTANG
bd0cfc5577 增加计时器TimerModule。
增加计时器TimerModule。
2023-11-22 12:20:08 +08:00
47 changed files with 2166 additions and 1121 deletions

View File

@@ -0,0 +1,16 @@
[
{
"ResPath": "Assets/AssetRaw/Effects",
"CacheTime": 300,
"MaxPoolCnt": 30,
"PoolGoFreeTime": 300,
"MinPoolCnt": 0
},
{
"ResPath": "Assets/AssetRaw/PoolObjects/",
"CacheTime": 300,
"MaxPoolCnt": 30,
"PoolGoFreeTime": 300,
"MinPoolCnt": 0
}
]

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: c9f7d9280641e9a4b87697ace96a4315
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -312,9 +312,9 @@ namespace GameLogic
/// <summary>
/// 获取item
/// </summary>
/// <param name="i"></param>
/// <param name="index"></param>
/// <returns></returns>
public virtual ItemT GetItem(int i)
public virtual ItemT GetItem(int index)
{
return null;
}

View File

@@ -148,11 +148,11 @@ namespace GameLogic
/// <summary>
/// 获取item
/// </summary>
/// <param name="i"></param>
/// <param name="index"></param>
/// <returns></returns>
public override TItem GetItem(int i)
public override TItem GetItem(int index)
{
return i >= 0 && i < m_items.Count ? m_items[i] : null;
return index >= 0 && index < m_itemCache.Count ? m_itemCache.GetValueByIndex(index) : null;
}
/// <summary>

View File

@@ -147,11 +147,11 @@ namespace GameLogic
/// <summary>
/// 获取item
/// </summary>
/// <param name="i"></param>
/// <param name="index"></param>
/// <returns></returns>
public override TItem GetItem(int i)
public override TItem GetItem(int index)
{
return i >= 0 && i < m_items.Count ? m_items[i] : null;
return index >= 0 && index < m_itemCache.Count ? m_itemCache.GetValueByIndex(index) : null;
}
/// <summary>

View File

@@ -13,9 +13,18 @@ namespace GameMain
private ProcedureOwner _procedureOwner;
private float CurrentSpeed =>
(GameModule.Resource.Downloader.TotalDownloadBytes -
GameModule.Resource.Downloader.CurrentDownloadBytes) / GameTime.time;
private float _lastUpdateDownloadedSize;
private float CurrentSpeed
{
get
{
float interval = GameTime.deltaTime;
var sizeDiff = GameModule.Resource.Downloader.CurrentDownloadBytes - _lastUpdateDownloadedSize;
_lastUpdateDownloadedSize = GameModule.Resource.Downloader.CurrentDownloadBytes;
var speed = (float)Math.Floor(sizeDiff / interval);
return speed;
}
}
protected override void OnEnter(ProcedureOwner procedureOwner)
{
@@ -63,7 +72,7 @@ namespace GameMain
Utility.File.GetByteLengthString(currentDownloadBytes),
Utility.File.GetByteLengthString(totalDownloadBytes),
GameModule.Resource.Downloader.Progress,
Utility.File.GetByteLengthString((int)CurrentSpeed));
Utility.File.GetLengthString((int)CurrentSpeed));
LoadUpdateLogic.Instance.DownProgressAction?.Invoke(GameModule.Resource.Downloader.Progress);
UILoadMgr.Show(UIDefine.UILoadUpdate,descriptionText);

View File

@@ -153,7 +153,7 @@ Transform:
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 709048975}
m_RootOrder: 10
m_RootOrder: 11
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &450845954
MonoBehaviour:
@@ -381,7 +381,7 @@ Transform:
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 709048975}
m_RootOrder: 6
m_RootOrder: 7
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &683825228
MonoBehaviour:
@@ -441,6 +441,7 @@ Transform:
m_Children:
- {fileID: 964133197}
- {fileID: 1751957488}
- {fileID: 1029867562}
- {fileID: 877336934}
- {fileID: 803382966}
- {fileID: 790894654}
@@ -505,7 +506,7 @@ Transform:
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 709048975}
m_RootOrder: 4
m_RootOrder: 5
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &790894655
MonoBehaviour:
@@ -571,7 +572,7 @@ Transform:
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 709048975}
m_RootOrder: 3
m_RootOrder: 4
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &803382967
MonoBehaviour:
@@ -617,7 +618,7 @@ Transform:
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 709048975}
m_RootOrder: 2
m_RootOrder: 3
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &877336935
MonoBehaviour:
@@ -696,7 +697,7 @@ Transform:
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 709048975}
m_RootOrder: 9
m_RootOrder: 10
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &914171639
MonoBehaviour:
@@ -790,7 +791,7 @@ Transform:
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 709048975}
m_RootOrder: 5
m_RootOrder: 6
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &965975542
MonoBehaviour:
@@ -814,6 +815,50 @@ MonoBehaviour:
downloadingMaxNum: 3
failedTryAgain: 3
adaptiveReplacementCacheCapacity: 32
--- !u!1 &1029867561
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1029867562}
- component: {fileID: 1029867563}
m_Layer: 0
m_Name: Timer
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &1029867562
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1029867561}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 709048975}
m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &1029867563
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1029867561}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5e79c115d5054209810f42dc6e25cf94, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1 &1047779123
GameObject:
m_ObjectHideFlags: 0
@@ -844,7 +889,7 @@ Transform:
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 709048975}
m_RootOrder: 11
m_RootOrder: 12
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &1047779125
MonoBehaviour:
@@ -967,7 +1012,7 @@ Transform:
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 709048975}
m_RootOrder: 7
m_RootOrder: 8
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &1666908678
MonoBehaviour:
@@ -1055,7 +1100,7 @@ Transform:
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 709048975}
m_RootOrder: 8
m_RootOrder: 9
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &1863338242
MonoBehaviour:

View File

@@ -0,0 +1,57 @@
using System;
namespace TEngine
{
/// <summary>
/// 内存池对象基类。
/// </summary>
public abstract class MemoryObject : IMemory
{
/// <summary>
/// 清理内存对象回收入池。
/// </summary>
public virtual void Clear()
{
}
/// <summary>
/// 从内存池中初始化。
/// </summary>
public abstract void InitFromPool();
/// <summary>
/// 回收到内存池。
/// </summary>
public abstract void RecycleToPool();
}
public static partial class MemoryPool
{
/// <summary>
/// 从内存池获取内存对象。
/// </summary>
/// <typeparam name="T">内存对象类型。</typeparam>
/// <returns>内存对象。</returns>
public static T Alloc<T>() where T : MemoryObject, new()
{
T memory = Acquire<T>();
memory.InitFromPool();
return memory;
}
/// <summary>
/// 将内存对象归还内存池。
/// </summary>
/// <param name="memory">内存对象。</param>
public static void Dealloc(MemoryObject memory)
{
if (memory == null)
{
throw new Exception("Memory is invalid.");
}
memory.RecycleToPool();
Release(memory);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ad13ef73c22340058c4420733a22b580
timeCreated: 1701273442

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngineInternal;
@@ -149,12 +150,14 @@ namespace TEngine
{
if (!isAsync)
{
image.sprite =
GameModule.Resource.LoadAsset<Sprite>(spriteName, customPackageName: customPackageName);
var operation = GameModule.Resource.LoadAssetGetOperation<Sprite>(spriteName, customPackageName: customPackageName);
image.sprite = operation.AssetObject as Sprite;
if (isSetNativeSize)
{
image.SetNativeSize();
}
image.gameObject.GetOrAddComponent<AssetReference>().Reference(operation, spriteName);
}
else
{
@@ -162,6 +165,8 @@ namespace TEngine
{
if (image == null)
{
operation.Dispose();
operation = null;
return;
}
@@ -170,6 +175,8 @@ namespace TEngine
{
image.SetNativeSize();
}
image.gameObject.GetOrAddComponent<AssetReference>().Reference(operation, spriteName);
}, customPackageName: customPackageName);
}
}
@@ -198,8 +205,10 @@ namespace TEngine
{
if (!isAsync)
{
spriteRenderer.sprite =
GameModule.Resource.LoadAsset<Sprite>(spriteName, customPackageName: customPackageName);
var operation = GameModule.Resource.LoadAssetGetOperation<Sprite>(spriteName, customPackageName: customPackageName);
spriteRenderer.sprite = operation.AssetObject as Sprite;
spriteRenderer.gameObject.GetOrAddComponent<AssetReference>().Reference(operation, spriteName);
}
else
{
@@ -207,10 +216,13 @@ namespace TEngine
{
if (spriteRenderer == null)
{
operation.Dispose();
operation = null;
return;
}
spriteRenderer.sprite = operation.AssetObject as Sprite;
spriteRenderer.gameObject.GetOrAddComponent<AssetReference>().Reference(operation, spriteName);
}, customPackageName: customPackageName);
}
}
@@ -281,5 +293,72 @@ namespace TEngine
return null;
}
public static AssetReference GetAssetReference(this GameObject gameObject)
{
if (gameObject == null)
{
return null;
}
return gameObject.GetComponent<AssetReference>();
}
/// <summary>
/// 加载资源并绑定资源引用到GameObject上。
/// </summary>
/// <param name="gameObject">GameObject。</param>
/// <param name="location">资源定位地址。</param>
/// <param name="callBack">加载完成回调。</param>
/// <param name="customPackageName">自定义包。</param>
/// <typeparam name="T">资源实例类型。</typeparam>
/// <returns>资源实例。</returns>
public static T LoadAsset<T>(this GameObject gameObject, string location, Action<T> callBack = null,
string customPackageName = "") where T : UnityEngine.Object
{
if (gameObject == null)
{
return null;
}
var operation = GameModule.Resource.LoadAssetGetOperation<T>(location, customPackageName: customPackageName);
var asset = operation.AssetObject as T;
gameObject.GetOrAddComponent<AssetReference>().Reference(operation, location);
callBack?.Invoke(asset);
return asset;
}
/// <summary>
/// 异步加载资源并绑定资源引用到GameObject上。
/// </summary>
/// <param name="gameObject">GameObject。</param>
/// <param name="location">资源定位地址。</param>
/// <param name="callBack">加载完成回调。</param>
/// <param name="customPackageName">自定义包。</param>
/// <typeparam name="T">资源实例类型。</typeparam>
/// <returns>资源实例。</returns>
public static async UniTask<T> LoadAssetAsync<T>(this GameObject gameObject, string location, Action<T> callBack = null,
string customPackageName = "") where T : UnityEngine.Object
{
if (gameObject == null)
{
return null;
}
var operation = GameModule.Resource.LoadAssetAsyncHandle<T>(location, customPackageName: customPackageName);
bool cancelOrFailed = await operation.ToUniTask().AttachExternalCancellation(gameObject.GetCancellationTokenOnDestroy())
.SuppressCancellationThrow();
if (cancelOrFailed)
{
return null;
}
gameObject.GetOrAddComponent<AssetReference>().Reference(operation, location);
var asset = operation.AssetObject as T;
callBack?.Invoke(asset);
return asset;
}
}
}

View File

@@ -196,7 +196,7 @@ namespace TEngine
_source = host.AddComponent<AudioSource>();
_source.playOnAwake = false;
AudioMixerGroup[] audioMixerGroups =
audioCategory.AudioMixer.FindMatchingGroups(Utility.Text.Format("Master/{0}/{1}", audioCategory.AudioMixerGroup.name, index));
audioCategory.AudioMixer.FindMatchingGroups(Utility.Text.Format("Master/{0}/{1}", audioCategory.AudioMixerGroup.name, $"{audioCategory.AudioMixerGroup.name} - {index}"));
_source.outputAudioMixerGroup = audioMixerGroups.Length > 0 ? audioMixerGroups[0] : audioCategory.AudioMixerGroup;
_source.rolloffMode = audioCategory.AudioGroupConfig.audioRolloffMode;
_source.minDistance = audioCategory.AudioGroupConfig.minDistance;

View File

@@ -48,7 +48,7 @@ AudioMixerGroupController:
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: 2
m_Name: Sound - 2
m_AudioMixer: {fileID: 24100000}
m_GroupID: 039cd795affa7134a8d5f5d43d3b659d
m_Children: []
@@ -67,7 +67,7 @@ AudioMixerGroupController:
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: 1
m_Name: Sound - 1
m_AudioMixer: {fileID: 24100000}
m_GroupID: c0d40106c2ffb1a44bd48f50b210ee20
m_Children: []
@@ -100,7 +100,7 @@ AudioMixerGroupController:
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: 3
m_Name: Sound - 3
m_AudioMixer: {fileID: 24100000}
m_GroupID: 5f20d1b8f9ac1914dac8beae718e7d40
m_Children: []
@@ -153,7 +153,7 @@ AudioMixerGroupController:
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: 1
m_Name: UISound - 1
m_AudioMixer: {fileID: 24100000}
m_GroupID: e012b6d2e0501df43a88eb6beff8ae07
m_Children: []
@@ -192,7 +192,7 @@ AudioMixerGroupController:
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: 0
m_Name: Sound - 0
m_AudioMixer: {fileID: 24100000}
m_GroupID: 71c50c6b966d1f548a63193919ebfbad
m_Children: []
@@ -225,7 +225,7 @@ AudioMixerGroupController:
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: 0
m_Name: Voice - 0
m_AudioMixer: {fileID: 24100000}
m_GroupID: f46651e8ad3c6034b8764fd635dda3fd
m_Children: []
@@ -313,25 +313,25 @@ AudioMixerController:
m_UpdateMode: 0
m_ExposedParameters:
- guid: 7835f2c4248cb3e43a1a773bab1f8b9d
name: UISoundVolume
name: SoundVolume0
- guid: 41591fd4a32f4034f880ecbc14ee69f1
name: MusicVolume
name: MusicVolume0
- guid: 6e0d1a5935a802d41b27d9e2fad3ba2f
name: UISoundVolume
name: UISoundVolume0
- guid: 0bc64c1c6cebbeb40ba2f724fdcaa257
name: VoiceVolume
name: VoiceVolume0
- guid: f62a8b3fe89df00409532af739ee4e02
name: SoundVolume
name: SoundVolume1
- guid: 265eaf7c8910ab842a845c7bb5e570c4
name: UISoundVolume
name: UISoundVolume1
- guid: 2a8ce0f3383c3f0468a04fa3fc5e317d
name: SoundVolume
name: SoundVolume2
- guid: e83be6d6c4ae85142a51f584159c4ff6
name: UISoundVolume
name: UISoundVolume2
- guid: e54edf7c1bf7ee44297e65adce5b10b7
name: SoundVolume
name: SoundVolume3
- guid: 2dd26f9dadf160f4bbd77f307c3f4f2e
name: UISoundVolume
name: UISoundVolume3
- guid: ba83e724007d7e9459f157db3a54a741
name: MasterVolume
- guid: 6d4c2b8bc0ef38d44b2fbff2b3298ab4
@@ -409,10 +409,14 @@ AudioMixerSnapshotController:
m_AudioMixer: {fileID: 24100000}
m_SnapshotID: 91dee90f8902c804c9da7728ea355157
m_FloatValues:
b47f0c73299cd9b4fba9896e70683903: 1
ba83e724007d7e9459f157db3a54a741: 0
fe15a1b40c14ea646a13dacb15b6a73b: 0
77212647508232a458ac7d48fb55d037: 1
3bbd22597ed32714eb271cf06b098c63: 0
30975daa872456b41bc18e0277e301e6: 1
6d4c2b8bc0ef38d44b2fbff2b3298ab4: -0.03
8542b6bfd7b7bfc4d9b961ba97edf0d2: 1
m_TransitionOverrides: {}
--- !u!244 &246003612463095956
AudioMixerEffectController:
@@ -464,7 +468,7 @@ AudioMixerGroupController:
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: 0
m_Name: Music - 0
m_AudioMixer: {fileID: 24100000}
m_GroupID: 1cf576bd46399874d9494863d6502d94
m_Children: []
@@ -483,7 +487,7 @@ AudioMixerGroupController:
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: 3
m_Name: UISound - 3
m_AudioMixer: {fileID: 24100000}
m_GroupID: 98657376d4096a947953ee04d82830c1
m_Children: []
@@ -516,7 +520,7 @@ AudioMixerGroupController:
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: 2
m_Name: UISound - 2
m_AudioMixer: {fileID: 24100000}
m_GroupID: e84c25a476798ea43a2f6de217af7dba
m_Children: []
@@ -629,7 +633,7 @@ AudioMixerGroupController:
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: 0
m_Name: UISound - 0
m_AudioMixer: {fileID: 24100000}
m_GroupID: 29257697b1e6be546aa0558e342a15a6
m_Children: []

View File

@@ -99,6 +99,13 @@ namespace TEngine
public static SceneModule Scene => _scene ??= Get<SceneModule>();
private static SceneModule _scene;
/// <summary>
/// 获取计时器模块。
/// </summary>
public static TimerModule Timer => _timer ??= Get<TimerModule>();
private static TimerModule _timer;
#endregion
/// <summary>
@@ -153,6 +160,7 @@ namespace TEngine
_ui = null;
_localization = null;
_scene = null;
_timer = null;
}
}
}

View File

@@ -59,6 +59,12 @@ namespace TEngine
public void Bind(AssetOperationHandle operation, string assetLocation, AssetReference parent = null,
string packageName = "")
{
if (_operationHandle != null)
{
Log.Warning($"rebind AssetReference gameObject.name:{gameObject.name} assetLocation:{assetLocation}");
_operationHandle.Dispose();
_operationHandle = null;
}
_operationHandle = operation;
this._assetLocation = assetLocation;
this._packageName = packageName;

View File

@@ -96,6 +96,14 @@ namespace TEngine
/// <param name="asset">要卸载的资源。</param>
void UnloadAsset(object asset);
/// <summary>
/// 释放游戏物体。
/// </summary>
/// <param name="gameObject">游戏物体。</param>
/// <param name="forceNoPool">强制不入回收池。</param>
/// <param name="delayTime">延迟时间。</param>
void FreeGameObject(GameObject gameObject, bool forceNoPool = false, float delayTime = 0f);
/// <summary>
/// 资源回收(卸载引用计数为零的资源)
/// </summary>
@@ -282,10 +290,11 @@ namespace TEngine
/// <param name="needInstance">是否需要实例化。</param>
/// <param name="needCache">是否需要缓存。</param>
/// <param name="packageName">指定资源包的名称。不传使用默认资源包</param>
/// <param name="parent">资源实例父节点。</param>
/// <typeparam name="T">要加载资源的类型。</typeparam>
/// <returns>异步资源实例。</returns>
UniTask<T> LoadAssetAsync<T>(string location, CancellationToken cancellationToken = default,
bool needInstance = true, bool needCache = false, string packageName = "") where T : Object;
bool needInstance = true, bool needCache = false, string packageName = "", Transform parent = null) where T : Object;
/// <summary>
/// 异步加载游戏物体。

View File

@@ -1,70 +0,0 @@
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;
}
}
/// <summary>
/// 等待异步实例化结束。
/// </summary>
public void WaitForAsyncComplete()
{
if (_handle != null)
{
if (_steps == ESteps.Done)
return;
_handle.WaitForAsyncComplete();
OnUpdate();
}
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 4e1853e7ff624c639fba990079beeee5
timeCreated: 1693831296

View File

@@ -0,0 +1,12 @@
using UnityEngine;
namespace TEngine
{
internal class DelayDestroyGo
{
public GoProperty Property;
public GameObject Asset;
public int HashId;
public float DestroyTime;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7e03aac69e9233b488725725586c612d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,234 +0,0 @@
using System.Collections.Generic;
using UnityEngine;
using YooAsset;
namespace TEngine
{
/// <summary>
/// 游戏物体对象池。
/// </summary>
internal class GameObjectPool
{
private readonly GameObject _root;
private readonly Queue<InstantiateOperation> _cacheOperations;
private readonly bool _dontDestroy;
private readonly int _initCapacity;
private readonly int _maxCapacity;
private readonly float _destroyTime;
private float _lastRestoreRealTime = -1f;
/// <summary>
/// 资源句柄。
/// </summary>
public AssetOperationHandle AssetHandle { private set; get; }
/// <summary>
/// 资源定位地址。
/// </summary>
public string Location { private set; get; }
/// <summary>
/// 内部缓存总数。
/// </summary>
public int CacheCount => _cacheOperations.Count;
/// <summary>
/// 外部使用总数。
/// </summary>
public int SpawnCount { private set; get; } = 0;
/// <summary>
/// 是否常驻不销毁。
/// </summary>
public bool DontDestroy => _dontDestroy;
/// <summary>
/// 游戏物体对象池。
/// </summary>
/// <param name="poolingRoot">对象池根节点。</param>
/// <param name="location">资源定位地址。</param>
/// <param name="dontDestroy">是否常驻不销毁。</param>
/// <param name="initCapacity">初始容量。</param>
/// <param name="maxCapacity">最大容量。</param>
/// <param name="destroyTime">对象池销毁时间。</param>
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<InstantiateOperation>(initCapacity);
}
/// <summary>
/// 创建对象池。
/// </summary>
/// <param name="package">资源包。</param>
public void CreatePool(ResourcePackage package)
{
// 加载游戏对象
AssetHandle = package.LoadAssetAsync<GameObject>(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);
}
}
/// <summary>
/// 销毁游戏对象池。
/// </summary>
public void DestroyPool()
{
// 卸载资源对象
AssetHandle.Release();
AssetHandle = null;
// 销毁游戏对象
Object.Destroy(_root);
_cacheOperations.Clear();
SpawnCount = 0;
}
/// <summary>
/// 查询静默时间内是否可以销毁。
/// </summary>
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;
}
/// <summary>
/// 游戏对象池是否已经销毁。
/// </summary>
public bool IsDestroyed()
{
return AssetHandle == null;
}
/// <summary>
/// 回收操作。
/// </summary>
/// <param name="operation">资源实例化操作句柄。</param>
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);
}
}
/// <summary>
/// 丢弃操作。
/// </summary>
/// <param name="operation">资源实例化操作句柄。</param>
public void Discard(InstantiateOperation operation)
{
if (IsDestroyed())
{
DestroyInstantiateOperation(operation);
return;
}
SpawnCount--;
if (SpawnCount <= 0)
_lastRestoreRealTime = Time.realtimeSinceStartup;
DestroyInstantiateOperation(operation);
}
/// <summary>
/// 获取一个游戏对象。
/// </summary>
/// <param name="parent">父节点位置。</param>
/// <param name="position">位置。</param>
/// <param name="rotation">旋转。</param>
/// <param name="forceClone">是否强制克隆。</param>
/// <param name="userDatas">用户自定义数据。</param>
/// <returns>Spawn操作句柄。</returns>
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);
}
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 3f0c9bab3d1243fda94ec9a08844ceee
timeCreated: 1693831296

View File

@@ -0,0 +1,25 @@
using System.Collections.Generic;
using UnityEngine;
namespace TEngine
{
internal class GoPoolNode
{
public readonly List<GameObject> ListGameObjects = new List<GameObject>();
public int MaxCacheCnt = 10;
public int GoRefCnt;
public int MinCacheCnt;
public int CacheFreeTime;
public float PoolGoRefreshTime;
public bool AddCacheGo(GameObject go)
{
if (ListGameObjects.Count >= MaxCacheCnt)
{
return false;
}
ListGameObjects.Add(go);
return true;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4e42a95f042081047b9105a70252b9f0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,13 @@
using UnityEngine;
namespace TEngine
{
internal struct GoProperty
{
public string ResPath;
public int Layer;
public uint FrameID;
public float FrameTime;
public Vector3 InitScale;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c868a337f95e6ad4e94ee4ed5b0485e9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,38 @@
using UnityEngine;
namespace TEngine
{
/// <summary>
/// 资源缓存数据。
/// </summary>
internal class ResCacheData : IMemory
{
/// <summary>
/// 资源实例。
/// </summary>
public Object Asset;
/// <summary>
/// 缓存刷新时间。
/// </summary>
public float CacheRefreshTime;
/// <summary>
/// 是否自动过期。
/// </summary>
public bool AutoExpire;
/// <summary>
/// 缓存过期时间。
/// </summary>
public int CacheExpireTime;
public void Clear()
{
Asset = null;
CacheRefreshTime = 0f;
AutoExpire = false;
CacheExpireTime = 0;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e6b38fc58dc07ab4da453ee3d4d02c20
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,25 @@
namespace TEngine
{
internal struct ResourceCacheConfig
{
public readonly string ResPath;
public readonly int CacheTime;
public readonly int MaxPoolCnt;
public readonly int PoolGoFreeTime;
public readonly int MinPoolCnt;
public ResourceCacheConfig(
string resPath,
int cacheTime,
int maxPoolCnt,
int poolGoFreeTime,
int minPoolCnt)
{
ResPath = resPath;
CacheTime = cacheTime;
MaxPoolCnt = maxPoolCnt;
PoolGoFreeTime = poolGoFreeTime;
MinPoolCnt = minPoolCnt;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5100cbca9f2a42ecb050957a3552d43a
timeCreated: 1701085446

View File

@@ -0,0 +1,326 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using YooAsset;
using Object = UnityEngine.Object;
namespace TEngine
{
public class ResourceCacheMgr
{
private static ResourceCacheMgr _instance;
public static ResourceCacheMgr Instance => _instance ??= new ResourceCacheMgr();
private readonly Dictionary<string, ResCacheData> _cachePool = new Dictionary<string, ResCacheData>();
private readonly Dictionary<string, ResCacheData> _persistCachePool = new Dictionary<string, ResCacheData>();
private bool _enableLog = true;
private readonly List<ResourceCacheConfig> _needCacheResList = new List<ResourceCacheConfig>();
private readonly List<string> _needPersistResList = new List<string>();
private GameTimerTick _tickCheckExpire;
private readonly List<string> _listToDel = new List<string>();
private bool _pauseCache;
public int PersistCachePoolCount = 30;
public bool PauseCache
{
set => _pauseCache = value;
get => _pauseCache;
}
internal Dictionary<string, ResCacheData> CachePoolAllData => _cachePool;
internal Dictionary<string, ResCacheData> PersistPoolAllData => _persistCachePool;
public int CacheCount => _cachePool.Count;
public int PersistCount => _persistCachePool.Count;
public void SetLogEnable(bool enable) => _enableLog = enable;
/// <summary>
/// 注册持久化的资源。
/// </summary>
/// <param name="resList">持久化的资源起始路径。存在这个路径开始的资源不会释放。</param>
public void RegPersistResPath(List<string> resList) => _needPersistResList.AddRange((IEnumerable<string>)resList);
public void InitDefaultCachePool()
{
if (!ResourceManager.EnableGoPool)
{
return;
}
string assetLocation = "need_cache_list";
GameModule.Resource.LoadAssetAsync<TextAsset>(assetLocation, handle =>
{
if (handle.AssetObject == null)
{
return;
}
TextAsset textAsset = handle.AssetObject as TextAsset;
if (textAsset == null)
{
return;
}
List<ResourceCacheConfig> list = Utility.Json.ToObject<List<ResourceCacheConfig>>(textAsset.text);
foreach (var config in list)
{
Instance.RegCacheResPath(config.ResPath, config.CacheTime, config.MaxPoolCnt, config.PoolGoFreeTime, config.MinPoolCnt);
}
});
}
/// <summary>
/// 注册缓存池的资源。
/// </summary>
/// <param name="resPath">缓存池的资源起始路径。存在这个路径开始的资源则加入对象池。</param>
/// <param name="cacheTime">缓存时间。</param>
/// <param name="maxPoolCnt">缓存池最大容量。</param>
/// <param name="poolGoFreeTime">缓存池释放时间。</param>
/// <param name="minPoolCnt">缓存池最小容量。</param>
/// <remarks> ResourceCacheMgr.Instance.RegCacheResPath("Assets/AssetRaw/Effects"); 所有特效相关资源都加入对象池。 </remarks>
public void RegCacheResPath(
string resPath,
int cacheTime = 0,
int maxPoolCnt = 30,
int poolGoFreeTime = 120,
int minPoolCnt = 0)
{
if (_enableLog)
{
Log.Warning($"RegCacheResPath: {resPath} cacheTime: {cacheTime} maxPoolCnt: {maxPoolCnt} poolGoFreeTime: {poolGoFreeTime} minPoolCnt: {minPoolCnt}");
}
_needCacheResList.Add(new ResourceCacheConfig(resPath, cacheTime, maxPoolCnt, poolGoFreeTime, minPoolCnt));
}
public void RefreshCacheTime(string resPath)
{
if (!_cachePool.TryGetValue(resPath, out ResCacheData resCacheData))
{
return;
}
resCacheData.CacheRefreshTime = Time.time;
}
public bool IsResourceCached(string resPath)
{
string key = resPath;
return _cachePool.ContainsKey(key) || _persistCachePool.ContainsKey(key);
}
public Object GetCacheDataByLocation(string location)
{
AssetInfo assetInfo = GameModule.Resource.GetAssetInfo(location);
return GetCacheData(assetInfo.AssetPath);
}
public Object GetCacheData(string resPath)
{
string key = resPath;
if (_cachePool.TryGetValue(key, out ResCacheData resCacheData))
{
resCacheData.CacheRefreshTime = Time.time;
return resCacheData.Asset;
}
else if (_persistCachePool.TryGetValue(key, out resCacheData))
{
resCacheData.CacheRefreshTime = Time.time;
return resCacheData.Asset;
}
return null;
}
public int GetMaxGoPoolCnt(string resPath)
{
return IsNeedCache(resPath, out int _, out var maxPoolCnt) ? maxPoolCnt : 0;
}
public bool GetCacheCfg(
string resPath,
out int maxPoolCnt,
out int cacheFreeTime,
out int minPoolCnt)
{
return IsNeedCache(resPath, out int _, out maxPoolCnt, out cacheFreeTime, out minPoolCnt);
}
public bool IsNeedCache(string resPath, out int cacheTime)
{
return IsNeedCache(resPath, out cacheTime, out _);
}
public bool IsNeedCache(string resPath, out int cacheTime, out int maxPoolCnt)
{
return IsNeedCache(resPath, out cacheTime, out maxPoolCnt, out _, out _);
}
public bool IsNeedCache(
string resPath,
out int cacheTime,
out int maxPoolCnt,
out int poolGoFreeTime,
out int minPoolCnt)
{
cacheTime = 0;
maxPoolCnt = 0;
poolGoFreeTime = 0;
minPoolCnt = 0;
foreach (var needCacheRes in _needCacheResList)
{
if (resPath.StartsWith(needCacheRes.ResPath))
{
cacheTime = needCacheRes.CacheTime;
maxPoolCnt = needCacheRes.MaxPoolCnt;
poolGoFreeTime = needCacheRes.PoolGoFreeTime;
minPoolCnt = needCacheRes.MinPoolCnt;
return true;
}
}
if (!IsNeedPersist(resPath))
{
return false;
}
cacheTime = 0;
maxPoolCnt = PersistCachePoolCount;
poolGoFreeTime = 0;
minPoolCnt = 0;
return true;
}
public bool IsNeedPersist(string resPath)
{
foreach (var persistKey in _needPersistResList)
{
if (resPath.IndexOf(persistKey, StringComparison.Ordinal) >= 0)
{
return true;
}
}
return false;
}
public List<string> GetAllPersistCache()
{
List<string> allPersistCache = new List<string>();
using Dictionary<string, ResCacheData>.Enumerator enumerator = _persistCachePool.GetEnumerator();
while (enumerator.MoveNext())
{
allPersistCache.Add(enumerator.Current.Key);
}
return allPersistCache;
}
public bool AddCache(
string resPath,
Object obj,
int cacheTime,
bool forcePersist = false)
{
if (null == obj)
{
Log.Info("add cache failed, resPath: {0}", resPath);
return false;
}
bool flag = IsNeedPersist(resPath) || forcePersist;
string str = resPath;
if (_persistCachePool.ContainsKey(str))
{
return true;
}
if (_cachePool.TryGetValue(str, out ResCacheData resCacheData1))
{
if (flag)
{
_cachePool.Remove(str);
_persistCachePool.Add(str, resCacheData1);
}
return true;
}
if (PauseCache)
{
return true;
}
ResCacheData resCacheData2 = MemoryPool.Acquire<ResCacheData>();
resCacheData2.Asset = obj;
resCacheData2.AutoExpire = false;
resCacheData2.CacheRefreshTime = Time.time;
resCacheData2.CacheExpireTime = cacheTime;
if (cacheTime > 0)
{
resCacheData2.AutoExpire = true;
}
if (flag)
{
_persistCachePool.Add(str, resCacheData2);
}
else
{
_cachePool.Add(str, resCacheData2);
}
return true;
}
public void RemoveAllCache() => _cachePool.Clear();
public void RemoveCache(string resPath)
{
if (!_cachePool.TryGetValue(resPath,out ResCacheData resCacheData))
{
return;
}
MemoryPool.Release(resCacheData);
_cachePool.Remove(resPath);
}
public int GetCacheCount() => _cachePool.Count + _persistCachePool.Count;
internal void Init() => _tickCheckExpire = new GameTimerTick(1f,CheckExpireCache);
internal void OnUpdate() => _tickCheckExpire.OnUpdate();
private void CheckExpireCache()
{
float time = Time.time;
ResourcePool instance = ResourcePool.Instance;
using Dictionary<string, ResCacheData>.Enumerator enumerator = _cachePool.GetEnumerator();
while (enumerator.MoveNext())
{
KeyValuePair<string, ResCacheData> current = enumerator.Current;
string key1 = current.Key;
current = enumerator.Current;
ResCacheData resCacheData = current.Value;
if (resCacheData.AutoExpire && resCacheData.CacheRefreshTime + resCacheData.CacheExpireTime < time)
{
if (resCacheData.Asset is GameObject && !instance.IsNeedAutoFree(key1))
{
resCacheData.CacheRefreshTime = Time.time;
}
else
{
List<string> listToDel = _listToDel;
current = enumerator.Current;
string key2 = current.Key;
listToDel.Add(key2);
}
}
}
foreach (var resPath in _listToDel)
{
MemoryPool.Release(_cachePool[resPath]);
_cachePool.Remove(resPath);
ResourcePool.Instance.FreeGoByResPath(resPath);
}
_listToDel.Clear();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8e211ca9bbc0ef74ca63508c6b10c380
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,261 +1,521 @@
using System;
#region Class Documentation
/************************************************************************************************************
Class Name: ResourcePool对象池。
Type: Util, Singleton
Example:
//注册 - 添加对象池路径满足这个路径开头的GameObject开启对象池
ResourceCacheMgr.Instance.RegCacheResPath("Assets/AssetRaw/Effects");
//正常引用资源。
var obj = await GameModule.Resource.LoadAssetAsync<GameObject>("Sprite",parent:transform);
//回收资源。
GameModule.Resource.FreeGameObject(obj);
//删除资源 放心资源不存在泄露。
Unity Engine.Object。Destroy(obj);
************************************************************************************************************/
#endregion
using System.Collections.Generic;
using UnityEngine;
using YooAsset;
namespace TEngine
{
/// <summary>
/// 游戏对象池系统。
/// <remarks>用法 SpawnHandle handle = ResourcePool.SpawnAsync("Cube");</remarks>
/// </summary>
public static class ResourcePool
internal class ResourcePool
{
private static bool _isInitialize = false;
private static readonly List<Spawner> Spawners = new List<Spawner>();
private static GameObject _root;
private static Spawner _defaultSpawner = null;
private static ResourcePool _instance;
/// <summary>
/// 默认Package对象生成器。
/// </summary>
private static Spawner DefaultSpawner => _defaultSpawner ??= CreateSpawner();
internal static ResourcePool Instance => _instance ??= new ResourcePool();
/// <summary>
/// 初始化游戏对象池系统
/// </summary>
internal static void Initialize(GameObject root)
private readonly Dictionary<string, GoPoolNode> _cacheGo = new Dictionary<string, GoPoolNode>();
private readonly Dictionary<int, GoProperty> _goProperty = new Dictionary<int, GoProperty>();
private readonly List<DelayDestroyGo> _delayDestroyList = new List<DelayDestroyGo>();
private readonly List<DelayDestroyGo> _freeDelayNode = new List<DelayDestroyGo>();
private readonly List<string> _listToDelete = new List<string>();
private Transform _poolRootTrans;
private uint _frameID = 1;
private float _frameTime;
private float _tickLogTime;
private bool _pauseGoPool;
private int _totalPoolObjectCount;
public bool LogWhenPoolFull = true;
public bool PoolCacheFreeEnable = true;
public int TotalPoolObjectCount => _totalPoolObjectCount;
public int DelayDestroyCount => _delayDestroyList.Count;
public int FreedDestroyCount => _freeDelayNode.Count;
public static float PoolWaitReuseTime = 0.2f;
public bool PauseGoPool
{
if (_isInitialize)
throw new Exception($"{nameof(ResourcePool)} is initialized !");
if (_isInitialize == false)
{
_root = root;
_isInitialize = true;
Log.Info($"{nameof(ResourcePool)} Initialize !");
}
set => _pauseGoPool = value;
get => _pauseGoPool;
}
/// <summary>
/// 销毁游戏对象池系统
/// </summary>
internal static void Destroy()
public void OnAwake()
{
if (_isInitialize)
{
foreach (var spawner in Spawners)
{
spawner.Destroy();
GetRootTrans();
ResourceCacheMgr.Instance.Init();
}
Spawners.Clear();
_isInitialize = false;
Log.Info($"{nameof(ResourcePool)} destroy all !");
}
}
/// <summary>
/// 更新游戏对象池系统
/// </summary>
internal static void Update()
private Transform GetRootTrans()
{
if (_isInitialize)
if (!Application.isPlaying)
{
foreach (var spawner in Spawners)
{
spawner.Update();
}
}
}
/// <summary>
/// 创建游戏对象生成器
/// </summary>
/// <param name="packageName">资源包名称</param>
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;
}
/// <summary>
/// 获取游戏对象生成器。
/// </summary>
/// <param name="packageName">资源包名称。</param>
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;
}
/// <summary>
/// 检测游戏对象生成器是否存在。
/// </summary>
/// <param name="packageName">资源包名称。</param>
public static bool HasSpawner(string packageName = "")
if (_poolRootTrans != null)
{
if (string.IsNullOrEmpty(packageName))
{
packageName = GameModule.Resource.packageName;
return _poolRootTrans;
}
foreach (var spawner in Spawners)
{
if (spawner.PackageName == packageName)
return true;
GameObject target = new GameObject("_GO_POOL_ROOT");
Object.DontDestroyOnLoad(target);
_poolRootTrans = target.transform;
return _poolRootTrans;
}
public void OnDestroy()
{
FreeAllCacheAndGo(needDestroy: false);
}
public void AddCacheGo(string resPath, GameObject go)
{
GoProperty property;
property.ResPath = resPath;
property.Layer = go.layer;
property.FrameID = _frameID;
property.FrameTime = _frameTime;
property.InitScale = go.transform.localScale;
AddCacheGo(resPath, go, property);
}
private DelayDestroyGo AllocDelayNode()
{
if (_freeDelayNode.Count <= 0)
{
return new DelayDestroyGo();
}
int index = _freeDelayNode.Count - 1;
DelayDestroyGo delayDestroyGo = _freeDelayNode[index];
_freeDelayNode.RemoveAt(index);
return delayDestroyGo;
}
private void FreeDelayNode(DelayDestroyGo node) => _freeDelayNode.Add(node);
public void DelayDestroy(GameObject go, GoProperty property, float delayTime)
{
if (delayTime <= 1.0 / 1000.0)
{
AddCacheGo(property.ResPath, go, property);
}
else
{
DelayDestroyGo delayDestroyGo = AllocDelayNode();
delayDestroyGo.Asset = go;
delayDestroyGo.HashId = go.GetHashCode();
delayDestroyGo.Property = property;
float num = Time.time + delayTime;
delayDestroyGo.DestroyTime = num;
int index1 = -1;
for (int index2 = 0; index2 < _delayDestroyList.Count; ++index2)
{
if (_delayDestroyList[index2].DestroyTime >= num)
{
index1 = index2;
break;
}
}
if (index1 >= 0)
{
_delayDestroyList.Insert(index1, delayDestroyGo);
}
else
{
_delayDestroyList.Add(delayDestroyGo);
}
}
}
public bool AddCacheGo(string resPath, GameObject go, GoProperty property)
{
if (_poolRootTrans == null)
{
DoDestroy(go);
return false;
}
#region
/// <summary>
/// 销毁所有对象池及其资源。
/// </summary>
/// <param name="includeAll">销毁所有对象池,包括常驻对象池。</param>
public static void DestroyAll(bool includeAll)
go.SetActive(false);
GoPoolNode orNewResourceNode = GetOrNewResourceNode(resPath);
if (!orNewResourceNode.AddCacheGo(go))
{
DefaultSpawner.DestroyAll(includeAll);
if (LogWhenPoolFull)
{
Log.Info("cache is full, ResPath[{0}] Max cache count:{1}", resPath, orNewResourceNode.MaxCacheCnt);
}
/// <summary>
/// 异步创建指定资源的游戏对象池。
/// </summary>
/// <param name="location">资源定位地址。</param>
/// <param name="dontDestroy">资源常驻不销毁。</param>
/// <param name="initCapacity">对象池的初始容量。</param>
/// <param name="maxCapacity">对象池的最大容量。</param>
/// <param name="destroyTime">静默销毁时间(注意:小于零代表不主动销毁)。</param>
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);
DoDestroy(go);
return false;
}
/// <summary>
/// 同步创建指定资源的游戏对象池。
/// </summary>
/// <param name="location">资源定位地址。</param>
/// <param name="dontDestroy">资源常驻不销毁。</param>
/// <param name="initCapacity">对象池的初始容量。</param>
/// <param name="maxCapacity">对象池的最大容量。</param>
/// <param name="destroyTime">静默销毁时间(注意:小于零代表不主动销毁)。</param>
public static CreatePoolOperation CreateGameObjectPoolSync(string location, bool dontDestroy = false, int initCapacity = 0, int maxCapacity = int.MaxValue,
float destroyTime = -1f)
go.transform.SetParent(GetRootTrans(), false);
int hashCode = go.GetHashCode();
property.FrameID = _frameID;
property.FrameTime = _frameTime;
AddGoProperty(hashCode, property);
++_totalPoolObjectCount;
if (orNewResourceNode.CacheFreeTime != 0)
{
return DefaultSpawner.CreateGameObjectPoolSync(location, dontDestroy, initCapacity, maxCapacity, destroyTime);
orNewResourceNode.PoolGoRefreshTime = _frameTime;
}
/// <summary>
/// 异步实例化一个游戏对象。
/// </summary>
/// <param name="location">资源定位地址。</param>
/// <param name="forceClone">强制克隆游戏对象,忽略缓存池里的对象。</param>
/// <param name="userDatas">用户自定义数据。</param>
public static SpawnHandle SpawnAsync(string location, bool forceClone = false, params System.Object[] userDatas)
{
return DefaultSpawner.SpawnAsync(location, null, Vector3.zero, Quaternion.identity, forceClone, userDatas);
return true;
}
/// <summary>
/// 异步实例化一个游戏对象。
/// </summary>
/// <param name="location">资源定位地址。</param>
/// <param name="parent">父物体。</param>
/// <param name="forceClone">强制克隆游戏对象,忽略缓存池里的对象。</param>
/// <param name="userDatas">用户自定义数据。</param>
public static SpawnHandle SpawnAsync(string location, Transform parent, bool forceClone = false, params System.Object[] userDatas)
public void AddNewRecycleProperty(GameObject go, string resPath, Vector3 initScale)
{
return DefaultSpawner.SpawnAsync(location, parent, Vector3.zero, Quaternion.identity, forceClone, userDatas);
if (PauseGoPool)
{
return;
}
/// <summary>
/// 异步实例化一个游戏对象。
/// </summary>
/// <param name="location">资源定位地址。</param>
/// <param name="parent">父物体。</param>
/// <param name="position">世界坐标。</param>
/// <param name="rotation">世界角度。</param>
/// <param name="forceClone">强制克隆游戏对象,忽略缓存池里的对象。</param>
/// <param name="userDatas">用户自定义数据。</param>
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);
GoProperty property;
property.ResPath = resPath;
property.Layer = go.layer;
property.FrameID = _frameID;
property.FrameTime = _frameTime;
property.InitScale = initScale;
AddGoProperty(go.GetHashCode(), property);
}
/// <summary>
/// 同步实例化一个游戏对象。
/// </summary>
/// <param name="location">资源定位地址。</param>
/// <param name="forceClone">强制克隆游戏对象,忽略缓存池里的对象。</param>
/// <param name="userDatas">用户自定义数据。</param>
public static SpawnHandle SpawnSync(string location, bool forceClone = false, params System.Object[] userDatas)
public GameObject AllocCacheGoByLocation(
string location,
Transform parentTrans = null,
bool haveLocalPos = false,
Vector3 localPos = default,
Quaternion localRot = default,
bool initEnable = true)
{
return DefaultSpawner.SpawnSync(location, null, Vector3.zero, Quaternion.identity, forceClone, userDatas);
AssetInfo assetInfo = GameModule.Resource.GetAssetInfo(location);
return AllocCacheGo(assetInfo.AssetPath, parentTrans, haveLocalPos, localPos, localRot, initEnable);
}
/// <summary>
/// 同步实例化一个游戏对象。
/// </summary>
/// <param name="location">资源定位地址。</param>
/// <param name="parent">父物体</param>
/// <param name="forceClone">强制克隆游戏对象,忽略缓存池里的对象。</param>
/// <param name="userDatas">用户自定义数据。</param>
public static SpawnHandle SpawnSync(string location, Transform parent, bool forceClone = false, params System.Object[] userDatas)
public GameObject AllocCacheGo(
string resPath,
Transform parentTrans = null,
bool haveLocalPos = false,
Vector3 localPos = default,
Quaternion localRot = default,
bool initEnable = true)
{
return DefaultSpawner.SpawnAsync(location, parent, forceClone, userDatas);
if (_cacheGo.TryGetValue(resPath, out GoPoolNode goPoolNode))
{
List<GameObject> listGo = goPoolNode.ListGameObjects;
ResourceCacheMgr.Instance.GetCacheData(resPath);
for (int index = 0; index < listGo.Count; ++index)
{
GameObject gameObject = listGo[index];
if (gameObject == null)
{
--_totalPoolObjectCount;
listGo[index] = listGo[^1];
listGo.RemoveAt(listGo.Count - 1);
--index;
}
else
{
int hashCode = gameObject.GetHashCode();
if (!_goProperty.TryGetValue(hashCode, out GoProperty goProperty))
{
--_totalPoolObjectCount;
Log.Warning("AllocCacheGo Find property failed, bug [{0}]", gameObject.name);
listGo[index] = listGo[^1];
listGo.RemoveAt(listGo.Count - 1);
--index;
}
else if (goProperty.FrameTime > _frameTime || goProperty.FrameTime + PoolWaitReuseTime < _frameTime)
{
gameObject.transform.SetParent(null);
gameObject.transform.localScale = goProperty.InitScale;
if (PauseGoPool)
{
RemoveGoProperty(hashCode);
}
/// <summary>
/// 同步实例化一个游戏对象。
/// </summary>
/// <param name="location">资源定位地址。</param>
/// <param name="parent">父物体。</param>
/// <param name="position">世界坐标。</param>
/// <param name="rotation">世界角度。</param>
/// <param name="forceClone">强制克隆游戏对象,忽略缓存池里的对象。</param>
/// <param name="userDatas">用户自定义数据。</param>
public static SpawnHandle SpawnSync(string location, Transform parent, Vector3 position, Quaternion rotation, bool forceClone = false, params System.Object[] userDatas)
Transform transform = gameObject.transform;
transform.SetParent(parentTrans);
gameObject.layer = goProperty.Layer;
if (haveLocalPos)
{
return DefaultSpawner.SpawnSync(location, parent, position, rotation, forceClone, userDatas);
transform.localPosition = localPos;
transform.localRotation = localRot;
}
#endregion
gameObject.SetActive(initEnable);
listGo[index] = listGo[^1];
listGo.RemoveAt(listGo.Count - 1);
--_totalPoolObjectCount;
return gameObject;
}
}
}
}
return null;
}
public static void FreeAllCacheAndGo(bool needDestroy = true)
{
ResourcePool.Instance.FreeAllCacheGo(needDestroy);
ResourceCacheMgr.Instance.RemoveAllCache();
}
public static void FreeAllPoolGo()
{
Instance.FreeAllCacheGo();
}
public void FreeAllCacheGo(bool needDestroy = true)
{
using Dictionary<string, GoPoolNode>.Enumerator enumerator = _cacheGo.GetEnumerator();
while (enumerator.MoveNext())
{
List<GameObject> listGo = enumerator.Current.Value.ListGameObjects;
for (int index = 0; index < listGo.Count; ++index)
{
GameObject go = listGo[index];
if (go != null)
{
if (needDestroy)
{
go.transform.SetParent(null, false);
DoDestroy(go, true);
}
}
}
listGo.Clear();
}
_cacheGo.Clear();
_goProperty.Clear();
_totalPoolObjectCount = 0;
}
private GoPoolNode GetOrNewResourceNode(string resPath)
{
if (!_cacheGo.TryGetValue(resPath, out GoPoolNode orNewResourceNode))
{
orNewResourceNode = new GoPoolNode();
ResourceCacheMgr.Instance.GetCacheCfg(resPath, out orNewResourceNode.MaxCacheCnt, out orNewResourceNode.CacheFreeTime,
out orNewResourceNode.MinCacheCnt);
_cacheGo.Add(resPath, orNewResourceNode);
}
return orNewResourceNode;
}
private void AddGoProperty(int hashCode, GoProperty property)
{
if (!_goProperty.ContainsKey(hashCode))
++GetOrNewResourceNode(property.ResPath).GoRefCnt;
_goProperty[hashCode] = property;
}
private void RemoveGoProperty(int hashCode)
{
if (!_goProperty.TryGetValue(hashCode, out GoProperty goProperty))
{
return;
}
--GetOrNewResourceNode(goProperty.ResPath).GoRefCnt;
_goProperty.Remove(hashCode);
}
private void DoDestroy(GameObject go, bool needDestroy = true)
{
RemoveGoProperty(go.GetHashCode());
if (needDestroy)
{
Object.Destroy(go);
}
}
public bool IsNeedAutoFree(string resPath)
{
return !_cacheGo.TryGetValue(resPath, out GoPoolNode goPoolNode) || goPoolNode.GoRefCnt <= goPoolNode.ListGameObjects.Count;
}
public void FreeGoByResPath(string resPath)
{
if (!_cacheGo.TryGetValue(resPath, out GoPoolNode goPoolNode))
{
return;
}
List<GameObject> listGo = goPoolNode.ListGameObjects;
Log.Assert(goPoolNode.GoRefCnt <= listGo.Count);
foreach (var go in listGo)
{
if (!(go == null))
{
DoDestroy(go);
}
}
listGo.Clear();
_cacheGo.Remove(resPath);
}
public bool IsExistInCache(string resPath)
{
return _cacheGo.TryGetValue(resPath, out GoPoolNode goPoolNode) && goPoolNode.GoRefCnt > 0;
}
public bool IsNeedRecycle(GameObject go, out GoProperty property, bool forceNoPool)
{
int hashCode = go.GetHashCode();
if (!_goProperty.TryGetValue(hashCode, out property))
return false;
if (PauseGoPool)
{
RemoveGoProperty(hashCode);
return false;
}
if (!ResourceCacheMgr.Instance.IsResourceCached(property.ResPath))
{
RemoveGoProperty(hashCode);
return false;
}
if (!forceNoPool)
{
return true;
}
RemoveGoProperty(hashCode);
return false;
}
public void ClearAllDelayDestroy()
{
List<DelayDestroyGo> delayDestroyList = _delayDestroyList;
for (int index = 0; index < delayDestroyList.Count; ++index)
{
DelayDestroyGo node = delayDestroyList[index];
AddCacheGo(node.Property.ResPath, node.Asset, node.Property);
FreeDelayNode(node);
}
delayDestroyList.Clear();
_freeDelayNode.Clear();
}
public void CheckPoolCacheFree()
{
if (!PoolCacheFreeEnable)
{
return;
}
using Dictionary<string, GoPoolNode>.Enumerator enumerator = _cacheGo.GetEnumerator();
while (enumerator.MoveNext())
{
GoPoolNode goPoolNode = enumerator.Current.Value;
string key = enumerator.Current.Key;
if (goPoolNode.CacheFreeTime != 0 && goPoolNode.CacheFreeTime + goPoolNode.PoolGoRefreshTime < _frameTime)
{
List<GameObject> listGo = goPoolNode.ListGameObjects;
for (int index = 0; index < listGo.Count; ++index)
{
GameObject go = listGo[index];
if (go != null)
{
go.transform.SetParent(null, false);
DoDestroy(go);
}
}
listGo.Clear();
_listToDelete.Add(key);
}
}
foreach (var location in _listToDelete)
{
_cacheGo.Remove(location);
}
if (_listToDelete.Count <= 0)
{
return;
}
_listToDelete.Clear();
}
public void OnUpdate()
{
float time = Time.time;
int num = -1;
for (int index = 0; index < _delayDestroyList.Count; ++index)
{
DelayDestroyGo delayDestroy = _delayDestroyList[index];
if (delayDestroy.DestroyTime <= time)
{
num = index;
if (delayDestroy.Asset == null)
{
Log.Warning("delay destroy gameobject is freed: {0}", delayDestroy.Property.ResPath);
RemoveGoProperty(delayDestroy.HashId);
}
else
{
AddCacheGo(delayDestroy.Property.ResPath, delayDestroy.Asset, delayDestroy.Property);
}
{
FreeDelayNode(delayDestroy);
}
}
else
{
break;
}
}
if (num >= 0)
{
_delayDestroyList.RemoveRange(0, num + 1);
}
CheckPoolCacheFree();
LateUpdate();
ResourceCacheMgr.Instance.OnUpdate();
}
private void LateUpdate()
{
++_frameID;
_frameTime = Time.time;
}
}
}

View File

@@ -1,3 +1,11 @@
fileFormatVersion: 2
guid: be208ee3e46e4243b9a68d4c7212015e
timeCreated: 1693831058
guid: 94ab30a1ade3e8f4ca3656bc73c1892c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,152 +0,0 @@
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;
/// <summary>
/// 实例化的游戏对象。
/// </summary>
public GameObject GameObj
{
get
{
if (_operation == null)
{
Log.Warning("The spawn handle is invalid !");
return null;
}
return _operation.Result;
}
}
/// <summary>
/// 用户自定义数据集。
/// </summary>
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;
}
}
/// <summary>
/// 回收对象。
/// </summary>
public void Restore()
{
if (_operation != null)
{
ClearCompletedCallback();
CancelHandle();
_pool.Restore(_operation);
_operation = null;
}
}
/// <summary>
/// 丢弃对象。
/// </summary>
public void Discard()
{
if (_operation != null)
{
ClearCompletedCallback();
CancelHandle();
_pool.Discard(_operation);
_operation = null;
}
}
/// <summary>
/// 等待异步实例化结束。
/// </summary>
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 !";
}
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: eb2cdce8eb814f7c92c74199d065688a
timeCreated: 1693831296

View File

@@ -1,262 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using YooAsset;
namespace TEngine
{
/// <summary>
/// 对象生成器。
/// </summary>
public class Spawner
{
private readonly List<GameObjectPool> _gameObjectPools = new List<GameObjectPool>(100);
private readonly List<GameObjectPool> _removeList = new List<GameObjectPool>(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;
}
/// <summary>
/// 更新游戏对象池系统。
/// </summary>
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();
}
}
/// <summary>
/// 销毁游戏对象池系统。
/// </summary>
internal void Destroy()
{
DestroyAll(true);
}
/// <summary>
/// 销毁所有对象池及其资源。
/// </summary>
/// <param name="includeAll">销毁所有对象池,包括常驻对象池。</param>
public void DestroyAll(bool includeAll)
{
if (includeAll)
{
foreach (var pool in _gameObjectPools)
{
pool.DestroyPool();
}
_gameObjectPools.Clear();
}
else
{
List<GameObjectPool> removeList = new List<GameObjectPool>();
foreach (var pool in _gameObjectPools)
{
if (pool.DontDestroy == false)
removeList.Add(pool);
}
foreach (var pool in removeList)
{
_gameObjectPools.Remove(pool);
pool.DestroyPool();
}
}
}
/// <summary>
/// 异步创建指定资源的游戏对象池。
/// </summary>
/// <param name="location">资源定位地址。</param>
/// <param name="dontDestroy">资源常驻不销毁。</param>
/// <param name="initCapacity">对象池的初始容量。</param>
/// <param name="maxCapacity">对象池的最大容量。</param>
/// <param name="destroyTime">静默销毁时间(注意:小于零代表不主动销毁)。</param>
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);
}
/// <summary>
/// 同步创建指定资源的游戏对象池。
/// </summary>
/// <param name="location">资源定位地址。</param>
/// <param name="dontDestroy">资源常驻不销毁。</param>
/// <param name="initCapacity">对象池的初始容量。</param>
/// <param name="maxCapacity">对象池的最大容量。</param>
/// <param name="destroyTime">静默销毁时间(注意:小于零代表不主动销毁)。</param>
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;
}
/// <summary>
/// 创建指定资源的游戏对象池。
/// </summary>
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;
}
}
/// <summary>
/// 异步实例化一个游戏对象。
/// </summary>
/// <param name="location">资源定位地址。</param>
/// <param name="forceClone">强制克隆游戏对象,忽略缓存池里的对象。</param>
/// <param name="userDatas">用户自定义数据。</param>
public SpawnHandle SpawnAsync(string location, bool forceClone = false, params System.Object[] userDatas)
{
return SpawnInternal(location, null, Vector3.zero, Quaternion.identity, forceClone, userDatas);
}
/// <summary>
/// 异步实例化一个游戏对象。
/// </summary>
/// <param name="location">资源定位地址。</param>
/// <param name="parent">父物体。</param>
/// <param name="forceClone">强制克隆游戏对象,忽略缓存池里的对象。</param>
/// <param name="userDatas">用户自定义数据。</param>
public SpawnHandle SpawnAsync(string location, Transform parent, bool forceClone = false, params System.Object[] userDatas)
{
return SpawnInternal(location, parent, Vector3.zero, Quaternion.identity, forceClone, userDatas);
}
/// <summary>
/// 异步实例化一个游戏对象。
/// </summary>
/// <param name="location">资源定位地址。</param>
/// <param name="parent">父物体。</param>
/// <param name="position">世界坐标。</param>
/// <param name="rotation">世界角度。</param>
/// <param name="forceClone">强制克隆游戏对象,忽略缓存池里的对象。</param>
/// <param name="userDatas">用户自定义数据。</param>
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);
}
/// <summary>
/// 同步实例化一个游戏对象。
/// </summary>
/// <param name="location">资源定位地址。</param>
/// <param name="forceClone">强制克隆游戏对象,忽略缓存池里的对象。</param>
/// <param name="userDatas">用户自定义数据。</param>
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;
}
/// <summary>
/// 同步实例化一个游戏对象。
/// </summary>
/// <param name="location">资源定位地址。</param>
/// <param name="parent">父物体</param>
/// <param name="forceClone">强制克隆游戏对象,忽略缓存池里的对象。</param>
/// <param name="userDatas">用户自定义数据。</param>
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;
}
/// <summary>
/// 同步实例化一个游戏对象。
/// </summary>
/// <param name="location">资源定位地址。</param>
/// <param name="parent">父物体。</param>
/// <param name="position">世界坐标。</param>
/// <param name="rotation">世界角度。</param>
/// <param name="forceClone">强制克隆游戏对象,忽略缓存池里的对象。</param>
/// <param name="userDatas">用户自定义数据。</param>
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;
}
/// <summary>
/// 实例化一个游戏对象。
/// </summary>
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;
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 570ac9abe8a04e77b49c42719a9fa93b
timeCreated: 1693831272

View File

@@ -1,8 +1,10 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Threading;
using Cysharp.Threading.Tasks;
using UnityEngine;
using YooAsset;
using Object = UnityEngine.Object;
namespace TEngine
{
@@ -90,22 +92,33 @@ namespace TEngine
/// </summary>
public int ARCTableCapacity { get; set; }
/// <summary>
/// 是否开启对象池。
/// </summary>
public static bool EnableGoPool { get; set; } = true;
private readonly Dictionary<string, AssetInfo> _assetInfoMap = new Dictionary<string, AssetInfo>();
private static readonly Type _typeOfGameObject = typeof(GameObject);
#endregion
#region
internal override void Update(float elapseSeconds, float realElapseSeconds)
{
ResourcePool.Update();
ResourcePool.Instance.OnUpdate();
}
internal override void Shutdown()
{
_assetInfoMap.Clear();
ReleasePreLoadAssets(isShutDown: true);
#if !UNITY_WEBGL
YooAssets.Destroy();
#endif
ResourcePool.Destroy();
ResourcePool.Instance.OnDestroy();
}
private void ReleaseAllHandle()
@@ -320,7 +333,7 @@ namespace TEngine
YooAssets.SetDefaultPackage(defaultPackage);
}
ResourcePool.Initialize(GameModule.Get<ResourceModule>().gameObject);
ResourcePool.Instance.OnAwake();
_releaseMaps ??= new Dictionary<string, AssetOperationHandle>(ARCTableCapacity);
_operationHandlesMaps ??= new Dictionary<string, AssetOperationHandle>(ARCTableCapacity);
@@ -415,6 +428,60 @@ namespace TEngine
throw new GameFrameworkException("System.NotImplementedException.");
}
/// <summary>
/// 释放游戏物体。
/// </summary>
/// <param name="gameObject">游戏物体。</param>
/// <param name="forceNoPool">强制不入回收池。</param>
/// <param name="delayTime">延迟时间。</param>
public void FreeGameObject(GameObject gameObject, bool forceNoPool = false, float delayTime = 0f)
{
if (Application.isPlaying)
{
if (EnableGoPool && ResourcePool.Instance.IsNeedRecycle(gameObject, out GoProperty property, forceNoPool))
{
if (delayTime > 0f)
{
ResourcePool.Instance.DelayDestroy(gameObject, property, delayTime);
return;
}
ResourcePool.Instance.AddCacheGo(property.ResPath, gameObject, property);
}
else
{
if (delayTime > 0f)
{
Object.Destroy(gameObject, delayTime);
return;
}
Object.Destroy(gameObject);
}
}
else
{
if (delayTime > 0f)
{
Object.Destroy(gameObject, delayTime);
return;
}
Object.Destroy(gameObject);
}
}
private void AddRecycleGoProperty(string location, GameObject go, Vector3 initScale)
{
bool flag = ResourceCacheMgr.Instance.IsNeedCache(location, out int _, out int maxPoolCnt);
if (!(EnableGoPool & flag) || maxPoolCnt <= 0)
{
return;
}
ResourcePool.Instance.AddNewRecycleProperty(go, location, initScale);
}
/// <summary>
/// 资源回收(卸载引用计数为零的资源)。
/// </summary>
@@ -459,7 +526,7 @@ namespace TEngine
#endif
}
/// <inheritdoc />
public HasAssetResult HasAsset(string location, string packageName = "")
{
if (string.IsNullOrEmpty(location))
@@ -467,21 +534,7 @@ namespace TEngine
throw new GameFrameworkException("Asset name is invalid.");
}
AssetInfo assetInfo;
if (string.IsNullOrEmpty(packageName))
{
assetInfo = YooAssets.GetAssetInfo(location);
}
else
{
var package = YooAssets.GetPackage(packageName);
if (package == null)
{
throw new GameFrameworkException($"The package does not exist. Package Name :{packageName}");
}
assetInfo = package.GetAssetInfo(location);
}
AssetInfo assetInfo = GetAssetInfo(location, packageName);
if (!CheckLocationValid(location))
{
@@ -512,7 +565,6 @@ namespace TEngine
#region
/// <inheritdoc />
public bool IsNeedDownloadFromRemote(string location, string packageName = "")
{
if (string.IsNullOrEmpty(packageName))
@@ -526,7 +578,7 @@ namespace TEngine
}
}
/// <inheritdoc />
public bool IsNeedDownloadFromRemote(AssetInfo assetInfo, string packageName = "")
{
if (string.IsNullOrEmpty(packageName))
@@ -540,7 +592,7 @@ namespace TEngine
}
}
/// <inheritdoc />
public AssetInfo[] GetAssetInfos(string tag, string packageName = "")
{
if (string.IsNullOrEmpty(packageName))
@@ -554,7 +606,7 @@ namespace TEngine
}
}
/// <inheritdoc />
public AssetInfo[] GetAssetInfos(string[] tags, string packageName = "")
{
if (string.IsNullOrEmpty(packageName))
@@ -568,21 +620,46 @@ namespace TEngine
}
}
/// <inheritdoc />
public AssetInfo GetAssetInfo(string location, string packageName = "")
{
if (string.IsNullOrEmpty(location))
{
throw new GameFrameworkException("Asset name is invalid.");
}
if (string.IsNullOrEmpty(packageName))
{
return YooAssets.GetAssetInfo(location);
if (_assetInfoMap.TryGetValue(location, out AssetInfo assetInfo))
{
return assetInfo;
}
assetInfo = YooAssets.GetAssetInfo(location);
_assetInfoMap[location] = assetInfo;
return assetInfo;
}
else
{
string key = $"{packageName}/{location}";
if (_assetInfoMap.TryGetValue(key, out AssetInfo assetInfo))
{
return assetInfo;
}
var package = YooAssets.GetPackage(packageName);
return package.GetAssetInfo(location);
if (package == null)
{
throw new GameFrameworkException($"The package does not exist. Package Name :{packageName}");
}
assetInfo = package.GetAssetInfo(location);
_assetInfoMap[key] = assetInfo;
return assetInfo;
}
}
/// <inheritdoc />
public bool CheckLocationValid(string location, string packageName = "")
{
if (string.IsNullOrEmpty(packageName))
@@ -598,44 +675,12 @@ namespace TEngine
#endregion
/// <inheritdoc />
public T LoadAsset<T>(string location, bool needInstance = true, bool needCache = false,
string packageName = "") where T : Object
public T LoadAsset<T>(string location, bool needInstance = true, bool needCache = false, string packageName = "") where T : Object
{
if (string.IsNullOrEmpty(location))
{
Log.Error("Asset name is invalid.");
return default;
return LoadAsset<T>(location, parent: null, needInstance, needCache, packageName);
}
AssetOperationHandle handle = GetHandleSync<T>(location, needCache, packageName);
if (typeof(T) == typeof(GameObject))
{
if (needInstance)
{
GameObject gameObject = handle.InstantiateSync();
if (!needCache)
{
AssetReference.BindAssetReference(gameObject, handle, location, packageName: packageName);
}
return gameObject as T;
}
}
T ret = handle.AssetObject as T;
if (!needCache)
{
handle.Dispose();
}
return ret;
}
/// <inheritdoc />
public T LoadAsset<T>(string location, Transform parent, bool needInstance = true, bool needCache = false,
string packageName = "")
public T LoadAsset<T>(string location, Transform parent, bool needInstance = true, bool needCache = false, string packageName = "")
where T : Object
{
if (string.IsNullOrEmpty(location))
@@ -644,9 +689,30 @@ namespace TEngine
return default;
}
Type assetType = typeof(T);
if (EnableGoPool && assetType == _typeOfGameObject)
{
GameObject go = ResourcePool.Instance.AllocCacheGoByLocation(location, parentTrans: parent);
if (go != null)
{
return go as T;
}
}
AssetOperationHandle handle = GetHandleSync<T>(location, needCache, packageName: packageName);
if (typeof(T) == typeof(GameObject))
AssetInfo assetInfo = GetAssetInfo(location, packageName);
if (EnableGoPool && handle.AssetObject != null)
{
if (ResourceCacheMgr.Instance.IsNeedCache(assetInfo.AssetPath, out var cacheTime))
{
ResourceCacheMgr.Instance.AddCache(assetInfo.AssetPath, handle.AssetObject, cacheTime);
}
}
if (assetType == _typeOfGameObject)
{
if (needInstance)
{
@@ -656,6 +722,11 @@ namespace TEngine
AssetReference.BindAssetReference(gameObject, handle, location, packageName: packageName);
}
if (EnableGoPool)
{
AddRecycleGoProperty(assetInfo.AssetPath, gameObject, gameObject.transform.localScale);
}
return gameObject as T;
}
}
@@ -669,38 +740,12 @@ namespace TEngine
return ret;
}
/// <inheritdoc />
public T LoadAsset<T>(string location, out AssetOperationHandle handle, bool needCache = false,
string packageName = "") where T : Object
{
handle = GetHandleSync<T>(location, needCache, packageName: packageName);
if (string.IsNullOrEmpty(location))
{
Log.Error("Asset name is invalid.");
return default;
return LoadAsset<T>(location, null, out handle, needCache, packageName);
}
if (typeof(T) == typeof(GameObject))
{
GameObject gameObject = handle.InstantiateSync();
if (!needCache)
{
AssetReference.BindAssetReference(gameObject, handle, location, packageName: packageName);
}
return gameObject as T;
}
T ret = handle.AssetObject as T;
if (!needCache)
{
handle.Dispose();
}
return ret;
}
/// <inheritdoc />
public T LoadAsset<T>(string location, Transform parent, out AssetOperationHandle handle,
bool needCache = false, string packageName = "") where T : Object
{
@@ -712,7 +757,7 @@ namespace TEngine
return default;
}
if (typeof(T) == typeof(GameObject))
if (typeof(T) == _typeOfGameObject)
{
GameObject gameObject = handle.InstantiateSync(parent);
if (!needCache)
@@ -732,21 +777,18 @@ namespace TEngine
return ret;
}
/// <inheritdoc />
public AssetOperationHandle LoadAssetGetOperation<T>(string location, bool needCache = false,
string packageName = "") where T : Object
{
return GetHandleSync<T>(location, needCache, packageName: packageName);
}
/// <inheritdoc />
public AssetOperationHandle LoadAssetAsyncHandle<T>(string location, bool needCache = false,
string packageName = "") where T : Object
{
return GetHandleAsync<T>(location, needCache, packageName: packageName);
}
/// <inheritdoc />
public SubAssetsOperationHandle LoadSubAssetsSync<TObject>(string location, string packageName = "")
where TObject : Object
{
@@ -759,7 +801,6 @@ namespace TEngine
return package.LoadSubAssetsSync<TObject>(location);
}
/// <inheritdoc />
public SubAssetsOperationHandle LoadSubAssetsAsync<TObject>(string location, string packageName = "")
where TObject : Object
{
@@ -772,7 +813,6 @@ namespace TEngine
return package.LoadSubAssetsAsync<TObject>(location: location);
}
/// <inheritdoc />
public SubAssetsOperationHandle LoadSubAssetsSync(AssetInfo assetInfo, string packageName = "")
{
if (string.IsNullOrEmpty(packageName))
@@ -784,7 +824,6 @@ namespace TEngine
return package.LoadSubAssetsSync(assetInfo);
}
/// <inheritdoc />
public async UniTask<List<T>> LoadAssetsByTagAsync<T>(string assetTag, string packageName = "")
where T : UnityEngine.Object
{
@@ -796,10 +835,27 @@ namespace TEngine
return assetObjects;
}
/// <inheritdoc />
public async UniTask<T> LoadAssetAsync<T>(string location, CancellationToken cancellationToken = default,
bool needInstance = true, bool needCache = false, string packageName = "") where T : Object
bool needInstance = true, bool needCache = false, string packageName = "", Transform parent = null) where T : Object
{
if (string.IsNullOrEmpty(location))
{
Log.Error("Asset name is invalid.");
return default;
}
Type assetType = typeof(T);
if (EnableGoPool && assetType == _typeOfGameObject)
{
GameObject go = ResourcePool.Instance.AllocCacheGoByLocation(location, parentTrans: parent);
if (go != null)
{
return go as T;
}
}
AssetOperationHandle handle = LoadAssetAsyncHandle<T>(location, needCache, packageName: packageName);
bool cancelOrFailed = await handle.ToUniTask().AttachExternalCancellation(cancellationToken)
@@ -810,16 +866,30 @@ namespace TEngine
return null;
}
if (typeof(T) == typeof(GameObject))
AssetInfo assetInfo = GetAssetInfo(location, packageName);
if (EnableGoPool && handle.AssetObject != null)
{
if (ResourceCacheMgr.Instance.IsNeedCache(assetInfo.AssetPath, out var cacheTime))
{
ResourceCacheMgr.Instance.AddCache(assetInfo.AssetPath, handle.AssetObject, cacheTime);
}
}
if (typeof(T) == _typeOfGameObject)
{
if (needInstance)
{
GameObject gameObject = handle.InstantiateSync();
GameObject gameObject = handle.InstantiateSync(parent);
if (!needCache)
{
AssetReference.BindAssetReference(gameObject, handle, location, packageName: packageName);
}
if (EnableGoPool)
{
AddRecycleGoProperty(assetInfo.AssetPath, gameObject, gameObject.transform.localScale);
}
return gameObject as T;
}
}
@@ -833,49 +903,30 @@ namespace TEngine
return ret;
}
/// <inheritdoc />
public async UniTask<GameObject> LoadGameObjectAsync(string location,
CancellationToken cancellationToken = default, bool needCache = false, string packageName = "")
{
AssetOperationHandle handle =
LoadAssetAsyncHandle<GameObject>(location, needCache, packageName: packageName);
bool cancelOrFailed = await handle.ToUniTask().AttachExternalCancellation(cancellationToken)
.SuppressCancellationThrow();
if (cancelOrFailed)
{
return null;
return await LoadGameObjectAsync(location, null, cancellationToken, needCache, packageName);
}
GameObject gameObject = handle.InstantiateSync();
if (!needCache)
{
AssetReference.BindAssetReference(gameObject, handle, location, packageName: packageName);
}
return gameObject;
}
/// <inheritdoc />
public async UniTask<GameObject> LoadGameObjectAsync(string location, Transform parent,
CancellationToken cancellationToken = default, bool needCache = false, string packageName = "")
{
GameObject gameObject =
await LoadGameObjectAsync(location, cancellationToken, needCache, packageName: packageName);
if (parent != null)
if (EnableGoPool)
{
gameObject.transform.SetParent(parent);
}
else
GameObject go = ResourcePool.Instance.AllocCacheGoByLocation(location, parentTrans: parent);
if (go != null)
{
Log.Error("Set Parent Failed");
return go;
}
}
GameObject gameObject = await LoadAssetAsync<GameObject>(location, cancellationToken, needInstance:true, needCache: needCache, packageName: packageName, parent: parent);
return gameObject;
}
/// <inheritdoc />
public async UniTask<RawFileOperationHandle> LoadRawAssetAsync(string location,
CancellationToken cancellationToken = default, string packageName = "")
{
@@ -896,7 +947,6 @@ namespace TEngine
return cancelOrFailed ? null : handle;
}
/// <inheritdoc />
public async UniTask<T> LoadSubAssetAsync<T>(string location, string assetName,
CancellationToken cancellationToken = default, string packageName = "") where T : Object
{
@@ -926,7 +976,6 @@ namespace TEngine
return cancelOrFailed ? null : handle.GetSubAssetObject<T>(assetName);
}
/// <inheritdoc />
public async UniTask<T[]> LoadAllSubAssetAsync<T>(string location,
CancellationToken cancellationToken = default, string packageName = "") where T : Object
{
@@ -960,7 +1009,6 @@ namespace TEngine
private readonly Dictionary<string, Object> _preLoadMaps = new Dictionary<string, Object>();
/// <inheritdoc />
public void PushPreLoadAsset(string location, Object assetObject, string packageName = "")
{
var cacheKey = string.IsNullOrEmpty(packageName) || packageName.Equals(PackageName)
@@ -974,7 +1022,6 @@ namespace TEngine
_preLoadMaps.Add(cacheKey, assetObject);
}
/// <inheritdoc />
public T GetPreLoadAsset<T>(string location, string packageName = "") where T : Object
{
var cacheKey = string.IsNullOrEmpty(packageName) || packageName.Equals(PackageName)

View File

@@ -234,7 +234,14 @@ namespace TEngine
/// <returns></returns>
public InitializationOperation InitPackage(string customPackageName = "")
{
return m_ResourceManager.InitPackage(customPackageName);
InitializationOperation operation = m_ResourceManager.InitPackage(customPackageName);
operation.Completed += _ =>
{
ResourceCacheMgr.Instance.InitDefaultCachePool();
};
return operation;
}
/// <summary>
@@ -361,6 +368,11 @@ namespace TEngine
m_ResourceManager.UnloadAsset(asset);
}
public void FreeGameObject(GameObject go, bool forceNoPool = false, float delayTime = 0f)
{
m_ResourceManager.FreeGameObject(go, forceNoPool, delayTime);
}
/// <summary>
/// 预订执行释放未被使用的资源。
/// </summary>
@@ -672,15 +684,16 @@ namespace TEngine
/// <param name="cancellationToken">取消操作Token。</param>
/// <param name="needInstance">是否需要实例化。</param>
/// <param name="needCache">是否需要缓存。</param>
/// <param name="customPackageName">指定资源包的名称。不传使用默认资源包</param>
/// <param name="customPackageName">指定资源包的名称。不传使用默认资源包</param>
/// <param name="parent">资源实例父节点。</param>
/// <typeparam name="T">要加载资源的类型。</typeparam>
/// <returns>异步资源实例。</returns>
public async UniTask<T> LoadAssetAsync<T>(string location, CancellationToken cancellationToken = default,
bool needInstance = true, bool needCache = false, string customPackageName = "")
bool needInstance = true, bool needCache = false, string customPackageName = "", Transform parent = null)
where T : UnityEngine.Object
{
return await m_ResourceManager.LoadAssetAsync<T>(location, cancellationToken, needInstance, needCache,
packageName: customPackageName);
packageName: customPackageName,parent:parent);
}
/// <summary>

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d56be587f7b64f4aac094df08798619f
timeCreated: 1700625097

View File

@@ -0,0 +1,60 @@
using UnityEngine;
namespace TEngine
{
public class GameTimerTick
{
protected OnTick Handle;
protected float LastTime;
protected float Interval;
protected bool ResetInterval = true;
public GameTimerTick(float interval, OnTick tickHandle) => Init(interval, true, true, tickHandle);
public GameTimerTick(float interval, bool immediately, OnTick tickHandle) => Init(interval, immediately, true, tickHandle);
public GameTimerTick(float interval, bool immediately, bool resetInterval, OnTick tickHandle)
{
Init(interval, immediately, resetInterval, tickHandle);
}
private void Init(float interval, bool immediately, bool resetInterval, OnTick tickHandle)
{
Interval = interval;
Handle = tickHandle;
ResetInterval = resetInterval;
if (immediately)
{
return;
}
LastTime = Time.time;
}
public void OnUpdate()
{
float time = Time.time;
if (LastTime + Interval >= time)
{
return;
}
if (ResetInterval)
{
LastTime = time;
}
else if (LastTime == 0.0)
{
LastTime = time;
}
else
{
LastTime += Interval;
}
Handle();
}
public delegate void OnTick();
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 76843c14c1344aa68863289bf7388238
timeCreated: 1701088103

View File

@@ -0,0 +1,465 @@
using System;
using System.Collections.Generic;
namespace TEngine
{
public delegate void TimerHandler(object[] args);
[UpdateModule]
internal class TimerManager : ModuleImp
{
[Serializable]
internal class Timer
{
public int timerId = 0;
public float curTime = 0;
public float time = 0;
public TimerHandler Handler;
public bool isLoop = false;
public bool isNeedRemove = false;
public bool isRunning = false;
public bool isUnscaled = false; //是否使用非缩放的时间
public object[] Args = null; //回调参数
}
private int _curTimerId = 0;
private readonly List<Timer> _timerList = new List<Timer>();
private readonly List<Timer> _unscaledTimerList = new List<Timer>();
private readonly List<int> _cacheRemoveTimers = new List<int>();
private readonly List<int> _cacheRemoveUnscaledTimers = new List<int>();
/// <summary>
/// 添加计时器。
/// </summary>
/// <param name="callback">计时器回调。</param>
/// <param name="time">计时器间隔。</param>
/// <param name="isLoop">是否循环。</param>
/// <param name="isUnscaled">是否不收时间缩放影响。</param>
/// <param name="args">传参。(避免闭包)</param>
/// <returns>计时器Id。</returns>
public int AddTimer(TimerHandler callback, float time, bool isLoop = false, bool isUnscaled = false, params object[] args)
{
Timer timer = new Timer
{
timerId = ++_curTimerId,
curTime = time,
time = time,
Handler = callback,
isLoop = isLoop,
isUnscaled = isUnscaled,
Args = args,
isNeedRemove = false,
isRunning = true
};
InsertTimer(timer);
return timer.timerId;
}
private void InsertTimer(Timer timer)
{
bool isInsert = false;
if (timer.isUnscaled)
{
for (int i = 0, len = _unscaledTimerList.Count; i < len; i++)
{
if (_unscaledTimerList[i].curTime > timer.curTime)
{
_unscaledTimerList.Insert(i, timer);
isInsert = true;
break;
}
}
if (!isInsert)
{
_unscaledTimerList.Add(timer);
}
}
else
{
for (int i = 0, len = _timerList.Count; i < len; i++)
{
if (_timerList[i].curTime > timer.curTime)
{
_timerList.Insert(i, timer);
isInsert = true;
break;
}
}
if (!isInsert)
{
_timerList.Add(timer);
}
}
}
/// <summary>
/// 暂停计时器。
/// </summary>
/// <param name="timerId">计时器Id。</param>
public void Stop(int timerId)
{
Timer timer = GetTimer(timerId);
if (timer != null) timer.isRunning = false;
}
/// <summary>
/// 恢复计时器。
/// </summary>
/// <param name="timerId">计时器Id。</param>
public void Resume(int timerId)
{
Timer timer = GetTimer(timerId);
if (timer != null) timer.isRunning = true;
}
/// <summary>
/// 计时器是否在运行中。
/// </summary>
/// <param name="timerId">计时器Id。</param>
/// <returns>否在运行中。</returns>
public bool IsRunning(int timerId)
{
Timer timer = GetTimer(timerId);
return timer is { isRunning: true };
}
/// <summary>
/// 获得计时器剩余时间
/// </summary>
public float GetLeftTime(int timerId)
{
Timer timer = GetTimer(timerId);
if (timer == null) return 0;
return timer.curTime;
}
/// <summary>
/// 重置计时器,恢复到开始状态。
/// </summary>
public void Restart(int timerId)
{
Timer timer = GetTimer(timerId);
if (timer != null)
{
timer.curTime = timer.time;
timer.isRunning = true;
}
}
/// <summary>
/// 重置计时器。
/// </summary>
public void Reset(int timerId, TimerHandler callback, float time, bool isLoop = false, bool isUnscaled = false)
{
Timer timer = GetTimer(timerId);
if (timer != null)
{
timer.curTime = time;
timer.time = time;
timer.Handler = callback;
timer.isLoop = isLoop;
timer.isNeedRemove = false;
if (timer.isUnscaled != isUnscaled)
{
RemoveTimerImmediate(timerId);
timer.isUnscaled = isUnscaled;
InsertTimer(timer);
}
}
}
/// <summary>
/// 重置计时器。
/// </summary>
public void Reset(int timerId, float time, bool isLoop, bool isUnscaled)
{
Timer timer = GetTimer(timerId);
if (timer != null)
{
timer.curTime = time;
timer.time = time;
timer.isLoop = isLoop;
timer.isNeedRemove = false;
if (timer.isUnscaled != isUnscaled)
{
RemoveTimerImmediate(timerId);
timer.isUnscaled = isUnscaled;
InsertTimer(timer);
}
}
}
/// <summary>
/// 立即移除。
/// </summary>
/// <param name="timerId"></param>
private void RemoveTimerImmediate(int timerId)
{
for (int i = 0, len = _timerList.Count; i < len; i++)
{
if (_timerList[i].timerId == timerId)
{
_timerList.RemoveAt(i);
return;
}
}
for (int i = 0, len = _unscaledTimerList.Count; i < len; i++)
{
if (_unscaledTimerList[i].timerId == timerId)
{
_unscaledTimerList.RemoveAt(i);
return;
}
}
}
/// <summary>
/// 移除计时器。
/// </summary>
/// <param name="timerId">计时器Id。</param>
public void RemoveTimer(int timerId)
{
for (int i = 0, len = _timerList.Count; i < len; i++)
{
if (_timerList[i].timerId == timerId)
{
_timerList[i].isNeedRemove = true;
return;
}
}
for (int i = 0, len = _unscaledTimerList.Count; i < len; i++)
{
if (_unscaledTimerList[i].timerId == timerId)
{
_unscaledTimerList[i].isNeedRemove = true;
return;
}
}
}
/// <summary>
/// 移除所有计时器。
/// </summary>
public void RemoveAllTimer()
{
_timerList.Clear();
_unscaledTimerList.Clear();
}
private Timer GetTimer(int timerId)
{
for (int i = 0, len = _timerList.Count; i < len; i++)
{
if (_timerList[i].timerId == timerId)
{
return _timerList[i];
}
}
for (int i = 0, len = _unscaledTimerList.Count; i < len; i++)
{
if (_unscaledTimerList[i].timerId == timerId)
{
return _unscaledTimerList[i];
}
}
return null;
}
private void LoopCallInBadFrame()
{
bool isLoopCall = false;
for (int i = 0, len = _timerList.Count; i < len; i++)
{
Timer timer = _timerList[i];
if (timer.isLoop && timer.curTime <= 0)
{
if (timer.Handler != null)
{
timer.Handler(timer.Args);
}
timer.curTime += timer.time;
if (timer.curTime <= 0)
{
isLoopCall = true;
}
}
}
if (isLoopCall)
{
LoopCallInBadFrame();
}
}
private void LoopCallUnscaledInBadFrame()
{
bool isLoopCall = false;
for (int i = 0, len = _unscaledTimerList.Count; i < len; i++)
{
Timer timer = _unscaledTimerList[i];
if (timer.isLoop && timer.curTime <= 0)
{
if (timer.Handler != null)
{
timer.Handler(timer.Args);
}
timer.curTime += timer.time;
if (timer.curTime <= 0)
{
isLoopCall = true;
}
}
}
if (isLoopCall)
{
LoopCallUnscaledInBadFrame();
}
}
private void UpdateTimer(float elapseSeconds)
{
bool isLoopCall = false;
for (int i = 0, len = _timerList.Count; i < len; i++)
{
Timer timer = _timerList[i];
if (timer.isNeedRemove)
{
_cacheRemoveTimers.Add(i);
continue;
}
if (!timer.isRunning) continue;
timer.curTime -= elapseSeconds;
if (timer.curTime <= 0)
{
if (timer.Handler != null)
{
timer.Handler(timer.Args);
}
if (timer.isLoop)
{
timer.curTime += timer.time;
if (timer.curTime <= 0)
{
isLoopCall = true;
}
}
else
{
_cacheRemoveTimers.Add(i);
}
}
}
for (int i = _cacheRemoveTimers.Count - 1; i >= 0; i--)
{
_timerList.RemoveAt(_cacheRemoveTimers[i]);
_cacheRemoveTimers.RemoveAt(i);
}
if (isLoopCall)
{
LoopCallInBadFrame();
}
}
private void UpdateUnscaledTimer(float realElapseSeconds)
{
bool isLoopCall = false;
for (int i = 0, len = _unscaledTimerList.Count; i < len; i++)
{
Timer timer = _unscaledTimerList[i];
if (timer.isNeedRemove)
{
_cacheRemoveUnscaledTimers.Add(i);
continue;
}
if (!timer.isRunning) continue;
timer.curTime -= realElapseSeconds;
if (timer.curTime <= 0)
{
if (timer.Handler != null)
{
timer.Handler(timer.Args);
}
if (timer.isLoop)
{
timer.curTime += timer.time;
if (timer.curTime <= 0)
{
isLoopCall = true;
}
}
else
{
_cacheRemoveUnscaledTimers.Add(i);
}
}
}
for (int i = _cacheRemoveUnscaledTimers.Count - 1; i >= 0; i--)
{
_unscaledTimerList.RemoveAt(_cacheRemoveUnscaledTimers[i]);
_cacheRemoveUnscaledTimers.RemoveAt(i);
}
if (isLoopCall)
{
LoopCallUnscaledInBadFrame();
}
}
private readonly List<System.Timers.Timer> _ticker = new List<System.Timers.Timer>();
public System.Timers.Timer AddSystemTimer(Action<object, System.Timers.ElapsedEventArgs> callBack)
{
int interval = 1000;
var timerTick = new System.Timers.Timer(interval);
timerTick.AutoReset = true;
timerTick.Enabled = true;
timerTick.Elapsed += new System.Timers.ElapsedEventHandler(callBack);
_ticker.Add(timerTick);
return timerTick;
}
private void DestroySystemTimer()
{
foreach (var ticker in _ticker)
{
if (ticker != null)
{
ticker.Stop();
}
}
}
internal override void Update(float elapseSeconds, float realElapseSeconds)
{
UpdateTimer(elapseSeconds);
UpdateUnscaledTimer(realElapseSeconds);
}
internal override void Shutdown()
{
RemoveAllTimer();
DestroySystemTimer();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3a3b960842a44c6fbba975083ba5fa50
timeCreated: 1700625107

View File

@@ -0,0 +1,170 @@
using UnityEngine;
namespace TEngine
{
/// <summary>
/// 计时器模块。
/// </summary>
[DisallowMultipleComponent]
public sealed partial class TimerModule : Module
{
private TimerManager _timerManager;
/// <summary>
/// 游戏框架组件初始化。
/// </summary>
protected override void Awake()
{
base.Awake();
_timerManager = ModuleImpSystem.GetModule<TimerManager>();
if (_timerManager == null)
{
Log.Fatal("TimerMgr is invalid.");
}
}
/// <summary>
/// 添加计时器。
/// </summary>
/// <param name="callback">计时器回调。</param>
/// <param name="time">计时器间隔。</param>
/// <param name="isLoop">是否循环。</param>
/// <param name="isUnscaled">是否不收时间缩放影响。</param>
/// <param name="args">传参。(避免闭包)</param>
/// <returns>计时器Id。</returns>
public int AddTimer(TimerHandler callback, float time, bool isLoop = false, bool isUnscaled = false, params object[] args)
{
if (_timerManager == null)
{
throw new GameFrameworkException("TimerMgr is invalid.");
}
return _timerManager.AddTimer(callback, time, isLoop, isUnscaled, args);
}
/// <summary>
/// 暂停计时器。
/// </summary>
/// <param name="timerId">计时器Id。</param>
public void Stop(int timerId)
{
if (_timerManager == null)
{
throw new GameFrameworkException("TimerMgr is invalid.");
}
_timerManager.Stop(timerId);
}
/// <summary>
/// 恢复计时器。
/// </summary>
/// <param name="timerId">计时器Id。</param>
public void Resume(int timerId)
{
if (_timerManager == null)
{
throw new GameFrameworkException("TimerMgr is invalid.");
}
_timerManager.Resume(timerId);
}
/// <summary>
/// 计时器是否在运行中。
/// </summary>
/// <param name="timerId">计时器Id。</param>
/// <returns>否在运行中。</returns>
public bool IsRunning(int timerId)
{
if (_timerManager == null)
{
throw new GameFrameworkException("TimerMgr is invalid.");
}
return _timerManager.IsRunning(timerId);
}
/// <summary>
/// 获得计时器剩余时间。
/// </summary>
public float GetLeftTime(int timerId)
{
if (_timerManager == null)
{
throw new GameFrameworkException("TimerMgr is invalid.");
}
return _timerManager.GetLeftTime(timerId);
}
/// <summary>
/// 重置计时器,恢复到开始状态。
/// </summary>
public void Restart(int timerId)
{
if (_timerManager == null)
{
throw new GameFrameworkException("TimerMgr is invalid.");
}
_timerManager.Restart(timerId);
}
/// <summary>
/// 重置计时器。
/// </summary>
public void ResetTimer(int timerId, TimerHandler callback, float time, bool isLoop = false, bool isUnscaled = false)
{
if (_timerManager == null)
{
throw new GameFrameworkException("TimerMgr is invalid.");
}
_timerManager.Reset(timerId,callback,time,isLoop,isUnscaled);
}
/// <summary>
/// 重置计时器。
/// </summary>
public void ResetTimer(int timerId, float time, bool isLoop, bool isUnscaled)
{
if (_timerManager == null)
{
throw new GameFrameworkException("TimerMgr is invalid.");
}
_timerManager.Reset(timerId, time,isLoop,isUnscaled);
}
/// <summary>
/// 移除计时器。
/// </summary>
/// <param name="timerId">计时器Id。</param>
public void RemoveTimer(int timerId)
{
if (_timerManager == null)
{
Log.Fatal("TimerMgr is invalid.");
throw new GameFrameworkException("TimerMgr is invalid.");
}
_timerManager.RemoveTimer(timerId);
}
/// <summary>
/// 移除所有计时器。
/// </summary>
public void RemoveAllTimer()
{
if (_timerManager == null)
{
Log.Fatal("TimerMgr is invalid.");
throw new GameFrameworkException("TimerMgr is invalid.");
}
_timerManager.RemoveAllTimer();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5e79c115d5054209810f42dc6e25cf94
timeCreated: 1700625348

View File

@@ -5,7 +5,7 @@
"depth": 0,
"source": "git",
"dependencies": {},
"hash": "febff1c658fa9d76726f3aa25f26ec6d35c1e2e6"
"hash": "f810ede6d74a7fcfc2c2fd03ad08c44696b3a0e0"
},
"com.cysharp.unitask": {
"version": "file:UniTask",