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

  1. Assets > Create > Input Actions > “PlayerInput”
  2. 添加 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. 场景配置

  1. 创建角色:空 GameObject > Add Component > CharacterController
  2. 添加脚本:附加 CharacterControllerInput
  3. 相机设置:创建子对象 Camera,设置为 Main Camera,调整位置(通常 eyes 高度)
  4. 地面检测:创建空子对象 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)或状态机扩展,请提供更多需求!

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注