diff --git a/Assets/Scirpts/CharacterController.cs b/Assets/Scirpts/CharacterController.cs new file mode 100644 index 000000000..286e05329 --- /dev/null +++ b/Assets/Scirpts/CharacterController.cs @@ -0,0 +1,296 @@ +using System; +using Sirenix.OdinInspector; +using UnityEngine; +using UnityEngine.InputSystem; + +namespace DDD +{ + public class CharacterController : MonoBehaviour + { + #region Variables + // Components + public Rigidbody Rigidbody { get; private set; } + private Transform _visualLook; + + // Move + [field: SerializeField, Range(1f, 20f), Tooltip("이동 속도")] + public float MoveSpeed { get; private set; } = 7f; + + [field: SerializeField] + public float MoveSpeedMultiplier { get; private set; } = 1f; + + public bool IsMoveEnabled { get; private set; } = true; + + private bool _isMoving; + + public bool IsMoving + { + get => _isMoving; + private set + { + if (_isMoving == value) return; + + _isMoving = value; + + // if (_isMoving) + // { + // AudioManager.Instance.PlaySfx(_walkingSfxName, true); + // } + // else + // { + // AudioManager.Instance.StopSfx(_walkingSfxName); + // } + } + } + + // Dash + [field: Title("대쉬")] + [field: SerializeField, Range(1f, 50f), Tooltip("대쉬 속도")] + public float DashSpeed { get; private set; } = 20f; + + [field: SerializeField, Range(0.1f, 1f), Tooltip("대쉬 시간")] + public float DashTime { get; private set; } = 0.2f; + + [field: SerializeField, Range(0f, 5f), Tooltip("대쉬 쿨타임")] + public float DashCooldown { get; private set; } = 0.5f; + + [SerializeField] + private ParticleSystem _dashParticle; + + [Title("사운드")] + // [SerializeField] + // private string _walkingSfxName = "TycoonPlayerWalking"; + [SerializeField] + private string _dashSfxName = "TycoonPlayerDashing"; + + public bool IsDashEnabled { get; private set; } = true; + public bool IsDashing { get; private set; } + + public bool IsDashCoolDownActive { get; private set; } + + private Vector3 _inputDirection; + + private Vector3 _currentDirection = Vector3.back; + + public Vector3 CurrentDirection + { + get => _currentDirection; + private set + { + if (value == Vector3.zero) return; + + _currentDirection = value; + } + } + + public Vector3 PushDirection { get; private set; } + public float PushPower { get; private set; } + public float PushPowerReduction { get; private set; } + + private InputAction _moveAction; + private InputAction _dashAction; + private Coroutine _dashInstance; + private float _finalSpeed; + + public Action OnSucceedDash; + + #endregion + + // Unity events + + #region Unity events + + private void Awake() + { + InitializeComponents(); + } + + private void Start() + { + _moveAction = PlayerInputKeyManager.Instance.GetAction(InputActionMaps.Tycoon, TycoonActions.Move); + // _dashAction = PlayerInputKeyManager.Instance.GetAction(InputActionMaps.Tycoon, TycoonActions.Dash); + + _moveAction.performed += OnMove; + _moveAction.canceled += OnMove; + // _dashAction.performed += OnDash; + } + + private void Update() + { + FlipVisualLook(); + } + + private void FixedUpdate() + { + if (!CanMove()) return; + + Move(); + } + + private void OnDestroy() + { + _moveAction.performed -= OnMove; + _moveAction.canceled -= OnMove; + // _dashAction.performed -= OnDash; + } + + #endregion + + // Initialize Methods + + #region Initialize Methods + + private void InitializeComponents() + { + Rigidbody = GetComponent(); + _visualLook = transform.Find("VisualLook"); + } + + #endregion + + // Methods + + #region Methods + + // Event methods + public void SetMoveSpeedMultiplier(float value) => MoveSpeedMultiplier = value; + public void ResetMoveSpeedMultiplier() => MoveSpeedMultiplier = 1f; + + public void EnableMoveAndDash() + { + IsMoveEnabled = true; + IsDashEnabled = true; + } + + public void DisableMoveAndDash() + { + IsMoveEnabled = false; + IsDashEnabled = false; + } + + public void SetCurrentDirection(Vector3 normalDirection) => CurrentDirection = normalDirection; + + // Methods + private void FlipVisualLook() + { + var 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; + } + + // Move + public bool CanMove() + { + return IsMoveEnabled; + } + + public void AddForce(Vector3 force, ForceMode forceMode) + { + Rigidbody.AddForce(force, forceMode); + } + + public void SetPush(Vector3 pushDirection, float pushPower) + { + throw new NotImplementedException(); + } + + public void OnMove(InputAction.CallbackContext context) + { + var movementInput = _moveAction.ReadValue(); + Debug.Log($"Move{movementInput.x} {movementInput.y}]"); + _inputDirection = new Vector3(movementInput.x, 0, movementInput.y).normalized; + } + + public void Move() + { + if (IsDashing) + { + IsMoving = false; + return; + } + + CurrentDirection = _inputDirection; + IsMoving = _inputDirection != Vector3.zero; + + var finalVelocity = _inputDirection * (MoveSpeed * MoveSpeedMultiplier); + if (!Rigidbody.isKinematic) + { + Rigidbody.linearVelocity = finalVelocity; + } + } + + // // Dash + // public bool CanDash() + // { + // if (!IsDashEnabled || IsDashing || IsDashCoolDownActive) return false; + // + // return true; + // } + // + // public void OnDash(InputAction.CallbackContext context) + // { + // if (!CanDash()) return; + // + // OnSucceedDash?.Invoke(); + // Dash(); + // } + // + // public void Dash() + // { + // Utils.StartUniqueCoroutine(this, ref _dashInstance, DashCoroutine()); + // } + // + // private IEnumerator DashCoroutine() + // { + // IsDashing = true; + // IsDashCoolDownActive = true; + // if (_dashParticle) + // { + // _dashParticle.Play(); + // } + // + // AudioManager.Instance.PlaySfx(_dashSfxName); + // + // var dashDirection = _inputDirection; + // if (dashDirection == Vector3.zero) + // { + // dashDirection = CurrentDirection; + // } + // + // var elapsedTime = 0f; + // while (elapsedTime <= DashTime) + // { + // var finalVelocity = dashDirection * DashSpeed; + // Rigidbody.linearVelocity = finalVelocity; + // + // elapsedTime += Time.fixedDeltaTime; + // yield return new WaitForFixedUpdate(); + // } + // + // var newDashCooldown = DashCooldown - TycoonManager.Instance.TycoonStatus.PlayerDashCooldownReduction; + // EndDash(newDashCooldown); + // } + // + // public void EndDash(float dashCooldown = float.PositiveInfinity) + // { + // Utils.EndUniqueCoroutine(this, ref _dashInstance); + // Rigidbody.linearVelocity = Vector3.zero; + // IsDashing = false; + // + // if (float.IsPositiveInfinity(dashCooldown)) + // { + // dashCooldown = DashCooldown; + // } + // + // EventManager.InvokeDashCooldown(dashCooldown); + // StartCoroutine(Utils.CoolDownCoroutine(dashCooldown, () => IsDashCoolDownActive = false)); + // } + + #endregion + } +} \ No newline at end of file diff --git a/Assets/Scirpts/CharacterController.cs.meta b/Assets/Scirpts/CharacterController.cs.meta new file mode 100644 index 000000000..5b9f457b2 --- /dev/null +++ b/Assets/Scirpts/CharacterController.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 620909d88f805ee4898b9af964a7f0e8 \ No newline at end of file diff --git a/Assets/Scirpts/PlayerInputKeyManager.cs b/Assets/Scirpts/PlayerInputKeyManager.cs new file mode 100644 index 000000000..cf5afb949 --- /dev/null +++ b/Assets/Scirpts/PlayerInputKeyManager.cs @@ -0,0 +1,299 @@ +using System.Threading.Tasks; +using DDD; +using UnityEngine; +using UnityEngine.InputSystem; +using UnityEngine.SceneManagement; + +public enum InputActionMaps + { + None = 0, + UI = 1, + Tycoon = 2, + TycoonUi = 3, + CombatTitle = 4, + Combat = 5, + CombatUi = 6 + } + + public static class UiActions + { + public const string Navigate = "Navigate"; + } + + public static class TycoonActions + { + public const string Move = "Move"; + public const string Dash = "Dash"; + public const string Interaction = "Interaction"; + public const string DevelopKey01 = "DevelopKey01"; + public const string OpenManualBook = "OpenManualBook"; + public const string ZoomOut = "ZoomOut"; + public const string ZoomIn = "ZoomIn"; + public const string Options = "Options"; + } + + public static class TycoonUiActions + { + public const string Move = "Move"; + public const string Cancel = "Cancel"; + public const string PressQ = "PressQ"; + public const string PressR = "PressR"; + public const string PressA = "PressA"; + public const string PressD = "PressD"; + public const string PressAnyKey = "PressAnyKey"; + public const string InteractionE = "InteractionE"; + public const string PressF = "PressF"; + } + + public class PlayerInputKeyManager : Singleton + { + [SerializeField] + private PlayerInput _currentPlayerInput; + + public bool IsInitialized { get; private set; } + + protected override void OnAwake() + { + base.OnAwake(); + + _currentPlayerInput = GetComponent(); + } + + private async void Start() + { + await Initialize(); + + SceneManager.sceneLoaded += OnSceneLoaded; + } + + private async void OnSceneLoaded(Scene scene, LoadSceneMode mode) + { + await Initialize(); + } + + private void OnDestroy() + { + SceneManager.sceneLoaded -= OnSceneLoaded; + } + + private async Task Initialize() + { + IsInitialized = false; + await Task.Delay(1000); + + DisableAllActionMaps(); + string currentSceneName = SceneManager.GetActiveScene().name; + + // if (currentSceneName == SceneName.TycoonTile) + // { + // SwitchCurrentActionMap(InputActionMaps.TycoonUi); + // } + // else + // { + SwitchCurrentActionMap(InputActionMaps.Tycoon); + // } + + IsInitialized = true; + } + + /// + /// 현재 실행되고 있는 PlayerInput을 관리할 수 있게 + /// PlayerInput 컴포넌트를 받아와서 사용하는 경우에 필수로 호출 + /// + /// + public void SetCurrentPlayerInput(PlayerInput playerInput) => _currentPlayerInput = playerInput; + + private bool IsNullCurrentPlayerInput() + { + if (_currentPlayerInput && _currentPlayerInput.enabled) return false; + + Debug.Log("CurrentPlayerInput가 할당되지 않았습니다."); + return true; + } + + public InputAction GetAction(InputActionMaps actionMapName, string actionName) + { + if (IsNullCurrentPlayerInput()) return null; + + var actionMap = _currentPlayerInput.actions.FindActionMap(actionMapName.ToString(), true); + if (actionMap == null) + { + Debug.LogError($"Action Map '{actionMapName}' not found!"); + return null; + } + + var action = actionMap.FindAction(actionName, true); + if (action == null) + { + Debug.LogError($"Action '{actionName}' not found in Action Map '{actionMapName}'!"); + } + return action; + } + + public string GetBoundKey(InputActionMaps actionMapName, string actionName) + { + if (IsNullCurrentPlayerInput()) return null; + + var actionMap = _currentPlayerInput.actions.FindActionMap(actionMapName.ToString(), true); + if (actionMap == null) + { + Debug.LogError($"Action Map '{actionMapName}' not found!"); + return null; + } + + var action = actionMap.FindAction(actionName, true); + if (action == null) + { + Debug.LogError($"Action '{actionName}' not found in Action Map '{actionMapName}'!"); + return null; + } + + // 첫 번째 바인딩에서 키 이름 가져오기 + foreach (var binding in action.bindings) + { + if (!string.IsNullOrEmpty(binding.path)) + { + // 키 이름만 추출 + var key = InputControlPath.ToHumanReadableString(binding.path, InputControlPath.HumanReadableStringOptions.OmitDevice); + return key; + } + } + + Debug.LogWarning($"No bindings found for action '{actionName}' in Action Map '{actionMapName}'."); + return null; + } + + public string GetBoundKey(InputAction inputAction) + { + if (IsNullCurrentPlayerInput()) return null; + + if (inputAction == null) + { + Debug.LogError($"Action not found'!"); + return null; + } + + // 첫 번째 바인딩에서 키 이름 가져오기 + foreach (var binding in inputAction.bindings) + { + if (!string.IsNullOrEmpty(binding.path)) + { + // 키 이름만 추출 + var key = InputControlPath.ToHumanReadableString(binding.path, InputControlPath.HumanReadableStringOptions.OmitDevice); + return key; + } + } + + Debug.LogWarning($"No bindings found for action '{inputAction}'"); + return null; + } + + public bool IsCurrentActionMap(InputActionMaps inputActionMaps) + { + if (IsNullCurrentPlayerInput()) return false; + + return _currentPlayerInput.currentActionMap.ToString() == inputActionMaps.ToString(); + } + + public void SwitchCurrentActionMap(string inputActionMaps) + { + if (IsNullCurrentPlayerInput()) return; + + _currentPlayerInput.SwitchCurrentActionMap(inputActionMaps); + } + + public void SwitchCurrentActionMap(InputActionMaps inputActionMaps) + { + if (IsNullCurrentPlayerInput()) return; + + _currentPlayerInput.SwitchCurrentActionMap(inputActionMaps.ToString()); + } + + public InputActionMap GetCurrentInputActionMap() + { + if (IsNullCurrentPlayerInput()) return null; + + return _currentPlayerInput.currentActionMap; + } + + public void EnableCurrentPlayerInput() + { + if (!_currentPlayerInput) return; + + _currentPlayerInput.enabled = true; + } + + public void DisableCurrentPlayerInput() + { + if (IsNullCurrentPlayerInput()) return; + + _currentPlayerInput.enabled = false; + } + + public void DisableAllActionMaps() + { + if (IsNullCurrentPlayerInput()) return; + + foreach (var element in _currentPlayerInput.actions.actionMaps) + { + element.Disable(); + } + } + + public void DisableAllActionsExcept(string exceptActionName) + { + if (IsNullCurrentPlayerInput()) return; + + var exceptAction = _currentPlayerInput.currentActionMap.FindAction(exceptActionName); + + foreach (var action in _currentPlayerInput.currentActionMap.actions) + { + if (action != exceptAction) + { + action.Disable(); + } + else + { + action.Enable(); + } + } + } + + public void EnableAllActionsMaps() + { + if (IsNullCurrentPlayerInput()) return; + + foreach (var action in _currentPlayerInput.actions) + { + action.Enable(); + } + } + + public void EnableAction(string actionName) + { + if (IsNullCurrentPlayerInput()) return; + + var action = _currentPlayerInput.currentActionMap.FindAction(actionName); + if (action == null) + { + Debug.Log($"현재 Action Map인 {_currentPlayerInput.currentActionMap}에는 {actionName} Action이 존재하지 않습니다"); + return; + } + + action.Enable(); + } + + public void DisableAction(string actionName) + { + if (IsNullCurrentPlayerInput()) return; + + var action = _currentPlayerInput.currentActionMap.FindAction(actionName); + if (action == null) + { + Debug.Log($"현재 Action Map인 {_currentPlayerInput.currentActionMap}에는 {actionName} Action이 존재하지 않습니다"); + return; + } + + action.Disable(); + } + } \ No newline at end of file diff --git a/Assets/Scirpts/PlayerInputKeyManager.cs.meta b/Assets/Scirpts/PlayerInputKeyManager.cs.meta new file mode 100644 index 000000000..a4d762ad0 --- /dev/null +++ b/Assets/Scirpts/PlayerInputKeyManager.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: f3a9c6e7027b3d944ae69e5e7ccc7627 \ No newline at end of file