2026-03-26 20:27:19 +07:00
|
|
|
using System;
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
using UnityEngine.InputSystem;
|
|
|
|
|
|
|
|
|
|
namespace OnlyScove.Scripts
|
|
|
|
|
{
|
|
|
|
|
public class InputReader : MonoBehaviour
|
|
|
|
|
{
|
2026-04-29 01:04:28 +07:00
|
|
|
[SerializeField] private InputActionAsset inputActions;
|
|
|
|
|
public InputActionAsset InputActions => inputActions;
|
|
|
|
|
|
2026-05-01 02:25:25 +07:00
|
|
|
private const string REBINDS_KEY = "InputRebinds";
|
|
|
|
|
|
|
|
|
|
private void OnEnable()
|
|
|
|
|
{
|
|
|
|
|
if (inputActions != null)
|
|
|
|
|
{
|
|
|
|
|
LoadBindings();
|
|
|
|
|
inputActions.Enable();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnDisable()
|
|
|
|
|
{
|
|
|
|
|
if (inputActions != null)
|
|
|
|
|
{
|
|
|
|
|
inputActions.Disable();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SaveBindings()
|
|
|
|
|
{
|
|
|
|
|
if (inputActions == null) return;
|
|
|
|
|
string rebinds = inputActions.SaveBindingOverridesAsJson();
|
|
|
|
|
PlayerPrefs.SetString(REBINDS_KEY, rebinds);
|
|
|
|
|
PlayerPrefs.Save();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void LoadBindings()
|
|
|
|
|
{
|
|
|
|
|
if (inputActions == null) return;
|
|
|
|
|
string rebinds = PlayerPrefs.GetString(REBINDS_KEY, string.Empty);
|
|
|
|
|
if (!string.IsNullOrEmpty(rebinds))
|
|
|
|
|
{
|
|
|
|
|
inputActions.LoadBindingOverridesFromJson(rebinds);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void ResetBindings()
|
|
|
|
|
{
|
|
|
|
|
if (inputActions == null) return;
|
|
|
|
|
inputActions.RemoveAllBindingOverrides();
|
|
|
|
|
PlayerPrefs.DeleteKey(REBINDS_KEY);
|
|
|
|
|
PlayerPrefs.Save();
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-26 20:27:19 +07:00
|
|
|
// Continuous Inputs
|
|
|
|
|
public virtual Vector2 MoveInput { get; protected set; }
|
|
|
|
|
public virtual Vector2 LookInput { get; protected set; }
|
|
|
|
|
public virtual Vector2 ScrollInput { get; protected set; }
|
2026-04-28 11:35:49 +07:00
|
|
|
public virtual bool IsSprintHeld { get; protected set; }
|
|
|
|
|
public virtual bool IsAttackHeld { get; protected set; }
|
2026-05-31 10:31:59 +07:00
|
|
|
|
|
|
|
|
public bool IsAimHeld { get; protected set; }
|
|
|
|
|
public bool IsBlockHeld { get; protected set; }
|
2026-06-04 12:42:00 +07:00
|
|
|
public bool IsInteractHeld { get; protected set; }
|
|
|
|
|
public bool IsScopeViewHeld { get; protected set; }
|
2026-04-03 22:46:17 +07:00
|
|
|
|
|
|
|
|
public void ApplyNetworkInput(Vector2 move, bool isSprint)
|
|
|
|
|
{
|
|
|
|
|
MoveInput = move;
|
|
|
|
|
IsSprintHeld = isSprint;
|
|
|
|
|
}
|
2026-03-26 20:27:19 +07:00
|
|
|
|
|
|
|
|
// One-shot Events
|
2026-04-28 11:35:49 +07:00
|
|
|
public event Action OnJumpEvent;
|
|
|
|
|
public event Action OnDodgeEvent;
|
|
|
|
|
public event Action OnSprintEvent;
|
|
|
|
|
public event Action OnAttackEvent;
|
|
|
|
|
public event Action OnCrouchEvent;
|
|
|
|
|
public event Action OnInteractEvent;
|
|
|
|
|
public event Action OnNextInteractEvent;
|
|
|
|
|
public event Action OnPreviousInteractEvent;
|
|
|
|
|
public event Action OnToggleViewEvent;
|
2026-05-31 10:31:59 +07:00
|
|
|
|
|
|
|
|
public event Action OnReloadEvent;
|
|
|
|
|
public event Action OnStrongAttackEvent;
|
|
|
|
|
public event Action OnSwitchSideEvent;
|
2026-06-04 12:42:00 +07:00
|
|
|
public event Action OnScopeViewEvent;
|
2026-04-28 11:35:49 +07:00
|
|
|
|
|
|
|
|
// UI Events
|
|
|
|
|
public event Action OnToggleSettingsEvent; // Cho Ctrl+O
|
|
|
|
|
public event Action OnCancelEvent; // Cho phím ESC hoặc phím đóng UI
|
2026-03-26 20:27:19 +07:00
|
|
|
|
2026-06-04 12:42:00 +07:00
|
|
|
// Polling flags
|
|
|
|
|
private bool wasAttackPressed;
|
|
|
|
|
private bool wasStrongAttackPressed;
|
|
|
|
|
private bool wasReloadPressed;
|
|
|
|
|
private bool wasSwitchSidePressed;
|
|
|
|
|
private bool wasScopeViewPressed;
|
|
|
|
|
private bool wasInteractPressed;
|
|
|
|
|
private bool wasDodgePressed;
|
|
|
|
|
private bool wasCrouchPressed;
|
|
|
|
|
private bool wasJumpPressed;
|
|
|
|
|
private bool wasAimReleased;
|
|
|
|
|
private bool wasNextPressed;
|
|
|
|
|
private bool wasPreviousPressed;
|
|
|
|
|
private bool wasToggleViewPressed;
|
|
|
|
|
|
|
|
|
|
public bool ConsumeAttack() { bool v = wasAttackPressed; wasAttackPressed = false; return v; }
|
|
|
|
|
public bool ConsumeStrongAttack() { bool v = wasStrongAttackPressed; wasStrongAttackPressed = false; return v; }
|
|
|
|
|
public bool ConsumeReload() { bool v = wasReloadPressed; wasReloadPressed = false; return v; }
|
|
|
|
|
public bool ConsumeSwitchSide() { bool v = wasSwitchSidePressed; wasSwitchSidePressed = false; return v; }
|
|
|
|
|
public bool ConsumeScopeView() { bool v = wasScopeViewPressed; wasScopeViewPressed = false; return v; }
|
|
|
|
|
public bool ConsumeInteract() { bool v = wasInteractPressed; wasInteractPressed = false; return v; }
|
|
|
|
|
public bool ConsumeDodge() { bool v = wasDodgePressed; wasDodgePressed = false; return v; }
|
|
|
|
|
public bool ConsumeCrouch() { bool v = wasCrouchPressed; wasCrouchPressed = false; return v; }
|
|
|
|
|
public bool ConsumeJump() { bool v = wasJumpPressed; wasJumpPressed = false; return v; }
|
|
|
|
|
public bool ConsumeAimReleased() { bool v = wasAimReleased; wasAimReleased = false; return v; }
|
|
|
|
|
public bool ConsumeNext() { bool v = wasNextPressed; wasNextPressed = false; return v; }
|
|
|
|
|
public bool ConsumePrevious() { bool v = wasPreviousPressed; wasPreviousPressed = false; return v; }
|
|
|
|
|
public bool ConsumeToggleView() { bool v = wasToggleViewPressed; wasToggleViewPressed = false; return v; }
|
|
|
|
|
|
2026-03-26 20:27:19 +07:00
|
|
|
public void OnAttack(InputAction.CallbackContext context)
|
|
|
|
|
{
|
|
|
|
|
if (context.performed)
|
|
|
|
|
{
|
2026-06-04 12:42:00 +07:00
|
|
|
wasAttackPressed = true;
|
2026-03-26 20:27:19 +07:00
|
|
|
OnAttackEvent?.Invoke();
|
|
|
|
|
IsAttackHeld = true;
|
|
|
|
|
}
|
|
|
|
|
if (context.canceled)
|
|
|
|
|
{
|
|
|
|
|
IsAttackHeld = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnMove(InputAction.CallbackContext context)
|
|
|
|
|
{
|
|
|
|
|
MoveInput = context.ReadValue<Vector2>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnLook(InputAction.CallbackContext context)
|
|
|
|
|
{
|
|
|
|
|
LookInput = context.ReadValue<Vector2>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnScroll(InputAction.CallbackContext context)
|
|
|
|
|
{
|
|
|
|
|
ScrollInput = context.ReadValue<Vector2>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnSprint(InputAction.CallbackContext context)
|
|
|
|
|
{
|
2026-03-31 08:16:46 +07:00
|
|
|
if (context.performed)
|
|
|
|
|
{
|
|
|
|
|
IsSprintHeld = true;
|
|
|
|
|
OnSprintEvent?.Invoke();
|
|
|
|
|
}
|
2026-03-26 20:27:19 +07:00
|
|
|
if (context.canceled) IsSprintHeld = false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-27 12:08:16 +07:00
|
|
|
public void OnToggleView(InputAction.CallbackContext context)
|
|
|
|
|
{
|
2026-06-04 12:42:00 +07:00
|
|
|
if (context.performed)
|
|
|
|
|
{
|
|
|
|
|
wasToggleViewPressed = true;
|
|
|
|
|
OnToggleViewEvent?.Invoke();
|
|
|
|
|
}
|
2026-03-27 12:08:16 +07:00
|
|
|
}
|
|
|
|
|
|
2026-03-26 20:27:19 +07:00
|
|
|
public void OnJump(InputAction.CallbackContext context)
|
|
|
|
|
{
|
2026-05-30 11:20:59 +07:00
|
|
|
if (context.performed)
|
|
|
|
|
{
|
|
|
|
|
wasJumpPressed = true;
|
|
|
|
|
OnJumpEvent?.Invoke();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool ConsumeJumpInput()
|
|
|
|
|
{
|
|
|
|
|
bool val = wasJumpPressed;
|
|
|
|
|
wasJumpPressed = false;
|
|
|
|
|
return val;
|
2026-03-26 20:27:19 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnDodgeOrThrust(InputAction.CallbackContext context)
|
|
|
|
|
{
|
2026-06-04 12:42:00 +07:00
|
|
|
if (context.performed)
|
|
|
|
|
{
|
|
|
|
|
wasDodgePressed = true;
|
|
|
|
|
OnDodgeEvent?.Invoke();
|
|
|
|
|
}
|
2026-03-26 20:27:19 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnCrouch(InputAction.CallbackContext context)
|
|
|
|
|
{
|
2026-06-04 12:42:00 +07:00
|
|
|
if (context.performed)
|
|
|
|
|
{
|
|
|
|
|
wasCrouchPressed = true;
|
|
|
|
|
OnCrouchEvent?.Invoke();
|
|
|
|
|
}
|
2026-03-26 20:27:19 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnInteract(InputAction.CallbackContext context)
|
|
|
|
|
{
|
2026-06-04 12:42:00 +07:00
|
|
|
if (context.performed)
|
|
|
|
|
{
|
|
|
|
|
wasInteractPressed = true;
|
|
|
|
|
IsInteractHeld = true;
|
|
|
|
|
OnInteractEvent?.Invoke();
|
|
|
|
|
}
|
|
|
|
|
if (context.canceled)
|
|
|
|
|
{
|
|
|
|
|
IsInteractHeld = false;
|
|
|
|
|
}
|
2026-03-26 20:27:19 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnNext(InputAction.CallbackContext context)
|
|
|
|
|
{
|
2026-06-04 12:42:00 +07:00
|
|
|
if (context.performed)
|
|
|
|
|
{
|
|
|
|
|
wasNextPressed = true;
|
|
|
|
|
OnNextInteractEvent?.Invoke();
|
|
|
|
|
}
|
2026-03-26 20:27:19 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnPrevious(InputAction.CallbackContext context)
|
|
|
|
|
{
|
2026-06-04 12:42:00 +07:00
|
|
|
if (context.performed)
|
|
|
|
|
{
|
|
|
|
|
wasPreviousPressed = true;
|
|
|
|
|
OnPreviousInteractEvent?.Invoke();
|
|
|
|
|
}
|
2026-03-26 20:27:19 +07:00
|
|
|
}
|
2026-04-28 11:35:49 +07:00
|
|
|
|
|
|
|
|
// UI Callbacks
|
|
|
|
|
public void OnToggleSettings(InputAction.CallbackContext context)
|
|
|
|
|
{
|
|
|
|
|
if (context.performed)
|
|
|
|
|
{
|
|
|
|
|
Debug.Log("[InputReader] Toggle Settings Action Performed!");
|
|
|
|
|
OnToggleSettingsEvent?.Invoke();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnCancel(InputAction.CallbackContext context)
|
|
|
|
|
{
|
2026-04-28 13:07:52 +07:00
|
|
|
if (context.performed)
|
|
|
|
|
{
|
|
|
|
|
Debug.Log("[InputReader] Cancel Action Performed (ESC)!");
|
|
|
|
|
OnCancelEvent?.Invoke();
|
|
|
|
|
}
|
2026-04-28 11:35:49 +07:00
|
|
|
}
|
2026-05-31 10:31:59 +07:00
|
|
|
public void OnAim(InputAction.CallbackContext context)
|
|
|
|
|
{
|
|
|
|
|
if (context.performed) IsAimHeld = true;
|
2026-06-04 12:42:00 +07:00
|
|
|
if (context.canceled)
|
|
|
|
|
{
|
|
|
|
|
IsAimHeld = false;
|
|
|
|
|
wasAimReleased = true;
|
|
|
|
|
}
|
2026-05-31 10:31:59 +07:00
|
|
|
}
|
|
|
|
|
public void OnBlock(InputAction.CallbackContext context)
|
|
|
|
|
{
|
|
|
|
|
if (context.performed) IsBlockHeld = true;
|
|
|
|
|
if (context.canceled) IsBlockHeld = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnStrongAttack(InputAction.CallbackContext context)
|
|
|
|
|
{
|
2026-06-04 12:42:00 +07:00
|
|
|
if (context.performed)
|
|
|
|
|
{
|
|
|
|
|
wasStrongAttackPressed = true;
|
|
|
|
|
OnStrongAttackEvent?.Invoke();
|
|
|
|
|
}
|
2026-05-31 10:31:59 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnReload(InputAction.CallbackContext context)
|
|
|
|
|
{
|
2026-06-04 12:42:00 +07:00
|
|
|
if (context.performed)
|
|
|
|
|
{
|
|
|
|
|
wasReloadPressed = true;
|
|
|
|
|
OnReloadEvent?.Invoke();
|
|
|
|
|
}
|
2026-05-31 10:31:59 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnSwitchSide(InputAction.CallbackContext context)
|
|
|
|
|
{
|
2026-06-04 12:42:00 +07:00
|
|
|
if (context.performed)
|
|
|
|
|
{
|
|
|
|
|
wasSwitchSidePressed = true;
|
|
|
|
|
OnSwitchSideEvent?.Invoke();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnScopeView(InputAction.CallbackContext context)
|
|
|
|
|
{
|
|
|
|
|
if (context.performed)
|
|
|
|
|
{
|
|
|
|
|
wasScopeViewPressed = true;
|
|
|
|
|
IsScopeViewHeld = true;
|
|
|
|
|
OnScopeViewEvent?.Invoke();
|
|
|
|
|
}
|
|
|
|
|
if (context.canceled)
|
|
|
|
|
{
|
|
|
|
|
IsScopeViewHeld = false;
|
|
|
|
|
}
|
2026-05-31 10:31:59 +07:00
|
|
|
}
|
2026-03-26 20:27:19 +07:00
|
|
|
}
|
2026-04-28 11:35:49 +07:00
|
|
|
}
|