using System; using Sirenix.OdinInspector; using Spine; using Spine.Unity; using UnityEngine; using AnimationState = Spine.AnimationState; using Random = UnityEngine.Random; namespace BlueWater.Players { public class SpineController : MonoBehaviour { // Variables #region Variables // Components [field: SerializeField] public SkeletonAnimation SkeletonAnimation { get; private set; } private AnimationState _animationState; // Variables [SerializeField, ShowIf("@!_isRandomSkin")] private string _initialSkinName = "default"; [SerializeField] private bool _isRandomSkin; [SerializeField, ShowIf("@_isRandomSkin")] private Vector2 _randomRange; #endregion // Unity events #region Unity events private void Awake() { InitializeComponents(); } #endregion // Initialize methods #region Initialize methods [Button("셋팅 초기화")] public virtual void InitializeComponents() { SkeletonAnimation = transform.GetComponentInChildren(); _animationState = SkeletonAnimation.AnimationState; if (_isRandomSkin) { SetRandomSkin(); } else { SetSkin(_initialSkinName); } } #endregion // Methods #region Methods /// 스파인 애니메이션 이름 /// 반복 여부 /// 애니메이션 속도 양수값 /// true인 경우 자동으로 speed에 음수값을 넣음 /// public TrackEntry PlayAnimation(string animationName, bool isLoopActive, float speed = 1f, bool isReverse = false) { if (!SkeletonAnimation || _animationState == null) return null; if (string.IsNullOrEmpty(animationName)) { Debug.LogError($"{animationName}의 애니메이션은 존재하지 않습니다."); return null; } _animationState.TimeScale = isReverse ? -Mathf.Abs(speed) : Mathf.Abs(speed); var trackEntry = _animationState.SetAnimation(0, animationName, isLoopActive); if (isReverse) { trackEntry.TrackTime = trackEntry.AnimationEnd; } return trackEntry; } public void SetSkin(string skinName) { if (SkeletonAnimation == null && _animationState == null) return; if (string.IsNullOrEmpty(skinName)) { Debug.LogError($"{skinName}의 스킨 이름은 존재하지 않습니다."); return; } SkeletonAnimation.Skeleton.SetSkin(skinName); SkeletonAnimation.Skeleton.SetSlotsToSetupPose(); _animationState.Apply(SkeletonAnimation.Skeleton); } public void SetRandomSkin() { if (SkeletonAnimation == null || SkeletonAnimation.Skeleton == null) return; var skins = SkeletonAnimation.skeleton.Data.Skins; var randomSkin = Random.Range((int)_randomRange.x, (int)_randomRange.y); var randomSkinName = skins.Items[randomSkin].Name; SetSkin(randomSkinName); } public async Awaitable WaitForAnimationCompletion(TrackEntry trackEntry, bool isReverse = false) { if (isReverse) { await AwaitUntil(() => trackEntry.TrackTime <= 0); } else { await AwaitUntil(() => trackEntry.IsComplete); } } public async Awaitable AwaitUntil(Func condition) { while (!condition()) { await Awaitable.NextFrameAsync(); } } #endregion } }