diff --git a/Assets/_Datas/01.Scenes/01.Restaurant.unity b/Assets/_Datas/01.Scenes/01.Restaurant.unity index 6420b4367..24b81af4a 100644 --- a/Assets/_Datas/01.Scenes/01.Restaurant.unity +++ b/Assets/_Datas/01.Scenes/01.Restaurant.unity @@ -445,8 +445,6 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 6c667d81563204249aea17e2052c7e50, type: 3} m_Name: m_EditorClassIdentifier: - inventoryRoot: {fileID: 1713185378} - itemPrefab: {fileID: 296314366} --- !u!4 &121938434 Transform: m_ObjectHideFlags: 0 @@ -2540,6 +2538,7 @@ GameObject: - component: {fileID: 968554882} - component: {fileID: 968554884} - component: {fileID: 968554885} + - component: {fileID: 968554886} m_Layer: 0 m_Name: RestaurantPlayer m_TagString: Untagged @@ -2637,6 +2636,26 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: c665f9c268555a74a8a805d67d09c80e, type: 3} m_Name: m_EditorClassIdentifier: +--- !u!114 &968554886 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 968554876} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 06e836de83eb924449235839869a147c, type: 3} + m_Name: + m_EditorClassIdentifier: + _originalMaterial: {fileID: 2100000, guid: 288333d9c9df2d84cadf3b48d918ebdb, type: 2} + _replacementMaterial: {fileID: 0} + _isSkinSet: 1 + _initialSkinName: Basic + _isRandomSkin: 0 + _isRandomRange: 0 + _randomRange: {x: 0, y: 0} + _randomStrings: [] --- !u!114 &985479691 MonoBehaviour: m_ObjectHideFlags: 0 @@ -5087,6 +5106,10 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} + - target: {fileID: 4059976348031579783, guid: f17834b768fb4724380bafc46049b63c, type: 3} + propertyPath: m_DefaultActionMap + value: df70fa95-8a34-4494-b137-73ab6b9c7d37 + objectReference: {fileID: 0} - target: {fileID: 6312950592041400320, guid: f17834b768fb4724380bafc46049b63c, type: 3} propertyPath: m_Name value: KeyManager diff --git a/Assets/_Datas/02.Scripts/Players.meta b/Assets/_Datas/02.Scripts/Characters.meta similarity index 100% rename from Assets/_Datas/02.Scripts/Players.meta rename to Assets/_Datas/02.Scripts/Characters.meta diff --git a/Assets/_Datas/02.Scripts/Characters/Players.meta b/Assets/_Datas/02.Scripts/Characters/Players.meta new file mode 100644 index 000000000..dd673ee9c --- /dev/null +++ b/Assets/_Datas/02.Scripts/Characters/Players.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 67cd165408b4f85469fbe1b9ecf3b9e5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/_Datas/02.Scripts/Characters/Players/RestaurantPlayer.meta b/Assets/_Datas/02.Scripts/Characters/Players/RestaurantPlayer.meta new file mode 100644 index 000000000..84d0c0b5b --- /dev/null +++ b/Assets/_Datas/02.Scripts/Characters/Players/RestaurantPlayer.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bc99ee6ab7cf7d34fb103aa950497c91 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/_Datas/02.Scripts/Players/IPlayerState.cs b/Assets/_Datas/02.Scripts/Characters/Players/RestaurantPlayer/IPlayerState.cs similarity index 100% rename from Assets/_Datas/02.Scripts/Players/IPlayerState.cs rename to Assets/_Datas/02.Scripts/Characters/Players/RestaurantPlayer/IPlayerState.cs diff --git a/Assets/_Datas/02.Scripts/Players/IPlayerState.cs.meta b/Assets/_Datas/02.Scripts/Characters/Players/RestaurantPlayer/IPlayerState.cs.meta similarity index 100% rename from Assets/_Datas/02.Scripts/Players/IPlayerState.cs.meta rename to Assets/_Datas/02.Scripts/Characters/Players/RestaurantPlayer/IPlayerState.cs.meta diff --git a/Assets/_Datas/02.Scripts/Players/PlayerStateMachine.cs b/Assets/_Datas/02.Scripts/Characters/Players/RestaurantPlayer/PlayerStateMachine.cs similarity index 100% rename from Assets/_Datas/02.Scripts/Players/PlayerStateMachine.cs rename to Assets/_Datas/02.Scripts/Characters/Players/RestaurantPlayer/PlayerStateMachine.cs diff --git a/Assets/_Datas/02.Scripts/Players/PlayerStateMachine.cs.meta b/Assets/_Datas/02.Scripts/Characters/Players/RestaurantPlayer/PlayerStateMachine.cs.meta similarity index 100% rename from Assets/_Datas/02.Scripts/Players/PlayerStateMachine.cs.meta rename to Assets/_Datas/02.Scripts/Characters/Players/RestaurantPlayer/PlayerStateMachine.cs.meta diff --git a/Assets/_Datas/02.Scripts/Players/RestaurantPlayer.cs b/Assets/_Datas/02.Scripts/Characters/Players/RestaurantPlayer/RestaurantPlayer.cs similarity index 100% rename from Assets/_Datas/02.Scripts/Players/RestaurantPlayer.cs rename to Assets/_Datas/02.Scripts/Characters/Players/RestaurantPlayer/RestaurantPlayer.cs diff --git a/Assets/_Datas/02.Scripts/Players/RestaurantPlayer.cs.meta b/Assets/_Datas/02.Scripts/Characters/Players/RestaurantPlayer/RestaurantPlayer.cs.meta similarity index 100% rename from Assets/_Datas/02.Scripts/Players/RestaurantPlayer.cs.meta rename to Assets/_Datas/02.Scripts/Characters/Players/RestaurantPlayer/RestaurantPlayer.cs.meta diff --git a/Assets/_Datas/02.Scripts/Players/RestaurantPlayerData.cs b/Assets/_Datas/02.Scripts/Characters/Players/RestaurantPlayer/RestaurantPlayerData.cs similarity index 100% rename from Assets/_Datas/02.Scripts/Players/RestaurantPlayerData.cs rename to Assets/_Datas/02.Scripts/Characters/Players/RestaurantPlayer/RestaurantPlayerData.cs diff --git a/Assets/_Datas/02.Scripts/Players/RestaurantPlayerData.cs.meta b/Assets/_Datas/02.Scripts/Characters/Players/RestaurantPlayer/RestaurantPlayerData.cs.meta similarity index 100% rename from Assets/_Datas/02.Scripts/Players/RestaurantPlayerData.cs.meta rename to Assets/_Datas/02.Scripts/Characters/Players/RestaurantPlayer/RestaurantPlayerData.cs.meta diff --git a/Assets/_Datas/02.Scripts/Players/RestaurantPlayerView.cs b/Assets/_Datas/02.Scripts/Characters/Players/RestaurantPlayer/RestaurantPlayerView.cs similarity index 94% rename from Assets/_Datas/02.Scripts/Players/RestaurantPlayerView.cs rename to Assets/_Datas/02.Scripts/Characters/Players/RestaurantPlayer/RestaurantPlayerView.cs index ef98ef195..c695a23d8 100644 --- a/Assets/_Datas/02.Scripts/Players/RestaurantPlayerView.cs +++ b/Assets/_Datas/02.Scripts/Characters/Players/RestaurantPlayer/RestaurantPlayerView.cs @@ -7,6 +7,7 @@ public class RestaurantPlayerView : MonoBehaviour private Rigidbody _rigidbody; private Transform _visualLook; private ParticleSystem _dashParticle; + private SpineController _spineController; private void Awake() { diff --git a/Assets/_Datas/02.Scripts/Players/RestaurantPlayerView.cs.meta b/Assets/_Datas/02.Scripts/Characters/Players/RestaurantPlayer/RestaurantPlayerView.cs.meta similarity index 100% rename from Assets/_Datas/02.Scripts/Players/RestaurantPlayerView.cs.meta rename to Assets/_Datas/02.Scripts/Characters/Players/RestaurantPlayer/RestaurantPlayerView.cs.meta diff --git a/Assets/_Datas/02.Scripts/Characters/SpineController.cs b/Assets/_Datas/02.Scripts/Characters/SpineController.cs new file mode 100644 index 000000000..a2266389c --- /dev/null +++ b/Assets/_Datas/02.Scripts/Characters/SpineController.cs @@ -0,0 +1,265 @@ +using System.Collections.Generic; +using Sirenix.OdinInspector; +using Spine; +using Spine.Unity; +using UnityEngine; +using AnimationState = Spine.AnimationState; +using Random = UnityEngine.Random; + +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/_Datas/02.Scripts/Characters/SpineController.cs.meta b/Assets/_Datas/02.Scripts/Characters/SpineController.cs.meta new file mode 100644 index 000000000..5243159ce --- /dev/null +++ b/Assets/_Datas/02.Scripts/Characters/SpineController.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 06e836de83eb924449235839869a147c \ No newline at end of file