Files
BABA_YAGA/Assets/Invector-3rdPersonController/Basic Locomotion/Scripts/CharacterController/vThirdPersonInput.cs

797 lines
24 KiB
C#
Raw Normal View History

2026-05-30 09:16:35 +07:00
using System.Collections;
2026-05-30 21:35:37 +07:00
using OnlyScove.Scripts;
2026-05-30 09:16:35 +07:00
using UnityEngine;
using UnityEngine.Events;
2026-05-31 10:31:59 +07:00
using UnityEngine.InputSystem;
2026-05-30 09:16:35 +07:00
namespace Invector.vCharacterController
{
[vClassHeader("Input Manager", iconName = "inputIcon")]
public class vThirdPersonInput : vMonoBehaviour, vIAnimatorMoveReceiver
{
public delegate void OnUpdateEvent();
public event OnUpdateEvent onUpdate;
public event OnUpdateEvent onLateUpdate;
public event OnUpdateEvent onFixedUpdate;
public event OnUpdateEvent onAnimatorMove;
#region Variables
[vEditorToolbar("Inputs")]
[vHelpBox("Check these options if you need to use the mouse cursor, ex: <b>2.5D, Topdown or Mobile</b>", vHelpBoxAttribute.MessageType.Info)]
public bool unlockCursorOnStart = false;
public bool showCursorOnStart = false;
[vHelpBox("PC only - use it to toggle between run/walk", vHelpBoxAttribute.MessageType.Info)]
public KeyCode toggleWalk = KeyCode.CapsLock;
[Header("Movement Input")]
2026-05-31 10:31:59 +07:00
// public GenericInput horizontalInput = new GenericInput("Horizontal", "LeftAnalogHorizontal", "Horizontal");
// public GenericInput verticalInput = new GenericInput("Vertical", "LeftAnalogVertical", "Vertical");
// public GenericInput sprintInput = new GenericInput("LeftShift", "LeftStickClick", "LeftStickClick");
// public GenericInput crouchInput = new GenericInput("C", "Y", "Y");
// public GenericInput strafeInput = new GenericInput("Tab", "RightStickClick", "RightStickClick");
// public GenericInput jumpInput = new GenericInput("Space", "X", "X");
// public GenericInput rollInput = new GenericInput("Q", "B", "B");
[Header("New Input System")]
public OnlyScove.Scripts.InputReader inputReader;
2026-05-30 09:16:35 +07:00
protected bool _lockInput = false;
[HideInInspector] public virtual bool lockInput { get { return _lockInput; } set { _lockInput = value; } }
[vEditorToolbar("Camera Settings")]
public bool lockCameraInput;
public bool invertCameraInputVertical, invertCameraInputHorizontal;
2026-05-31 10:31:59 +07:00
// [vEditorToolbar("Inputs")]
2026-05-30 09:16:35 +07:00
[Header("Camera Input")]
2026-05-31 10:31:59 +07:00
// public GenericInput rotateCameraXInput = new GenericInput("Mouse X", "RightAnalogHorizontal", "Mouse X");
// public GenericInput rotateCameraYInput = new GenericInput("Mouse Y", "RightAnalogVertical", "Mouse Y");
// public GenericInput cameraZoomInput = new GenericInput("Mouse ScrollWheel", "", "");
2026-05-30 09:16:35 +07:00
[vEditorToolbar("Events")]
public UnityEvent OnLockCamera;
public UnityEvent OnUnlockCamera;
public UnityEvent onEnableAnimatorMove = new UnityEvent();
public UnityEvent onDisableDisableAnimatorMove = new UnityEvent();
[HideInInspector]
public vCamera.vThirdPersonCamera tpCamera; // access tpCamera info
[HideInInspector]
public bool ignoreTpCamera; // controls whether update the cameraStates of not
[HideInInspector]
public string customCameraState; // generic string to change the CameraState
[HideInInspector]
public string customlookAtPoint; // generic string to change the CameraPoint of the Fixed Point Mode
[HideInInspector]
public bool changeCameraState; // generic bool to change the CameraState
[HideInInspector]
public bool smoothCameraState; // generic bool to know if the state will change with or without lerp
[HideInInspector]
public vThirdPersonController cc; // access the ThirdPersonController component
[HideInInspector]
public vHUDController hud; // access vHUDController component
protected bool updateIK = false;
protected bool isInit;
[HideInInspector] public bool lockMoveInput;
protected InputDevice inputDevice { get { return vInput.instance.inputDevice; } }
protected Camera _cameraMain;
protected bool withoutMainCamera;
public virtual bool lockUpdateMoveDirection { get; set; } // lock the method UpdateMoveDirection
public virtual Camera cameraMain
{
get
{
if (!_cameraMain && !withoutMainCamera)
{
if (!Camera.main)
{
Debug.Log("Missing a Camera with the tag MainCamera, please add one.");
withoutMainCamera = true;
}
else
{
_cameraMain = Camera.main;
cc.rotateTarget = _cameraMain.transform;
}
}
return _cameraMain;
}
set
{
_cameraMain = value;
}
}
public Animator animator
{
get
{
if (cc == null)
{
cc = GetComponent<vThirdPersonController>();
}
if (cc.animator == null)
{
return GetComponent<Animator>();
}
return cc.animator;
}
}
#endregion
#region Initialize Character, Camera & HUD when LoadScene
protected virtual void Start()
{
cc = GetComponent<vThirdPersonController>();
if (cc != null)
{
cc.Init();
}
cc.onDead.AddListener((GameObject _gameObject) => { cc.ResetInputAnimatorParameters(); SetLockAllInput(true); cc.StopCharacter(); });
StartCoroutine(CharacterInit());
ShowCursor(showCursorOnStart);
LockCursor(unlockCursorOnStart);
EnableOnAnimatorMove();
2026-05-31 10:31:59 +07:00
if (inputReader != null)
{
// Nhảy (Jump)
inputReader.OnJumpEvent += () =>
{
if (JumpConditions()) cc.Jump(true);
};
// Lộn vòng (Roll / Dodge)
inputReader.OnDodgeEvent += () =>
{
if (RollConditions()) cc.Roll();
};
// Ngồi (Crouch)
inputReader.OnCrouchEvent += () =>
{
cc.AutoCrouch();
cc.Crouch();
};
// Đổi góc nhìn hoặc Khóa mục tiêu (Strafe) - Tuỳ bạn map nút nào vào OnToggleViewEvent
inputReader.OnToggleViewEvent += () =>
{
cc.Strafe();
};
}
2026-05-30 09:16:35 +07:00
}
protected virtual IEnumerator CharacterInit()
{
FindCamera();
yield return new WaitForEndOfFrame();
FindHUD();
}
public virtual void FindHUD()
{
if (hud == null && vHUDController.instance != null)
{
hud = vHUDController.instance;
hud.Init(cc);
}
}
public virtual void FindCamera()
{
var tpCameras = FindObjectsOfType<vCamera.vThirdPersonCamera>();
if (tpCameras.Length > 1)
{
tpCamera = System.Array.Find(tpCameras, tp => !tp.isInit);
if (tpCamera == null)
{
tpCamera = tpCameras[0];
}
if (tpCamera != null)
{
for (int i = 0; i < tpCameras.Length; i++)
{
if (tpCamera != tpCameras[i])
{
Destroy(tpCameras[i].gameObject);
}
}
}
}
else if (tpCameras.Length == 1)
{
tpCamera = tpCameras[0];
}
if (tpCamera && tpCamera.mainTarget != transform)
{
tpCamera.SetMainTarget(this.transform);
}
}
#endregion
protected virtual void LateUpdate()
{
if (cc == null)
{
return;
}
if (!updateIK)
{
return;
}
if (onLateUpdate != null)
{
onLateUpdate.Invoke();
}
CameraInput(); // update camera input
UpdateCameraStates(); // update camera states
updateIK = false;
}
protected virtual void FixedUpdate()
{
if (onFixedUpdate != null)
{
onFixedUpdate.Invoke();
}
Physics.SyncTransforms();
cc.UpdateMotor(); // handle the ThirdPersonMotor methods
cc.ControlLocomotionType(); // handle the controller locomotion type and movespeed
ControlRotation();
cc.UpdateAnimator(); // handle the ThirdPersonAnimator methods
updateIK = true;
}
protected virtual void Update()
{
if (cc == null || Time.timeScale == 0)
{
return;
}
if (onUpdate != null)
{
onUpdate.Invoke();
}
InputHandle(); // update input methods
UpdateHUD(); // update hud graphics
}
public virtual void OnAnimatorMoveEvent()
{
if (cc == null)
{
return;
}
cc.ControlAnimatorRootMotion();
if (onAnimatorMove != null)
{
onAnimatorMove.Invoke();
}
}
#region Generic Methods
// you can call this methods anywhere in the inspector or third party assets to have better control of the controller or cutscenes
/// <summary>
/// Lock all Basic Input from the Player
/// </summary>
/// <param name="value"></param>
public virtual void SetLockBasicInput(bool value)
{
lockInput = value;
if (value)
{
//cc.input = Vector2.zero;
//cc.isSprinting = false;
//cc.animator.SetFloat("InputHorizontal", 0, 0.25f, Time.deltaTime);
//cc.animator.SetFloat("InputVertical", 0, 0.25f, Time.deltaTime);
//cc.animator.SetFloat("InputMagnitude", 0, 0.25f, Time.deltaTime);
}
}
/// <summary>
/// Lock all Inputs
/// </summary>
/// <param name="value"></param>
public virtual void SetLockAllInput(bool value)
{
SetLockBasicInput(value);
}
/// <summary>
/// Show/Hide Cursor
/// </summary>
/// <param name="value"></param>
public virtual void ShowCursor(bool value)
{
Cursor.visible = value;
}
/// <summary>
/// Lock/Unlock the cursor to the center of screen
/// </summary>
/// <param name="value"></param>
public virtual void LockCursor(bool value)
{
if (!value)
{
Cursor.lockState = CursorLockMode.Locked;
}
else
{
Cursor.lockState = CursorLockMode.None;
}
}
/// <summary>
/// Lock the Camera Input
/// </summary>
/// <param name="value"></param>
public virtual void SetLockCameraInput(bool value)
{
lockCameraInput = value;
if (lockCameraInput)
{
OnLockCamera.Invoke();
}
else
{
OnUnlockCamera.Invoke();
}
}
/// <summary>
/// If you're using the MoveCharacter method with a custom targetDirection, check this true to align the character with your custom targetDirection
/// </summary>
/// <param name="value"></param>
public virtual void SetLockUpdateMoveDirection(bool value)
{
lockUpdateMoveDirection = value;
}
/// <summary>
/// Limits the character to walk only, useful for cutscenes and 'indoor' areas
/// </summary>
/// <param name="value"></param>
public virtual void SetWalkByDefault(bool value)
{
cc.freeSpeed.walkByDefault = value;
cc.strafeSpeed.walkByDefault = value;
}
/// <summary>
/// Reset the character to the default walk settings
/// </summary>
public virtual void ResetWalkByDefault()
{
cc.freeSpeed.walkByDefault = cc.freeSpeed.defaultWalkByDefault;
cc.strafeSpeed.walkByDefault = cc.strafeSpeed.defaultWalkByDefault;
}
/// <summary>
/// Set the character to Strafe Locomotion
/// </summary>
/// <param name="value"></param>
public virtual void SetStrafeLocomotion(bool value)
{
cc.lockInStrafe = value;
cc.isStrafing = value;
}
/// <summary>
/// OnAnimatorMove Event Sender
/// </summary>
public virtual vAnimatorMoveSender animatorMoveSender { get; set; }
/// <summary>
/// Use Animator Move Event Sender <seealso cref="vAnimatorMoveSender"/>
/// </summary>
protected bool _useAnimatorMove { get; set; }
/// <summary>
/// Check if OnAnimatorMove is Enabled
/// </summary>
public virtual bool UseAnimatorMove
{
get
{
return _useAnimatorMove;
}
set
{
if (_useAnimatorMove != value)
{
if (value)
{
animatorMoveSender = gameObject.AddComponent<vAnimatorMoveSender>();
onEnableAnimatorMove?.Invoke();
}
else
{
if (animatorMoveSender)
{
Destroy(animatorMoveSender);
}
onEnableAnimatorMove?.Invoke();
}
}
_useAnimatorMove = value;
}
}
/// <summary>
/// Enable OnAnimatorMove event
/// </summary>
public virtual void EnableOnAnimatorMove()
{
UseAnimatorMove = true;
}
/// <summary>
/// Disable OnAnimatorMove event
/// </summary>
public virtual void DisableOnAnimatorMove()
{
UseAnimatorMove = false;
}
#endregion
#region Basic Locomotion Inputs
public virtual void InputHandle()
{
if (lockInput || cc.ragdolled)
{
return;
}
MoveInput();
SprintInput();
CrouchInput();
StrafeInput();
JumpInput();
RollInput();
}
public virtual void MoveInput()
{
2026-05-31 10:31:59 +07:00
if (inputReader == null) return;
2026-05-30 09:16:35 +07:00
if (!lockMoveInput)
{
// gets input
var input = cc.input;
2026-05-31 10:31:59 +07:00
// input.x = horizontalInput.GetAxisRaw();
// input.z = verticalInput.GetAxisRaw();
// Input.GetAxisRaw("Horizontal");
// Input.GetAxisRaw("Vertical");
input.x = inputReader.MoveInput.x;
input.z = inputReader.MoveInput.y;
2026-05-30 09:16:35 +07:00
cc.input = input;
}
2026-05-31 10:31:59 +07:00
//if (Input.GetKeyDown(toggleWalk))
//{
//cc.alwaysWalkByDefault = !cc.alwaysWalkByDefault;
//}
Input.GetKeyDown(KeyCode.CapsLock);
2026-05-30 09:16:35 +07:00
cc.ControlKeepDirection();
}
public virtual bool rotateToLockTargetConditions => tpCamera && tpCamera.lockTarget && cc.isStrafing && !cc.isRolling && !cc.isJumping && !cc.customAction;
public virtual void ControlRotation()
{
if (cameraMain && !lockUpdateMoveDirection)
{
if (!cc.keepDirection)
{
cc.UpdateMoveDirection(cameraMain.transform);
}
}
if (rotateToLockTargetConditions)
{
cc.RotateToPosition(tpCamera.lockTarget.position); // rotate the character to a specific target
}
else
{
cc.ControlRotationType(); // handle the controller rotation type (strafe or free)
}
}
public virtual void StrafeInput()
{
2026-05-31 10:31:59 +07:00
// if (strafeInput.GetButtonDown())
// {
// cc.Strafe();
// }
2026-05-30 09:16:35 +07:00
}
public virtual void SprintInput()
{
2026-05-31 10:31:59 +07:00
// if (sprintInput.useInput)
// {
// cc.Sprint(cc.useContinuousSprint ? sprintInput.GetButtonDown() : sprintInput.GetButton());
// }
if (inputReader == null) return;
cc.Sprint(inputReader.IsSprintHeld);
2026-05-30 09:16:35 +07:00
}
public virtual void CrouchInput()
{
cc.AutoCrouch();
2026-05-31 10:31:59 +07:00
// if (crouchInput.useInput && crouchInput.GetButtonDown())
// {
// cc.Crouch();
// }
2026-05-30 09:16:35 +07:00
}
/// <summary>
/// Conditions to trigger the Jump animation & behavior
/// </summary>
/// <returns></returns>
public virtual bool JumpConditions()
{
return !cc.inJumpStarted && !cc.customAction && !cc.isCrouching && cc.isGrounded && !((int)cc.GroundAngle() > cc.slopeLimit) && cc.currentStamina >= cc.jumpStamina && !cc.isJumping && !cc.isRolling;
}
/// <summary>
/// Input to trigger the Jump
/// </summary>
public virtual void JumpInput()
{
2026-05-31 10:31:59 +07:00
// if (jumpInput.GetButtonDown() && JumpConditions())
// {
// cc.Jump(true);
// }
2026-05-30 09:16:35 +07:00
}
/// <summary>
/// Conditions to trigger the Roll animation & behavior
/// </summary>
/// <returns></returns>
public virtual bool RollConditions()
{
return (!cc.isRolling || cc.canRollAgain) && cc.isGrounded && cc.input != Vector3.zero && !cc.customAction && cc.currentStamina > cc.rollStamina && !cc.isJumping && !cc.isSliding;
}
/// <summary>
/// Input to trigger the Roll
/// </summary>
public virtual void RollInput()
{
2026-05-31 10:31:59 +07:00
// if (rollInput.GetButtonDown() && RollConditions())
// {
// cc.Roll();
// }
2026-05-30 09:16:35 +07:00
}
#endregion
#region Camera Methods
public virtual void CameraInput()
{
if (!cameraMain)
{
return;
}
if (tpCamera == null)
{
return;
}
2026-05-31 10:31:59 +07:00
if (!cameraMain || tpCamera == null || inputReader == null) return;
// var Y = lockCameraInput ? 0f : rotateCameraYInput.GetAxis();
// var X = lockCameraInput ? 0f : rotateCameraXInput.GetAxis();
var Y = lockCameraInput ? 0f : inputReader.LookInput.y;
var X = lockCameraInput ? 0f : inputReader.LookInput.x;
if (invertCameraInputHorizontal) X *= -1;
if (invertCameraInputVertical) Y *= -1;
// var zoom = cameraZoomInput.GetAxis();
var zoom = inputReader.ScrollInput.y;
2026-05-30 09:16:35 +07:00
tpCamera.RotateCamera(X, Y);
if (!lockCameraInput)
{
tpCamera.Zoom(zoom);
}
}
public virtual void UpdateCameraStates()
{
// CAMERA STATE - you can change the CameraState here, the bool means if you want lerp of not, make sure to use the same CameraState String that you named on TPCameraListData
if (ignoreTpCamera)
{
return;
}
if (tpCamera == null)
{
tpCamera = FindObjectOfType<vCamera.vThirdPersonCamera>();
if (tpCamera == null)
{
return;
}
if (tpCamera)
{
tpCamera.SetMainTarget(this.transform);
tpCamera.Init();
}
}
if (changeCameraState)
{
tpCamera.ChangeState(customCameraState, customlookAtPoint, smoothCameraState);
}
else if (cc.isCrouching)
{
tpCamera.ChangeState("Crouch", true);
}
else if (cc.isStrafing)
{
tpCamera.ChangeState("Strafing", true);
}
else
{
tpCamera.ChangeState("Default", true);
}
}
public virtual void ChangeCameraState(string cameraState, bool useLerp = true)
{
if (useLerp)
{
ChangeCameraStateWithLerp(cameraState);
}
else
{
ChangeCameraStateNoLerp(cameraState);
}
}
public virtual void ResetCameraAngleSmooth()
{
if (tpCamera)
{
tpCamera.ResetAngle();
}
}
public virtual void ResetCameraAngleWithoutSmooth()
{
if (tpCamera)
{
tpCamera.ResetAngleWithoutSmooth();
}
}
public virtual void ChangeCameraStateWithLerp(string cameraState)
{
changeCameraState = true;
customCameraState = cameraState;
smoothCameraState = true;
}
public virtual void ChangeCameraStateNoLerp(string cameraState)
{
changeCameraState = true;
customCameraState = cameraState;
smoothCameraState = false;
}
public virtual void ResetCameraState()
{
changeCameraState = false;
customCameraState = string.Empty;
}
#endregion
#region HUD
public virtual void UpdateHUD()
{
if (hud == null)
{
if (vHUDController.instance != null)
{
hud = vHUDController.instance;
hud.Init(cc);
}
else
{
return;
}
}
hud.UpdateHUD(cc);
}
#endregion
}
/// <summary>
/// Interface to receive events from <seealso cref="vAnimatorMoveSender"/>
/// </summary>
public interface vIAnimatorMoveReceiver
{
/// <summary>
/// Check if Component is Enabled
/// </summary>
bool enabled { get; set; }
/// <summary>
/// Method Called from <seealso cref="vAnimatorMoveSender"/>
/// </summary>
void OnAnimatorMoveEvent();
}
/// <summary>
/// OnAnimatorMove Event Sender
/// </summary>
public class vAnimatorMoveSender : MonoBehaviour
{
protected virtual void Awake()
{
///Hide in Inpector
this.hideFlags = HideFlags.HideInInspector;
vIAnimatorMoveReceiver[] animatorMoves = GetComponents<vIAnimatorMoveReceiver>();
for (int i = 0; i < animatorMoves.Length; i++)
{
var receiver = animatorMoves[i];
animatorMoveEvent += () =>
{
if (receiver.enabled)
{
receiver.OnAnimatorMoveEvent();
}
};
}
}
/// <summary>
/// AnimatorMove event called using default unity OnAnimatorMove
/// </summary>
public System.Action animatorMoveEvent;
protected virtual void OnAnimatorMove()
{
animatorMoveEvent?.Invoke();
}
}
}