本文详细介绍Unity中的New Input System。这是Unity全新的输入系统,比旧的输入系统更加强大和灵活,可以更好地管理复杂的输入(如手柄、键盘、鼠标、触摸屏等),并且更好地支持跨平台游戏开发。
以创建Player输入动作为例:
注意:要区分Action Properties下的Action Type(动作类型)和Control Type(输入设备类型)
Player Input
组件在游戏对象下添加Player Input组件,用于管理输入动作。
推荐使用Invoke Unity Events,便于在Unity事件中管理输入动作
代码均为可实现代码,博主非常懒没有剪成代码块,如有需要,在Unity中配置好后,复制粘贴直接就可使用!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerReconstruction : MonoBehaviour
{
[SerializeField] private Transform cameraTransform;
private CharacterController controller;
private float moveSpeed = 5f;
private float jumpForce = 5f;
private float sensitivity = 2f;
private float gravity = -9.81f;
private Vector3 moveDirection;
private Vector3 moveVelocity;
private float velocityRotation;
private float verticalVelocity;
private ActionsReconstruction actionsReconstruction;
void Awake()
{
controller = GetComponent<CharacterController>();
actionsReconstruction = new ActionsReconstruction();
}
void OnEnable()
{
actionsReconstruction.Enable();
}
void OnDisable()
{
actionsReconstruction.Disable();
}
void Update()
{
HandleMove();
HandleLook();
HandleJump();
}
private void HandleMove()
{
Vector2 moveInput = actionsReconstruction.Player.Move.ReadValue<Vector2>();
moveDirection = transform.right * moveInput.x + transform.forward * moveInput.y;
moveVelocity = moveDirection * moveSpeed;
Vector3 finalVelocity = moveVelocity + Vector3.up * verticalVelocity;
controller.Move(finalVelocity * Time.deltaTime);
}
private void HandleLook()
{
Vector2 lookInput = actionsReconstruction.Player.Look.ReadValue<Vector2>();
transform.Rotate(Vector3.up * lookInput.x * sensitivity * Time.deltaTime);
velocityRotation -= lookInput.y * sensitivity * Time.deltaTime;
velocityRotation = Mathf.Clamp(velocityRotation, -90f, 90f);
cameraTransform.localRotation = Quaternion.Euler(velocityRotation, 0f, 0f);
}
private void HandleJump()
{
if (controller.isGrounded)
{
verticalVelocity = -0.5f;
}
else
{
verticalVelocity += gravity * Time.deltaTime;
}
}
}
上述实现方式利用轮训式正确的调用了全新的输入系统,但是我们会发现,如果在Update中调用,会存在卡顿的现象,这是因为NewInputSystem的输入动作是每帧都会调用的,但是我们每一帧都会执行一遍Update方法,我们就可能想到用事件调用的方式来解决这个问题。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem; // 添加InputSystem命名空间
public class PlayerReconstruction : MonoBehaviour
{
[SerializeField] private Transform cameraTransform;
private CharacterController controller;
private float moveSpeed = 5f;
private float jumpForce = 5f;
private float sensitivity = 2f;
private float gravity = -9.81f;
private Vector3 moveDirection;
private Vector3 moveVelocity;
private float velocityRotation;
private float verticalVelocity;
private Vector2 moveInput;
private Vector2 lookInput;
private ActionsReconstruction actionsReconstruction;
void Awake()
{
controller = GetComponent<CharacterController>();
actionsReconstruction = new ActionsReconstruction();
// 注册输入回调
actionsReconstruction.Player.Move.performed += OnMove;
actionsReconstruction.Player.Move.canceled += OnMove;
actionsReconstruction.Player.Look.performed += OnLook;
actionsReconstruction.Player.Look.canceled += OnLook;
actionsReconstruction.Player.Jump.performed += OnJump;
}
void OnEnable()
{
actionsReconstruction.Enable();
}
void OnDisable()
{
actionsReconstruction.Disable();
}
void OnDestroy()
{
// 取消注册输入回调
actionsReconstruction.Player.Move.performed -= OnMove;
actionsReconstruction.Player.Look.performed -= OnLook;
actionsReconstruction.Player.Jump.performed -= OnJump;
}
void Update()
{
HandleMove();
HandleLook();
HandleJump();
}
// 移动输入回调
private void OnMove(InputAction.CallbackContext context)
{
moveInput = context.ReadValue<Vector2>();
}
// 视角输入回调
private void OnLook(InputAction.CallbackContext context)
{
lookInput = context.ReadValue<Vector2>();
}
// 跳跃输入回调
private void OnJump( InputAction.CallbackContext context)
{
if (controller.isGrounded)
{
verticalVelocity = jumpForce;
}
}
private void OnMoveCanceled(InputAction.CallbackContext context)
{
moveInput = Vector2.zero;
}
private void OnLookCanceled(InputAction.CallbackContext context)
{
lookInput = Vector2.zero;
}
private void HandleMove()
{
moveDirection = transform.right * moveInput.x + transform.forward * moveInput.y;
moveVelocity = moveDirection * moveSpeed;
Vector3 finalVelocity = moveVelocity + Vector3.up * verticalVelocity;
controller.Move(finalVelocity * Time.deltaTime);
}
private void HandleLook()
{
transform.Rotate(Vector3.up * lookInput.x * sensitivity * Time.deltaTime);
velocityRotation -= lookInput.y * sensitivity * Time.deltaTime;
velocityRotation = Mathf.Clamp(velocityRotation, -90f, 90f);
cameraTransform.localRotation = Quaternion.Euler(velocityRotation, 0f, 0f);
}
private void HandleJump()
{
if (controller.isGrounded)
{
verticalVelocity = -0.5f;
}
else
{
verticalVelocity += gravity * Time.deltaTime;
}
}
}
上述方法用回调的方法完成了新输入系统的操作,避免了轮训式调用,只在用户输入的时候回调方法,但是我们会发现,如果用户输入的频率很高,比如玩家一直按着移动键,那么就会一直回调方法,这样就会导致性能的下降,随着输入频率的增加可能会出现丢包的现象,所以在特定的情况我们还需要轮训的方法调用,让我们思考一下什么时候需要轮训的方法调用,什么时候需要事件调用的方法调用。
从以上的实例讲,类似于玩家移动视角移动,我们可能会一直按着移动键,那么我们就可以使用轮训的方法调用,如果玩家只是偶尔移动一下,那么我们就可以使用事件调用的方法调用,比如跳跃的逻辑我们就可以使用事件调用的方法调用,因为跳跃的逻辑只需要在玩家按下跳跃键的时候调用一次就可以了。
private void OnJump(InputValue inputValue)
{
if (controller.isGrounded)
{
verticalVelocity = jumpForce;
}
}