yyl
2026-05-11 51b0f6ed9f4e1d3bb6f8144470b46908c7699a96
Main/System/Hero/UIHeroController.cs
@@ -1,7 +1,10 @@

using System;
using Spine;
using Spine.Unity;
using UnityEngine;
using UnityEngine.UI;
using Cysharp.Threading.Tasks;
public class UIHeroController : MonoBehaviour
{
@@ -9,62 +12,56 @@
   private int skinID;
   protected SkeletonGraphic skeletonGraphic;
   protected Spine.AnimationState spineAnimationState;
   public Spine.AnimationState spineAnimationState;
   private GameObject instanceGO;
   private bool isInitializing = false;
   private bool isInitialized = false;
   private System.Threading.CancellationTokenSource loadCancellationToken; // 用于取消之前的加载任务
   private Action onComplete;
   public void Create(int _skinID, float scale = 0.8f, Action _onComplete = null, string motionName = "idle", bool isLh = false)
   // 使用 UniTask 提供的并发控制 - 支持 WebGL
   // 限制同时加载的资源数量(默认为4,可根据设备性能调整)
   private const int MAX_CONCURRENT_LOADS = 4;
   private static int currentLoadingCount = 0;
   private static readonly object loadLock = new object();
   private static int lastInitFrame = -1; // 上一次执行Initialize的帧号,用于确保每帧最多1次
   private static GameObjectPoolManager.GameObjectPool cachedUIHeroPool; // 缓存UIHero预制体池
   private RectTransform _instanceRect; // 缓存RectTransform避免重复GetComponent
   public Action onComplete;
#pragma warning disable CS1998 // 异步方法内部通过 DelayedInitializeAsync 异步执行,无需直接 await
   public async UniTask Create(int _skinID, float scale = 0.8f, Action _onComplete = null, string motionName = "idle", bool isLh = false)
   {
      if (skinID == _skinID)
      {
      {
         //避免重复创建
         if (skeletonGraphic != null)
         {
            SetMaterialNone();
            if (isLh)
            {
               var skinConfigTmp = HeroSkinConfig.Get(skinID);
               if (skinConfigTmp != null && skinConfigTmp.Tachie.IsSpine())
               {
                  skeletonGraphic.enabled = true;
               }
            }
            else
            {
               skeletonGraphic.enabled = true;
            }
         }
         return;
      }
      if (skeletonGraphic != null)
      {
         skeletonGraphic.enabled = false;
      }
      skinID = _skinID;
      onComplete = _onComplete;
      pool = GameObjectPoolManager.Instance.RequestPool(UILoader.LoadPrefab("UIHero"));
      if (!transform.gameObject.activeSelf)
      {
         transform.SetActive(true);
      }
      if (instanceGO == null)
      {
         instanceGO = pool.Request();
         instanceGO.transform.SetParent(transform);
         //transform 的Pivot Y是0,让instanceGO 居中
         instanceGO.transform.localPosition = new Vector3(0, instanceGO.GetComponent<RectTransform>().sizeDelta.y * 0.5f);
         //instanceGO.transform.localPosition = Vector3.zero;
         instanceGO.transform.localScale = Vector3.one;
         instanceGO.transform.localRotation = Quaternion.identity;
      }
      skeletonGraphic = instanceGO.GetComponentInChildren<SkeletonGraphic>(true);
      var skinConfig = HeroSkinConfig.Get(skinID);
      if (isLh)
      {
         skeletonGraphic.skeletonDataAsset = ResManager.Instance.LoadAsset<SkeletonDataAsset>("Hero/SpineRes/", skinConfig.Tachie);
      }
      else
      {
         skeletonGraphic.skeletonDataAsset = ResManager.Instance.LoadAsset<SkeletonDataAsset>("Hero/SpineRes/", skinConfig.SpineRes);
      }
      if (skeletonGraphic.skeletonDataAsset == null)
      {
         transform.SetActive(false);
         if (pool != null)
            pool.Release(instanceGO);
         skeletonGraphic = null;
         Destroy(instanceGO);
         Debug.LogError("未配置spine");
         return;
      }
      skeletonGraphic.Initialize(true);
      if (isLh)
      {
         //X轴偏移,Y轴偏移,缩放,是否水平翻转(0否1是)
         if (skinConfig.TachieParam.Length == 4)
         {
@@ -79,47 +76,292 @@
            this.transform.localRotation = Quaternion.identity;
         }
         //立绘特殊处理,没有spine动画的改用图片
         var lhImg = this.AddMissingComponent<RawImage>();
         if (!skinConfig.Tachie.IsSpine())
         {
            //图片替换
            lhImg.SetTexture2DPNG(skinConfig.Tachie);
            if (skeletonGraphic != null)
            {
               skeletonGraphic.enabled = false;
            }
            lhImg.raycastTarget = false;
            return;
         }
         else
         {
            if (skeletonGraphic != null)
            {
               skeletonGraphic.enabled = true;
            }
            lhImg.enabled = false;
         }
      }
      else
      {
         this.transform.localScale = Vector3.one * scale;
      }
      onComplete = _onComplete;
      // 取消之前的异步任务,避免多次调用导致错乱
      CancelLoadTask();
      // 创建新的取消令牌
      loadCancellationToken = new System.Threading.CancellationTokenSource();
      // 使用 UniTask 进行异步初始化,将instanceGO创建和资源加载都移到异步处理
      DelayedInitializeAsync(skinConfig, motionName, isLh, loadCancellationToken.Token).Forget();
   }
#pragma warning restore CS1998
   /// <summary>
   /// 取消之前的加载任务
   /// </summary>
   private void CancelLoadTask()
   {
      if (loadCancellationToken != null && !loadCancellationToken.IsCancellationRequested)
      {
         loadCancellationToken.Cancel();
         loadCancellationToken.Dispose();
      }
   }
   public async UniTask CreateAsync(int _skinID, float scale = 0.8f, Action _onComplete = null, string motionName = "idle", bool isLh = false)
   {
      if (skinID == _skinID)
      {
         //避免重复创建
         if (skeletonGraphic != null)
         {
            SetMaterialNone();
            if (isLh)
            {
               var skinConfigTmp = HeroSkinConfig.Get(skinID);
               if (skinConfigTmp != null && skinConfigTmp.Tachie.Contains("SkeletonData"))
               {
                  skeletonGraphic.enabled = true;
               }
            }
            else
            {
               skeletonGraphic.enabled = true;
            }
         }
         return;
      }
      skinID = _skinID;
      var skinConfig = HeroSkinConfig.Get(skinID);
      if (isLh)
      {
         //X轴偏移,Y轴偏移,缩放,是否水平翻转(0否1是)
         if (skinConfig.TachieParam.Length == 4)
         {
            this.transform.localPosition = new Vector3(skinConfig.TachieParam[0], skinConfig.TachieParam[1], 0);
            this.transform.localScale = Vector3.one * skinConfig.TachieParam[2];
            this.transform.localRotation = Quaternion.Euler(0, skinConfig.TachieParam[3] == 0 ? 0 : 180, 0);
         }
         else
         {
            this.transform.localPosition = Vector3.zero;
            this.transform.localScale = Vector3.one;
            this.transform.localRotation = Quaternion.identity;
         }
         //立绘特殊处理,没有spine动画的改用图片
         var lhImg = this.AddMissingComponent<RawImage>();
         if (!skinConfig.Tachie.Contains("SkeletonData"))
         {
            //图片替换
            lhImg.SetTexture2DPNG(skinConfig.Tachie);
            lhImg.SetNativeSize();
            if (skeletonGraphic != null)
            {
               skeletonGraphic.enabled = false;
            }
            lhImg.enabled = true;
            lhImg.raycastTarget = false;
            return;
         }
         else
         {
            if (skeletonGraphic != null)
            {
               skeletonGraphic.enabled = true;
            }
            lhImg.enabled = false;
         }
      }
      else
      {
         this.transform.localScale = Vector3.one * scale;
      }
      onComplete = _onComplete;
      if (cachedUIHeroPool == null)
         cachedUIHeroPool = GameObjectPoolManager.Instance.GetPool(await UILoader.LoadPrefabAsync("UIHero"));
      pool = cachedUIHeroPool;
      if (this == null) return;
      if (!transform.gameObject.activeSelf)
      {
         transform.SetActive(true);
      }
      if (instanceGO == null)
      {
         instanceGO = pool.Request();
         instanceGO.transform.SetParent(transform);
         _instanceRect = instanceGO.GetComponent<RectTransform>();
         //transform 的Pivot Y是0,让instanceGO 居中
         instanceGO.transform.localPosition = new Vector3(0, _instanceRect.sizeDelta.y * 0.5f);
         //instanceGO.transform.localPosition = Vector3.zero;
         instanceGO.transform.localScale = Vector3.one;
         instanceGO.transform.localRotation = Quaternion.identity;
      }
      skeletonGraphic = instanceGO.GetComponentInChildren<SkeletonGraphic>(true);
      if (isLh)
      {
         skeletonGraphic.skeletonDataAsset = await ResManager.Instance.LoadAssetAsync<SkeletonDataAsset>("Hero/SpineRes/", skinConfig.Tachie);
      }
      else
      {
         skeletonGraphic.skeletonDataAsset = await ResManager.Instance.LoadAssetAsync<SkeletonDataAsset>("Hero/SpineRes/", skinConfig.SpineRes);
      }
      if (skeletonGraphic.skeletonDataAsset == null)
      {
         transform.SetActive(false);
         if (pool != null)
            pool.Release(instanceGO);
         skeletonGraphic = null;
         Destroy(instanceGO);
         Debug.LogError("未配置spine");
         return;
      }
      skeletonGraphic.initialSkinName = skinConfig.InitialSkinName;
      skeletonGraphic.Initialize(true);
      // 初始化完成后设置皮肤
      if (!string.IsNullOrEmpty(skinConfig.InitialSkinName))
      {
         var skeleton = skeletonGraphic.Skeleton;
         skeleton.SetSkin(skinConfig.InitialSkinName);
         skeleton.SetSlotsToSetupPose();
         skeletonGraphic.Update(0);
      }
      skeletonGraphic.enabled = true;
      // 注册到SpineUpdateManager,批量更新减少跨语言调用开销
      SpineUpdateManager.Instance.Register(skeletonGraphic);
      // 不可见时完全停止更新,滚动列表场景收益很大
      skeletonGraphic.updateWhenInvisible = Spine.Unity.UpdateMode.Nothing;
      SetMaterialNone();
      spineAnimationState = skeletonGraphic.AnimationState;
      spineAnimationState.Data.DefaultMix = 0f;
      if (motionName == "")
         motionName = GetFistSpineAnim();
      PlayAnimation(motionName, true);
      spineAnimationState.Complete -= OnAnimationComplete;
      spineAnimationState.Complete += OnAnimationComplete;
#if UNITY_EDITOR
      await UniTask.Delay(100);
      if (skeletonGraphic != null && skeletonGraphic.material != null)
      {
         skeletonGraphic.material.shader = Shader.Find(skeletonGraphic.material.shader.name);
      }
#endif
   }
   public bool HasAnimation(string motionName)
   {
      if (skeletonGraphic == null || skeletonGraphic.Skeleton == null)
      {
         Debug.LogWarning("skeletonGraphic or Skeleton is null, cannot check animation: " + motionName);
         return false;
      }
      return skeletonGraphic.Skeleton.ContainsMotion(motionName);
   }
   protected void OnDestroy()
   {
        if (spineAnimationState != null)
        {
            spineAnimationState.Complete -= OnAnimationComplete;
        }
      // 取消正在进行的加载任务
      CancelLoadTask();
      if (spineAnimationState != null)
      {
         spineAnimationState.Complete -= OnAnimationComplete;
      }
      // 从SpineUpdateManager中移除
      if (skeletonGraphic != null)
         SpineUpdateManager.Instance.Unregister(skeletonGraphic);
      if (pool != null)
         pool.Release(instanceGO);
      skeletonGraphic = null;
      pool = null;
   }
   private string pendingAnimationName = null;
   private bool pendingAnimationLoop = false;
   private bool pendingAnimationReplay = true;
   private float? pendingSpeed = null;
   private bool pendingEnabled = true; // 改为普通bool,false表示未设置,true表示需要启用
   private bool pendingGray = false; // 改为普通bool,表示是否需要设置灰度
    public virtual void PlayAnimation(string motionName, bool loop = false)
    {
        if (spineAnimationState == null) return;
   /// <summary>
   /// 播放 Spine 动画
   /// </summary>
   /// <param name="motionName">动作名</param>
   /// <param name="loop">循环</param>
   /// <param name="replay">如果相同动作是否再次重播,比如跑步重播就会跳帧不顺滑</param>
   public virtual TrackEntry PlayAnimation(string motionName, bool loop = false, bool replay = true)
   {
      // 如果正在初始化中,保存动画参数,等待初始化完成后再播放
      if (isInitializing)
      {
         pendingAnimationName = motionName;
         pendingAnimationLoop = loop;
         pendingAnimationReplay = replay;
         return null;
      }
      if (spineAnimationState == null)
      {
         Debug.LogWarning("spineAnimationState is null, cannot play animation: " + motionName);
         return null;
      }
      if (GetCurrentAnimationName() == motionName && !replay)
         return null;
      // 直接使用 ToString() 而不是调用 GetAnimationName
        spineAnimationState.SetAnimation(0, motionName.ToString(), loop);
    }
      try
      {
         return spineAnimationState.SetAnimation(0, motionName.ToString(), loop);
      }
      catch (System.Exception e)
      {
         Debug.LogError("播放动画失败: " + motionName + ", 错误: " + e.Message);
         return null;
      }
   }
   // 播放第一个动画(作为默认动画)
   string GetFistSpineAnim()
   {
      if (skeletonGraphic == null || skeletonGraphic.Skeleton == null)
      {
         Debug.LogWarning("skeletonGraphic or Skeleton is null, cannot get first animation");
         return "idle"; // 返回默认动画名称
      }
      var skeletonData = skeletonGraphic.Skeleton.Data;
      if (skeletonData.Animations.Count > 0)
      {
@@ -129,8 +371,23 @@
      {
         Debug.LogError("Spine 数据中没有找到任何动画!武将皮肤:" + skinID);
      }
      return "";
    }
      return "idle"; // 返回默认动画名称
   }
   /// <summary>
   /// 获取当前正在播放的 Spine 动画名称
   /// </summary>
   /// <returns>当前动画名称,如果没有动画则返回空字符串</returns>
   public string GetCurrentAnimationName()
   {
      if (spineAnimationState == null || spineAnimationState.GetCurrent(0) == null)
      {
         return string.Empty;
      }
      return spineAnimationState.GetCurrent(0).Animation.Name;
   }
   /// <summary>
   /// 动画完成事件处理
@@ -140,4 +397,318 @@
      onComplete?.Invoke();
   }
   //越大越快
   public void SetSpeed(float speed)
   {
      // 如果正在初始化中,保存速度参数,等待初始化完成后再设置
      if (isInitializing)
      {
         pendingSpeed = speed;
         return;
      }
      if (spineAnimationState == null)
      {
         Debug.LogWarning("spineAnimationState is null, cannot set speed");
         return;
      }
      spineAnimationState.TimeScale = speed;
   }
   public void SetEnabled(bool isEnable)
   {
      // 如果正在初始化中,保存启用状态,等待初始化完成后再设置
      if (isInitializing)
      {
         pendingEnabled = isEnable;
         return;
      }
      if (skeletonGraphic == null)
      {
         Debug.LogWarning("skeletonGraphic is null, cannot set enabled state");
         return;
      }
      skeletonGraphic.enabled = isEnable;
   }
   public void SetGray()
   {
      // 如果正在初始化中,标记需要设置灰度,等待初始化完成后再设置
      if (isInitializing)
      {
         pendingGray = true;
         return;
      }
      if (skeletonGraphic == null)
      {
         Debug.LogWarning("skeletonGraphic is null, cannot set gray material");
         return;
      }
      skeletonGraphic.material = MaterialUtility.GetDefaultSpriteGrayMaterial();
   }
   public void SetMaterialNone()
   {
      // 如果正在初始化中,标记需要设置无材质,等待初始化完成后再设置
      if (isInitializing)
      {
         pendingGray = false;
         return;
      }
      if (skeletonGraphic == null)
      {
         Debug.LogWarning("skeletonGraphic is null, cannot set material to none");
         return;
      }
      skeletonGraphic.material = GetDefaultSpineMaterial();
   }
   /// <summary>
   /// 获取默认的 Spine 渲染材质。
   /// 对于包含非 Normal 混合模式(Additive/Multiply/Screen)插槽的角色,
   /// 返回 atlas 原始材质(Blend One OneMinusSrcAlpha),保证 PMA 混合正确;
   /// 对于普通角色,返回 null(使用 Canvas 默认材质,行为不变)。
   /// </summary>
   private Material GetDefaultSpineMaterial()
   {
      if (skeletonGraphic == null || skeletonGraphic.Skeleton == null)
         return null;
      var slotsData = skeletonGraphic.Skeleton.Data.Slots;
      for (int i = 0; i < slotsData.Count; i++)
      {
         if (slotsData.Items[i].BlendMode != Spine.BlendMode.Normal)
         {
            // 存在非 Normal 混合模式的插槽,必须使用 Spine 材质
            // Canvas 默认材质的 Blend SrcAlpha OneMinusSrcAlpha 会导致
            // PMA Additive 效果(vertex alpha=0)完全消失
            var dataAsset = skeletonGraphic.skeletonDataAsset;
            if (dataAsset != null && dataAsset.atlasAssets.Length > 0)
               return dataAsset.atlasAssets[0].PrimaryMaterial;
            break;
         }
      }
      return null;
   }
   /// <summary>
   /// 延迟初始化,结合并发控制和分帧延迟
   /// 1. 资源加载使用真正的异步(LoadAssetAsync)
   /// 2. skeletonGraphic.Initialize() 前进行分帧延迟,避免主线程卡顿
   /// </summary>
   private async UniTaskVoid DelayedInitializeAsync(HeroSkinConfig skinConfig, string motionName, bool isLh, System.Threading.CancellationToken cancellationToken)
   {
      isInitializing = true;
      try
      {
         // 检查是否已被取消
         cancellationToken.ThrowIfCancellationRequested();
         // 获取加载信号量 - 限制并发数,避免资源竞争
         await AcquireLoadSlotAsync(cancellationToken);
         // 异步创建instanceGO和加载资源(真正的异步,不阻塞)
         await CreateInstanceAndLoadAssetsAsync(skinConfig, isLh, cancellationToken);
         // 再次检查是否已被取消
         cancellationToken.ThrowIfCancellationRequested();
         // 确保每帧最多执行1次Initialize(true),避免同帧大量骨骼解析导致卡顿
         while (Time.frameCount == lastInitFrame)
         {
            cancellationToken.ThrowIfCancellationRequested();
            await UniTask.NextFrame(cancellationToken);
         }
         lastInitFrame = Time.frameCount;
         if (skeletonGraphic == null || skeletonGraphic.skeletonDataAsset == null)
         {
            Debug.LogError("资源加载失败,无法初始化模型");
            return;
         }
         skeletonGraphic.initialSkinName = skinConfig.InitialSkinName;
         skeletonGraphic.Initialize(true);
         Debug.Log($"[UIHeroController] Spine Initialize完成, skeleton valid: {skeletonGraphic.IsValid}, enabled: {skeletonGraphic.enabled}");
         // 初始化完成后设置皮肤
         if (!string.IsNullOrEmpty(skinConfig.InitialSkinName))
         {
            var skeleton = skeletonGraphic.Skeleton;
            skeleton.SetSkin(skinConfig.InitialSkinName);
            skeleton.SetSlotsToSetupPose();
            skeletonGraphic.Update(0);
         }
         spineAnimationState = skeletonGraphic.AnimationState;
         spineAnimationState.Data.DefaultMix = 0f;
         // 初始化完成后才显示模型
         skeletonGraphic.enabled = pendingEnabled;
         // 注册到SpineUpdateManager,批量更新减少跨语言调用开销
         SpineUpdateManager.Instance.Register(skeletonGraphic);
         // 不可见时完全停止更新,滚动列表场景收益很大
         skeletonGraphic.updateWhenInvisible = Spine.Unity.UpdateMode.Nothing;
         if (pendingGray)
         {
            skeletonGraphic.material = MaterialUtility.GetDefaultSpriteGrayMaterial();
         }
         else
         {
            skeletonGraphic.material = GetDefaultSpineMaterial();
         }
         // 检查是否有待设置的速度,如果有则设置
         if (pendingSpeed.HasValue)
         {
            spineAnimationState.TimeScale = pendingSpeed.Value;
            pendingSpeed = null;
         }
         // 检查是否有待播放的动画,如果有则优先播放外部调用的动画
         if (!string.IsNullOrEmpty(pendingAnimationName))
         {
            isInitializing = false;
            PlayAnimation(pendingAnimationName, pendingAnimationLoop, pendingAnimationReplay);
            // 清除所有待播放动画参数
            pendingAnimationName = null;
            pendingAnimationLoop = false;
            pendingAnimationReplay = true;
         }
         else
         {
            // 如果没有外部调用的动画,播放默认动画
            if (motionName == "")
               motionName = GetFistSpineAnim();
            isInitializing = false;
            PlayAnimation(motionName, true);
         }
         spineAnimationState.Complete -= OnAnimationComplete;
         spineAnimationState.Complete += OnAnimationComplete;
         isInitialized = true;
         isInitializing = false;
      }
      catch (System.OperationCanceledException)
      {
         // 任务被取消,正常返回
         isInitializing = false;
      }
      catch (System.Exception e)
      {
         Debug.LogError($"英雄初始化异常: {e.Message}\n{e.StackTrace}");
         isInitializing = false;
      }
      finally
      {
         // 释放加载槽位
         ReleaseLoadSlot();
      }
   }
   /// <summary>
   /// 获取加载槽位(支持 WebGL 的并发控制)
   /// </summary>
   private async UniTask AcquireLoadSlotAsync(System.Threading.CancellationToken cancellationToken)
   {
      while (true)
      {
         // 检查是否已被取消
         cancellationToken.ThrowIfCancellationRequested();
         lock (loadLock)
         {
            if (currentLoadingCount < MAX_CONCURRENT_LOADS)
            {
               currentLoadingCount++;
               return;
            }
         }
         // 如果已达到最大并发数,等待下一帧再试
         await UniTask.NextFrame(cancellationToken);
      }
   }
   /// <summary>
   /// 释放加载槽位
   /// </summary>
   private void ReleaseLoadSlot()
   {
      lock (loadLock)
      {
         currentLoadingCount--;
      }
   }
   /// <summary>
   /// 异步创建instanceGO和加载资源(用于非立绘)
   /// 使用真正的异步加载,不阻塞主线程
   /// </summary>
   private async UniTask CreateInstanceAndLoadAssetsAsync(HeroSkinConfig skinConfig, bool isLh, System.Threading.CancellationToken cancellationToken)
   {
      // 确保transform处于激活状态
      if (!transform.gameObject.activeSelf)
      {
         transform.SetActive(true);
      }
      // 检查是否已被取消
      cancellationToken.ThrowIfCancellationRequested();
      // 创建pool和instanceGO(使用缓存避免重复加载预制体)
      if (cachedUIHeroPool == null)
         cachedUIHeroPool = GameObjectPoolManager.Instance.GetPool(await UILoader.LoadPrefabAsync("UIHero"));
      pool = cachedUIHeroPool;
      if (instanceGO == null)
      {
         instanceGO = pool.Request();
         instanceGO.transform.SetParent(transform);
         _instanceRect = instanceGO.GetComponent<RectTransform>();
         //transform 的Pivot Y是0,让instanceGO 居中
         instanceGO.transform.localPosition = new Vector3(0, _instanceRect.sizeDelta.y * 0.5f);
         instanceGO.transform.localScale = Vector3.one;
         instanceGO.transform.localRotation = Quaternion.identity;
      }
      skeletonGraphic = instanceGO.GetComponentInChildren<SkeletonGraphic>(true);
      // 真正的异步加载资源 - 不阻塞主线程
      string assetName = isLh ? skinConfig.Tachie : skinConfig.SpineRes;
      Debug.Log($"[UIHeroController] 开始加载spine 资源: {assetName}");
      SkeletonDataAsset loadedAsset = await ResManager.Instance.LoadAssetAsync<SkeletonDataAsset>("Hero/SpineRes/", assetName);
      // 再次检查是否已被取消
      cancellationToken.ThrowIfCancellationRequested();
      if (loadedAsset != null)
      {
         skeletonGraphic.skeletonDataAsset = loadedAsset;
         Debug.Log($"[UIHeroController] Spine资源加载成功: {assetName}, atlas count: {loadedAsset.atlasAssets?.Length}");
      }
      else
      {
         transform.SetActive(false);
         if (pool != null)
            pool.Release(instanceGO);
         skeletonGraphic = null;
         Destroy(instanceGO);
         Debug.LogError("未配置spine");
      }
   }
   /// <summary>
   /// 检查是否已完成初始化
   /// </summary>
   public bool IsInitialized()
   {
      return isInitialized && !isInitializing;
   }
}