using System; using System.Collections; using Sirenix.OdinInspector; using Spine.Unity; using UnityEngine; using AnimationState = Spine.AnimationState; namespace BlueWater.Players { public class SpineController : MonoBehaviour { // Variables #region Variables // Components [SerializeField] private SkeletonAnimation _skeletonAnimation; private AnimationState _animationState; // Variables [SerializeField] private string _initialSkinName = "default"; #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; SetSkin(_initialSkinName); } #endregion // Methods #region Methods public void PlayAnimation(string animationName, bool isLoopActive, float speed = 1f) { if (!_skeletonAnimation && _animationState == null) return; if (string.IsNullOrEmpty(animationName)) { Debug.LogError($"{animationName}의 애니메이션은 존재하지 않습니다."); return; } _animationState.TimeScale = speed; _animationState.SetAnimation(0, animationName, isLoopActive); } 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 bool IsComparingCurrentAnimation(string animationName, int trackIndex = 0) { if (!_skeletonAnimation || _animationState == null) return false; var currentAnimation = _animationState.GetCurrent(trackIndex)?.Animation; return currentAnimation != null && currentAnimation.Name == animationName; } public IEnumerator WaitForAnimationToRun(string animationName, Action onSuccess, float timeout = 2f) { var elapsedTime = 0f; while (!IsComparingCurrentAnimation(animationName)) { elapsedTime += Time.deltaTime; yield return null; if (elapsedTime > timeout) { Debug.Log("Timeout waiting for animation state: " + animationName); onSuccess?.Invoke(false); yield break; } } onSuccess?.Invoke(true); } public void SetCurrentAnimationSpeed(float targetDuration, int trackIndex = 0) { if (!_skeletonAnimation || _animationState == null) return; var currentAnimation = _animationState.GetCurrent(trackIndex)?.Animation; if (currentAnimation != null) { var animationLength = currentAnimation.Duration; _animationState.TimeScale = animationLength / targetDuration; } } public float GetCurrentAnimationNormalizedTime(int trackIndex = 0) { if (!_skeletonAnimation || _animationState == null) return 0f; var currentTrackEntry = _animationState.GetCurrent(trackIndex); if (currentTrackEntry != null) { return currentTrackEntry.TrackTime / currentTrackEntry.Animation.Duration; } return 0f; } public float GetCurrentAnimationLength(int trackIndex = 0) { if (!_skeletonAnimation || _animationState == null) return 0f; var currentAnimation = _animationState.GetCurrent(trackIndex)?.Animation; return currentAnimation != null ? currentAnimation.Duration : 0f; } public void ResetAnimationSpeed() { if (!_skeletonAnimation || _animationState == null) return; _animationState.TimeScale = 1f; } #endregion } }