|
using System;
|
using Spine;
|
using Spine.Unity;
|
using UnityEngine;
|
using UnityEngine.UI;
|
using Cysharp.Threading.Tasks;
|
|
public class UIHeroController : MonoBehaviour
|
{
|
private GameObjectPoolManager.GameObjectPool pool;
|
private int skinID;
|
protected SkeletonGraphic skeletonGraphic;
|
|
public Spine.AnimationState spineAnimationState;
|
private GameObject instanceGO;
|
private bool isInitializing = false;
|
private bool isInitialized = false;
|
private System.Threading.CancellationTokenSource loadCancellationToken; // 用于取消之前的加载任务
|
|
// 使用 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;
|
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.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()
|
{
|
// 取消正在进行的加载任务
|
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,表示是否需要设置灰度
|
|
/// <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
|
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)
|
{
|
return skeletonData.Animations.Items[0].Name;
|
}
|
else
|
{
|
Debug.LogError("Spine 数据中没有找到任何动画!武将皮肤:" + skinID);
|
}
|
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>
|
/// 动画完成事件处理
|
/// </summary>
|
protected virtual void OnAnimationComplete(Spine.TrackEntry trackEntry)
|
{
|
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;
|
}
|
}
|