diff --git a/Assets/_DDD/_Scripts/GameCharacter/SpineController.cs b/Assets/_DDD/_Scripts/GameCharacter/SpineController.cs new file mode 100644 index 000000000..ce05df758 --- /dev/null +++ b/Assets/_DDD/_Scripts/GameCharacter/SpineController.cs @@ -0,0 +1,264 @@ +using System.Collections.Generic; +using Sirenix.OdinInspector; +using Spine; +using Spine.Unity; +using UnityEngine; +using AnimationState = Spine.AnimationState; + +namespace DDD +{ + public class SpineController : MonoBehaviour + { + // Variables + #region Variables + + private SkeletonAnimation _skeletonAnimation; + + [SerializeField, ReadOnly] + private Material _originalMaterial; + + [SerializeField] + private Material _replacementMaterial; + + private AnimationState _animationState; + + // Variables + [SerializeField] + private bool _isSkinSet = true; + + [SerializeField, ShowIf("@_isSkinSet && !_isRandomSkin")] + private string _initialSkinName = "default"; + + [SerializeField, ShowIf("@_isSkinSet")] + private bool _isRandomSkin; + + [SerializeField, ShowIf("@_isSkinSet && _isRandomSkin")] + private bool _isRandomRange; + + [SerializeField, ShowIf("@_isSkinSet && _isRandomSkin && _isRandomRange"), Tooltip("x <= 값 < y")] + private Vector2 _randomRange; + + [SerializeField, ShowIf("@_isSkinSet && _isRandomSkin && !_isRandomRange")] + private List _randomStrings; + + private bool _customMaterialEnabled; + + #endregion + + // Unity events + #region Unity events + + private void Reset() + { + InitializeComponents(); + } + + private void Awake() + { + InitializeComponents(); + + if (!_isSkinSet) return; + + if (_isRandomSkin) + { + if (_isRandomRange) + { + SetRandomSkin(); + } + else + { + SetRandomStringListSkin(); + } + } + else + { + SetSkin(_initialSkinName); + } + } + + #endregion + + // Initialize methods + #region Initialize methods + + private void InitializeComponents() + { + _skeletonAnimation = transform.GetComponentInChildren(); + if (!_originalMaterial) + { + _originalMaterial = _skeletonAnimation.SkeletonDataAsset.atlasAssets[0].PrimaryMaterial; + } + + _animationState = _skeletonAnimation.AnimationState; + } + + #endregion + + // Methods + #region Methods + + /// 스파인 애니메이션 이름 + /// 반복 여부 + /// 애니메이션 속도 양수값 + /// true인 경우 자동으로 speed에 음수값(-1)을 넣음 + /// + /// + public TrackEntry PlayAnimation(string animationName, bool isLoopActive, float speed = 1f, bool isReverse = false, int trackIndex = 0) + { + if (!_skeletonAnimation || _animationState == null) return null; + + if (string.IsNullOrEmpty(animationName)) + { + Debug.LogError($"{animationName}의 애니메이션은 존재하지 않습니다."); + return null; + } + + // 중복 체크 + var currentTrackEntry = _animationState.GetCurrent(trackIndex); + if (currentTrackEntry != null && currentTrackEntry.Animation.Name == animationName) + { + return currentTrackEntry; + } + + _animationState.TimeScale = isReverse ? -Mathf.Abs(speed) : Mathf.Abs(speed); + var trackEntry = _animationState.SetAnimation(trackIndex, animationName, isLoopActive); + + if (isReverse) + { + trackEntry.TrackTime = trackEntry.AnimationEnd; + } + + return trackEntry; + } + + public TrackEntry PlayAnimationDuration(string animationName, bool isLoopActive, float duration, bool isReverse = false, int trackIndex = 0) + { + if (!_skeletonAnimation || _animationState == null) return null; + + if (string.IsNullOrEmpty(animationName)) + { + Debug.LogError($"{animationName}의 애니메이션은 존재하지 않습니다."); + return null; + } + + // 중복 체크 + var currentTrackEntry = _animationState.GetCurrent(trackIndex); + if (currentTrackEntry != null && currentTrackEntry.Animation.Name == animationName) + { + return currentTrackEntry; + } + + var findAnimation = _skeletonAnimation.Skeleton.Data.FindAnimation(animationName); + if (findAnimation == null) + { + Debug.LogError($"{animationName} 애니메이션을 찾을 수 없습니다."); + return null; + } + + var speed = findAnimation.Duration / duration; + _animationState.TimeScale = isReverse ? -Mathf.Abs(speed) : Mathf.Abs(speed); + var trackEntry = _animationState.SetAnimation(trackIndex, animationName, isLoopActive); + + if (isReverse) + { + trackEntry.TrackTime = trackEntry.AnimationEnd; + } + + return trackEntry; + } + + public TrackEntry AddAnimation(string animationName, bool isLoopActive, int trackIndex = 0) + { + if (!_skeletonAnimation || _animationState == null) return null; + + if (string.IsNullOrEmpty(animationName)) + { + Debug.LogError($"{animationName} 애니메이션은 존재하지 않습니다."); + return null; + } + + var trackEntry = _animationState.AddAnimation(trackIndex, animationName, isLoopActive, 0); + + return trackEntry; + } + + public string GetCurrentSkin() + { + if (_skeletonAnimation == null) return null; + + return _skeletonAnimation.Skeleton.Skin.ToString(); + } + + 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 void SetRandomStringListSkin() + { + if (_skeletonAnimation == null || _skeletonAnimation.Skeleton == null) return; + + if (_randomStrings == null || _randomStrings.Count <= 0) + { + Debug.LogError("_randomStrings 설정 오류"); + return; + } + + var randomSkin = Random.Range(0, _randomStrings.Count); + var randomSkinName = _randomStrings[randomSkin]; + SetSkin(randomSkinName); + } + + public void EnableCustomMaterial() + { + if (_customMaterialEnabled) return; + + _skeletonAnimation.CustomMaterialOverride[_originalMaterial] = _replacementMaterial; + _customMaterialEnabled = true; + } + + public void DisableCustomMaterial() + { + if (!_customMaterialEnabled) return; + + _skeletonAnimation.CustomMaterialOverride.Remove(_originalMaterial); + _customMaterialEnabled = false; + } + + public bool IsAnimationComplete(int trackIndex = 0) + { + if (!_skeletonAnimation || _animationState == null) return false; + + var currentTrackEntry = _animationState.GetCurrent(trackIndex); + if (currentTrackEntry == null) + { + Debug.LogWarning($"트랙 {trackIndex}에서 재생 중인 애니메이션이 없습니다."); + return false; + } + + return currentTrackEntry.IsComplete; + } + + #endregion + } +} \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/GameCharacter/SpineController.cs.meta b/Assets/_DDD/_Scripts/GameCharacter/SpineController.cs.meta new file mode 100644 index 000000000..fdf9d11ff --- /dev/null +++ b/Assets/_DDD/_Scripts/GameCharacter/SpineController.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 6d7e5480ae1ebf54b8537ad2a08696d2 \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/RestaurantCharacter/RestaurantCharacterAnimation.cs b/Assets/_DDD/_Scripts/RestaurantCharacter/RestaurantCharacterAnimation.cs index b32361fa0..f8f27f851 100644 --- a/Assets/_DDD/_Scripts/RestaurantCharacter/RestaurantCharacterAnimation.cs +++ b/Assets/_DDD/_Scripts/RestaurantCharacter/RestaurantCharacterAnimation.cs @@ -1,264 +1,27 @@ -using System.Collections.Generic; -using Sirenix.OdinInspector; -using Spine; -using Spine.Unity; using UnityEngine; -using AnimationState = Spine.AnimationState; namespace DDD { public class RestaurantCharacterAnimation : MonoBehaviour { - // Variables - #region Variables - - private SkeletonAnimation _skeletonAnimation; - - [SerializeField, ReadOnly] - private Material _originalMaterial; - - [SerializeField] - private Material _replacementMaterial; - - private AnimationState _animationState; - - // Variables - [SerializeField] - private bool _isSkinSet = true; - - [SerializeField, ShowIf("@_isSkinSet && !_isRandomSkin")] - private string _initialSkinName = "default"; - - [SerializeField, ShowIf("@_isSkinSet")] - private bool _isRandomSkin; - - [SerializeField, ShowIf("@_isSkinSet && _isRandomSkin")] - private bool _isRandomRange; - - [SerializeField, ShowIf("@_isSkinSet && _isRandomSkin && _isRandomRange"), Tooltip("x <= 값 < y")] - private Vector2 _randomRange; - - [SerializeField, ShowIf("@_isSkinSet && _isRandomSkin && !_isRandomRange")] - private List _randomStrings; - - private bool _customMaterialEnabled; - - #endregion - - // Unity events - #region Unity events - - private void Reset() - { - InitializeComponents(); - } + private RestaurantPlayerMovement _restaurantPlayerMovement; + private SpineController _spineController; private void Awake() { - InitializeComponents(); - - if (!_isSkinSet) return; - - if (_isRandomSkin) - { - if (_isRandomRange) - { - SetRandomSkin(); - } - else - { - SetRandomStringListSkin(); - } - } - else - { - SetSkin(_initialSkinName); - } + _restaurantPlayerMovement = GetComponent(); + _spineController = GetComponent(); } - #endregion - - // Initialize methods - #region Initialize methods - - private void InitializeComponents() + private void OnMove(bool isMoving) { - _skeletonAnimation = transform.GetComponentInChildren(); - if (!_originalMaterial) - { - _originalMaterial = _skeletonAnimation.SkeletonDataAsset.atlasAssets[0].PrimaryMaterial; - } - - _animationState = _skeletonAnimation.AnimationState; + string animationName = isMoving ? RestaurantPlayerAnimation.Walk : RestaurantPlayerAnimation.Idle; + _spineController.PlayAnimation(animationName, true); } - - #endregion - // Methods - #region Methods - - /// 스파인 애니메이션 이름 - /// 반복 여부 - /// 애니메이션 속도 양수값 - /// true인 경우 자동으로 speed에 음수값(-1)을 넣음 - /// - /// - public TrackEntry PlayAnimation(string animationName, bool isLoopActive, float speed = 1f, bool isReverse = false, int trackIndex = 0) + private void OnDash(float dashTime) { - if (!_skeletonAnimation || _animationState == null) return null; - - if (string.IsNullOrEmpty(animationName)) - { - Debug.LogError($"{animationName}의 애니메이션은 존재하지 않습니다."); - return null; - } - - // 중복 체크 - var currentTrackEntry = _animationState.GetCurrent(trackIndex); - if (currentTrackEntry != null && currentTrackEntry.Animation.Name == animationName) - { - return currentTrackEntry; - } - - _animationState.TimeScale = isReverse ? -Mathf.Abs(speed) : Mathf.Abs(speed); - var trackEntry = _animationState.SetAnimation(trackIndex, animationName, isLoopActive); - - if (isReverse) - { - trackEntry.TrackTime = trackEntry.AnimationEnd; - } - - return trackEntry; + _spineController.PlayAnimationDuration(RestaurantPlayerAnimation.Dash, false, duration:dashTime); } - - public TrackEntry PlayAnimationDuration(string animationName, bool isLoopActive, float duration, bool isReverse = false, int trackIndex = 0) - { - if (!_skeletonAnimation || _animationState == null) return null; - - if (string.IsNullOrEmpty(animationName)) - { - Debug.LogError($"{animationName}의 애니메이션은 존재하지 않습니다."); - return null; - } - - // 중복 체크 - var currentTrackEntry = _animationState.GetCurrent(trackIndex); - if (currentTrackEntry != null && currentTrackEntry.Animation.Name == animationName) - { - return currentTrackEntry; - } - - var findAnimation = _skeletonAnimation.Skeleton.Data.FindAnimation(animationName); - if (findAnimation == null) - { - Debug.LogError($"{animationName} 애니메이션을 찾을 수 없습니다."); - return null; - } - - var speed = findAnimation.Duration / duration; - _animationState.TimeScale = isReverse ? -Mathf.Abs(speed) : Mathf.Abs(speed); - var trackEntry = _animationState.SetAnimation(trackIndex, animationName, isLoopActive); - - if (isReverse) - { - trackEntry.TrackTime = trackEntry.AnimationEnd; - } - - return trackEntry; - } - - public TrackEntry AddAnimation(string animationName, bool isLoopActive, int trackIndex = 0) - { - if (!_skeletonAnimation || _animationState == null) return null; - - if (string.IsNullOrEmpty(animationName)) - { - Debug.LogError($"{animationName} 애니메이션은 존재하지 않습니다."); - return null; - } - - var trackEntry = _animationState.AddAnimation(trackIndex, animationName, isLoopActive, 0); - - return trackEntry; - } - - public string GetCurrentSkin() - { - if (_skeletonAnimation == null) return null; - - return _skeletonAnimation.Skeleton.Skin.ToString(); - } - - 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 void SetRandomStringListSkin() - { - if (_skeletonAnimation == null || _skeletonAnimation.Skeleton == null) return; - - if (_randomStrings == null || _randomStrings.Count <= 0) - { - Debug.LogError("_randomStrings 설정 오류"); - return; - } - - var randomSkin = Random.Range(0, _randomStrings.Count); - var randomSkinName = _randomStrings[randomSkin]; - SetSkin(randomSkinName); - } - - public void EnableCustomMaterial() - { - if (_customMaterialEnabled) return; - - _skeletonAnimation.CustomMaterialOverride[_originalMaterial] = _replacementMaterial; - _customMaterialEnabled = true; - } - - public void DisableCustomMaterial() - { - if (!_customMaterialEnabled) return; - - _skeletonAnimation.CustomMaterialOverride.Remove(_originalMaterial); - _customMaterialEnabled = false; - } - - public bool IsAnimationComplete(int trackIndex = 0) - { - if (!_skeletonAnimation || _animationState == null) return false; - - var currentTrackEntry = _animationState.GetCurrent(trackIndex); - if (currentTrackEntry == null) - { - Debug.LogWarning($"트랙 {trackIndex}에서 재생 중인 애니메이션이 없습니다."); - return false; - } - - return currentTrackEntry.IsComplete; - } - - #endregion } } \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/RestaurantCharacter/RestaurantPlayerCharacter.cs b/Assets/_DDD/_Scripts/RestaurantCharacter/RestaurantPlayerCharacter.cs index 851c03542..d5da1909a 100644 --- a/Assets/_DDD/_Scripts/RestaurantCharacter/RestaurantPlayerCharacter.cs +++ b/Assets/_DDD/_Scripts/RestaurantCharacter/RestaurantPlayerCharacter.cs @@ -4,6 +4,32 @@ namespace DDD { public class RestaurantPlayerCharacter : RestaurantCharacter { + private RestaurantPlayerMovement _movement; + private Transform _visualLook; + + private void Awake() + { + _movement = GetComponent(); + + _visualLook = transform.Find(CommonConstants.VisualLook); + } + + private void Update() + { + FlipVisualLook(); + } + + private void FlipVisualLook() + { + Vector3 localScale = _visualLook.localScale; + localScale.x = _movement.GetCurrentDirection().x switch + { + > 0.01f => -Mathf.Abs(localScale.x), + < -0.01f => Mathf.Abs(localScale.x), + _ => localScale.x + }; + _visualLook.localScale = localScale; + } } } \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/RestaurantCharacter/RestaurantPlayerMovement.cs b/Assets/_DDD/_Scripts/RestaurantCharacter/RestaurantPlayerMovement.cs index 65b5f341f..1b93152e0 100644 --- a/Assets/_DDD/_Scripts/RestaurantCharacter/RestaurantPlayerMovement.cs +++ b/Assets/_DDD/_Scripts/RestaurantCharacter/RestaurantPlayerMovement.cs @@ -8,8 +8,6 @@ namespace DDD public class RestaurantPlayerMovement : RestaurantCharacterMovement { private Rigidbody _rigidbody; - private RestaurantCharacterAnimation _animation; - private Transform _visualLook; private RestaurantPlayerDataSo _playerData; @@ -20,11 +18,12 @@ public class RestaurantPlayerMovement : RestaurantCharacterMovement private bool _isDashCooldown; private bool _isInitialized; + public Action OnMoving; + public Action OnDashing; + private void Awake() { _rigidbody = GetComponent(); - _animation = GetComponent(); - _visualLook = transform.Find(CommonConstants.VisualLook); } private async void Start() @@ -45,13 +44,6 @@ private async void Start() } } - private void Update() - { - if (_isInitialized == false) return; - - FlipVisualLook(); - } - private void FixedUpdate() { if (_isInitialized == false) return; @@ -78,18 +70,6 @@ public void SetCurrentDirection(Vector3 normalDirection) _currentDirection = normalDirection; } - - private void FlipVisualLook() - { - Vector3 localScale = _visualLook.localScale; - localScale.x = _currentDirection.x switch - { - > 0.01f => -Mathf.Abs(localScale.x), - < -0.01f => Mathf.Abs(localScale.x), - _ => localScale.x - }; - _visualLook.localScale = localScale; - } private void OnMove(InputAction.CallbackContext context) { @@ -107,8 +87,7 @@ private void Move() SetCurrentDirection(_inputDirection); _isMoving = _inputDirection != Vector3.zero; - string animationName = _isMoving ? RestaurantPlayerAnimation.Walk : RestaurantPlayerAnimation.Idle; - _animation.PlayAnimation(animationName, true); + OnMoving?.Invoke(_isMoving); Vector3 finalVelocity = _inputDirection * _playerData.MoveSpeed; _rigidbody.linearVelocity = finalVelocity; @@ -129,12 +108,10 @@ private bool CanDash() private IEnumerator DashCoroutine() { - // TODO : ui생기면 연동 - _isDashing = true; _isDashCooldown = true; - _animation.PlayAnimationDuration(RestaurantPlayerAnimation.Dash, false, _playerData.DashTime); + OnDashing?.Invoke(_playerData.DashTime); Vector3 dashVelocity = _currentDirection.normalized * _playerData.DashSpeed; _rigidbody.linearVelocity = dashVelocity; @@ -146,5 +123,7 @@ private IEnumerator DashCoroutine() yield return new WaitForSeconds(_playerData.DashCooldown); _isDashCooldown = false; } + + public Vector3 GetCurrentDirection() => _currentDirection; } } \ No newline at end of file