You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中基于CharacterController的玩家如何添加空中动量

问题根源

现有代码出现空中滑翔效果由三个问题直接导致:

  1. 声明了airSpeed参数但从未调用,地面与空中共用同一套水平速度计算逻辑,空中可获得与地面完全一致的移动加速度
  2. 未保存起跳瞬间的水平动量,每帧直接重新计算水平移动量传入controller.Move,完全没有保留起跳惯性
  3. 空中stepOffset参数设置错误,代码中给空中设置了1f的步高,比地面0.5f的步高还大,会触发CharacterController自带的斜面/墙面贴滑逻辑,加重滑翔感
修改方案
  • 新增私有变量存储起跳后保留的水平动量,新增airControlStrength参数控制空中调整方向的力度
  • 地面状态下正常计算水平移动速度,每帧同步更新水平动量值
  • 起跳后不再直接覆盖水平速度,在原有起跳动量的基础上叠加小幅的空中控制偏移,同时限制空中水平速度上限不超过airSpeed
  • 修正空中stepOffset值为0,避免贴墙/贴斜面自动滑动
  • 落地后自动切回地面移动逻辑,重置垂直速度

修改后可直接使用的完整代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Move : MonoBehaviour
{
    public CharacterController controller;

    // 移动倍率参数
    private float speedMult = 1f;
    private float speed;
    public float baseSpeed = 20f;
    public float gravity = -9.81f;
    public float jumpHeight = 3f;

    public float walkMult = 1f;
    public float sprintMult = 1.5f;
    public float crouchMult = 0.5f;
    private int jumpAmount = 2;
  
    public float airSpeed = 6f;
    // 空中控制力度,可在Inspector面板调整
    public float airControlStrength = 2f;

    public float crouchYMult = 0.5f;
    private float startYMult = 1f;

    public MovementState state;

    public enum MovementState
    {
        walking,
        sprinting,
        crouching,
        air
    }

    // 地面检测参数
    public Transform groundCheck;
    public float groundDistance = 0.5f;
    public LayerMask groundMask;

    Vector3 velocity;
    // 存储水平方向动量
    private Vector3 horizontalMomentum;
    bool isGrounded;

    private void StateHandler() 
    {
        if(isGrounded && Input.GetButton("Sprint"))
        {
            state = MovementState.sprinting;
            speedMult = sprintMult;
        }
        else if(isGrounded && Input.GetButton("Crouch"))
        {
            state = MovementState.crouching;
            speedMult = crouchMult;
        }
        else if(isGrounded)
        {
            state = MovementState.walking;
            speedMult = walkMult;
        }
        else
        {
            state = MovementState.air;
        }
    }
    private void Start() 
    {
        startYMult = transform.localScale.y;    
    }

    void Update()
    {
        StateHandler();

        // 地面检测
        isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);
        if (isGrounded)
        {
            jumpAmount = 2;
            if (velocity.y < 0)
            {
                velocity.y = -5f;
            }
        }

        // 获取输入方向
        float x = Input.GetAxis("Horizontal");
        float z = Input.GetAxis("Vertical");
        Vector3 moveDir = transform.right * x + transform.forward * z;
        if (moveDir.magnitude > 1)
        {
            moveDir = moveDir.normalized;
        }

        if (isGrounded)
        {
            // 地面移动逻辑
            speed = speedMult * baseSpeed;
            horizontalMomentum = moveDir * speed;
            controller.stepOffset = 0.5f;
        }
        else
        {
            // 空中移动逻辑:保留动量+小幅控制
            Vector3 airMoveOffset = moveDir * airControlStrength;
            horizontalMomentum += airMoveOffset * Time.deltaTime;
            // 限制空中最高速度
            if (horizontalMomentum.magnitude > airSpeed)
            {
                horizontalMomentum = horizontalMomentum.normalized * airSpeed;
            }
            controller.stepOffset = 0f;
        }

        // 应用水平移动
        controller.Move(horizontalMomentum * Time.deltaTime);

        // 跳跃逻辑
        if (Input.GetButtonDown("Jump") && isGrounded)
        {
            velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
        }
        else if (Input.GetButtonDown("Jump") && jumpAmount > 0)
        {
            velocity.y = Mathf.Sqrt(jumpHeight * 1.2f * -2f * gravity);
            jumpAmount -= 1;
        }

        // 应用重力
        velocity.y += gravity * Time.deltaTime;
        controller.Move(velocity * Time.deltaTime);

        // 蹲伏缩放
        if(Input.GetButton("Crouch"))
        {
            transform.localScale = new Vector3(transform.localScale.x, crouchYMult, transform.localScale.z);
        }
        else
        {
            transform.localScale = new Vector3(transform.localScale.x, startYMult, transform.localScale.z);
        }
    }
}

参数调整说明

  • 直接在Inspector面板调小airSpeed可以降低空中最高移动速度,调小airControlStrength可以让空中转向、变速更迟钝,起跳动量保留效果更明显
  • 如果需要更真实的空气阻力效果,可以在空中逻辑块中添加一行horizontalMomentum *= 0.998f;,数值越小空中减速越快

内容的提问来源于stack exchange,提问作者T14D3

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 14:06:15