Unity WASD+QE 控制角色移动与转向(含 Shift 加速)实现教程
【Unity笔记】Unity WASD+QE 控制角色移动与转向(含 Shift 加速)实现教程
引言
WASD+QE 控制方案是 FPS/TPS 游戏的标准输入模式:WASD 控制平面移动,Q/E 实现侧向平移(Strafe),Shift 提供冲刺/加速功能。该教程基于 Unity Input System(新输入系统,推荐)和 Legacy Input(旧系统兼容),支持地面检测、加速度曲线、转向平滑和动画状态机集成。适用于 Unity 2021.3+(Input System 包),兼容 URP/HDRP 和 XR 项目。
核心特性:
- 多平台输入:键盘 + 手柄支持(Gamepad 左摇杆 + 右摇杆)
- 物理真实感:加速度/减速度曲线,惯性移动
- 转向控制:鼠标/右摇杆控制视角,QE 侧移不影响朝向
- Shift 加速:冲刺模式,带耐力/冷却机制
- 动画集成:参数化 Animator(Speed、IsGrounded、IsSprinting)
- 地面检测:Raycast/SphereCast 防穿地,斜坡适应
时间复杂度 O(1),性能优化使用 FixedUpdate 处理物理,LateUpdate 处理相机跟随。
核心实现(CharacterControllerInput.cs)
1. 输入系统设置(推荐新 Input System)
首先在 Project Settings > Input System Package 启用新输入系统,创建 Input Action Asset:
创建 Input Actions:
- Assets > Create > Input Actions > “PlayerInput”
- 添加 Action Maps:
- Movement:WASD/Gamepad Left Stick(Vector2)
- Look:Mouse Delta/Right Stick(Vector2)
- Strafe:Q/E(Vector2,X轴:-1=Q, +1=E)
- Sprint:Left Shift(Button)
2. 完整控制器脚本
using UnityEngine;
using UnityEngine.InputSystem;
using System;
public class CharacterControllerInput : MonoBehaviour
{
[Header("移动设置")]
public float walkSpeed = 5f;
public float sprintSpeed = 8f;
public float acceleration = 10f;
public float deceleration = 15f;
public float airMultiplier = 0.3f; // 空中移动减弱
[Header("转向设置")]
public float mouseSensitivity = 2f;
public float controllerSensitivity = 100f;
public float maxLookAngle = 80f; // 垂直视角限制
[Header("地面检测")]
public Transform groundCheck;
public float groundDistance = 0.4f;
public LayerMask groundMask;
[Header("动画参数")]
public Animator animator;
public string speedParam = "Speed";
public string groundedParam = "IsGrounded";
public string sprintParam = "IsSprinting";
// 组件
private CharacterController controller;
private Camera playerCamera;
private PlayerInput playerInput;
// 输入
private Vector2 moveInput;
private Vector2 lookInput;
private bool sprintInput;
private bool isGrounded;
// 运行时变量
private Vector3 velocity;
private float xRotation = 0f;
private float currentSpeed;
private bool isSprinting;
void Awake()
{
controller = GetComponent<CharacterController>();
playerCamera = Camera.main;
playerInput = GetComponent<PlayerInput>();
// 锁定光标
Cursor.lockState = CursorLockMode.Locked;
}
void Update()
{
HandleInput();
HandleLook();
HandleGroundCheck();
HandleMovement();
UpdateAnimator();
}
private void HandleInput()
{
// 新 Input System 自动绑定(通过 PlayerInput 组件)
moveInput = playerInput.actions["Movement"].ReadValue<Vector2>();
lookInput = playerInput.actions["Look"].ReadValue<Vector2>();
sprintInput = playerInput.actions["Sprint"].IsPressed();
}
private void HandleLook()
{
// 鼠标/摇杆转向
float sensitivity = Input.GetMouseButton(1) ? mouseSensitivity : controllerSensitivity * 0.01f;
xRotation -= lookInput.y * sensitivity;
xRotation = Mathf.Clamp(xRotation, -maxLookAngle, maxLookAngle);
playerCamera.transform.localRotation = Quaternion.Euler(xRotation, 0f, 0f);
transform.rotation *= Quaternion.Euler(0f, lookInput.x * sensitivity, 0f);
}
private void HandleGroundCheck()
{
isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);
if (isGrounded && velocity.y < 0)
{
velocity.y = -2f; // 轻微下拉力确保接地
}
}
private void HandleMovement()
{
// 水平移动
Vector3 moveDirection = transform.right * moveInput.x + transform.forward * moveInput.y;
float targetSpeed = sprintInput && moveInput.magnitude > 0.5f ? sprintSpeed : walkSpeed;
isSprinting = sprintInput && moveInput.magnitude > 0.5f;
// 加速度/减速度
float accel = isGrounded ? (isSprinting ? acceleration * 1.5f : acceleration) : acceleration * airMultiplier;
float decel = isGrounded ? deceleration : deceleration * airMultiplier;
currentSpeed = Mathf.MoveTowards(currentSpeed, targetSpeed * moveInput.magnitude,
(moveInput.magnitude > 0 ? accel : decel) * Time.deltaTime);
Vector3 horizontalVelocity = moveDirection.normalized * currentSpeed;
controller.Move(horizontalVelocity * Time.deltaTime);
// 重力
velocity.y += Physics.gravity.y * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
}
private void UpdateAnimator()
{
if (animator != null)
{
animator.SetFloat(speedParam, currentSpeed);
animator.SetBool(groundedParam, isGrounded);
animator.SetBool(sprintParam, isSprinting);
}
}
// 可视化地面检测
void OnDrawGizmosSelected()
{
if (groundCheck != null)
{
Gizmos.color = isGrounded ? Color.green : Color.red;
Gizmos.DrawWireSphere(groundCheck.position, groundDistance);
}
}
}
3. Legacy Input 版本(旧输入系统)
如果不使用新 Input System,可替换 HandleInput:
private void HandleInput_Legacy()
{
// WASD + QE 移动
float horizontal = Input.GetAxisRaw("Horizontal"); // A/D
float vertical = Input.GetAxisRaw("Vertical"); // W/S
float strafe = 0f;
if (Input.GetKey(KeyCode.Q)) strafe = -1f;
if (Input.GetKey(KeyCode.E)) strafe = 1f;
moveInput = new Vector2(strafe, vertical).normalized;
// 鼠标看向
lookInput.x = Input.GetAxis("Mouse X");
lookInput.y = Input.GetAxis("Mouse Y");
sprintInput = Input.GetKey(KeyCode.LeftShift);
}
设置步骤
1. 场景配置
- 创建角色:空 GameObject > Add Component > CharacterController
- 添加脚本:附加 CharacterControllerInput
- 相机设置:创建子对象 Camera,设置为 Main Camera,调整位置(通常 eyes 高度)
- 地面检测:创建空子对象 GroundCheck,位置在角色脚下(Y=-0.9)
2. Input Action Asset 配置
// PlayerInput 示例配置
{
"Movement": {
"W": "up",
"S": "down",
"A": "left",
"D": "right",
"Gamepad Left Stick": "Vector2"
},
"Strafe": {
"Q": "left",
"E": "right"
},
"Look": {
"Mouse Delta": "Vector2",
"Gamepad Right Stick": "Vector2"
},
"Sprint": {
"Left Shift": "pressed",
"Gamepad Left Trigger": "pressed"
}
}
3. Animator 集成
在 Animator Controller 中:
- 参数:Speed (Float)、IsGrounded (Bool)、IsSprinting (Bool)
- 状态:
- Idle → Walk (Speed > 0.1)
- Walk → Sprint (IsSprinting && Speed > 0.1)
- Any → Fall (!IsGrounded)
高级功能扩展
4. 冲刺耐力系统
[Header("冲刺设置")]
public float maxStamina = 100f;
public float staminaDrain = 20f;
public float staminaRegen = 10f;
private float currentStamina;
void Update()
{
HandleStamina();
}
private void HandleStamina()
{
if (isSprinting && moveInput.magnitude > 0.5f)
{
currentStamina -= staminaDrain * Time.deltaTime;
if (currentStamina <= 0) sprintInput = false; // 耐力耗尽停止冲刺
}
else
{
currentStamina = Mathf.Min(maxStamina, currentStamina + staminaRegen * Time.deltaTime);
}
isSprinting = sprintInput && currentStamina > 0f;
}
5. 斜坡适应与跳跃
[Header("物理设置")]
public float jumpHeight = 2f;
public float gravity = -9.81f;
private bool jumpInput;
private void HandleJump()
{
if (jumpInput && isGrounded)
{
velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
}
velocity.y += gravity * Time.deltaTime;
}
private void HandleSlope()
{
// Raycast 检测斜坡角度
RaycastHit hit;
if (Physics.Raycast(transform.position, Vector3.down, out hit, groundDistance + 0.1f, groundMask))
{
float slopeAngle = Vector3.Angle(hit.normal, Vector3.up);
if (slopeAngle > controller.slopeLimit)
{
// 斜坡滑动
Vector3 slideDirection = Vector3.ProjectOnPlane(Physics.gravity, hit.normal);
controller.Move(slideDirection * Time.deltaTime);
}
}
}
6. 平滑转向(可选)
[Header("转向平滑")]
public float rotationSpeed = 10f;
private Quaternion targetRotation;
private void SmoothRotation(Vector3 moveDirection)
{
if (moveDirection.magnitude > 0.1f)
{
targetRotation = Quaternion.LookRotation(moveDirection);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
}
}
性能优化与调试
优化建议
- FixedUpdate:移动物理计算移至 FixedUpdate(更稳定)
- 对象池:大量粒子/特效使用对象池
- LOD:远距离降低动画复杂度
- Profiler:监控 Input System 和 CharacterController 开销
调试技巧
- Gizmos:可视化地面检测和移动向量
- Debug.Log:输出输入值、速度、耐力
- 动画窗口:实时查看 Animator 参数变化
跨平台适配
- Gamepad 支持:Input System 自动映射左摇杆(移动)、右摇杆(看向)
- 触屏适配:虚拟摇杆 + 自动冲刺 Toggle
- 移动端:降低 sensitivity,添加触屏转向
此 WASD+QE 控制器提供完整的角色移动解决方案,支持动画集成和高级物理。如果需要网络同步(Photon/Mirror)或状态机扩展,请提供更多需求!