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

Unity FPS控制器重力失效求助:ApplyGravity函数不生效

FPS控制器重力失效问题排查与修复

我正在制作一款FPS控制器,无法为角色施加重力。已编写ApplyGravity()函数,调试发现角色的y方向数值在减小,但角色本身并未移动。移动和相机旋转功能均正常,需要解决重力失效问题。

问题代码

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

public class FirstPersonController : MonoBehaviour
{
    [Header("Dependencies")]
    [SerializeField] private GameInput gameInput;

    [Header("Movement Parameters")]
    [SerializeField] private float moveSpeed;
    [SerializeField] private float sprintSpeed;

    [Header("Look Parameters")]
    [SerializeField] private Transform cameraFollowTransform;
    [SerializeField] private float lookSensitivity;
    [SerializeField] private float topClamp = 90.0f;
    [SerializeField] private float bottomClamp = -90.0f;

    [Header("Jumping Parameters")]
    [SerializeField] private float jumpForce;

    [Header("Gravity Parameters")]
    [SerializeField] private float gravityMultiplier = 3f;


    // player
    private CharacterController characterController;
    private float rotationVelocity;
    private bool isSprinting;
    
    // input
    private Vector2 inputVector;
    private Vector3 moveDirection;

    // gravity 
    private float gravityForce = -9.81f;
    private float velocity;

    // cinemachine
    private float cinemachineTargetPitch;

    private void Awake()
    {
        characterController = GetComponent<CharacterController>();

        Cursor.lockState = CursorLockMode.Locked;
        Cursor.visible = false;
    }

    private void Update()
    {
        ApplyGravity();
        HandleMovement();    
    }

    private void LateUpdate()
    {
        HandleCameraRotation();
    }

    private void HandleMovement()
    {
        inputVector = gameInput.GetMovementVectorNormalized();
        moveDirection = new Vector3(inputVector.x, 0f, inputVector.y);

        moveDirection = transform.right * inputVector.x + transform.forward * inputVector.y;

        float targetSpeed = CheckSprint() ? sprintSpeed : moveSpeed;
        float moveDistance = targetSpeed * Time.deltaTime;

        characterController.Move(moveDirection * moveDistance);
    }

    private void HandleCameraRotation()
    {
        Vector2 inputVector = gameInput.GetLookVector();
        
        cinemachineTargetPitch += inputVector.y * lookSensitivity * Time.deltaTime;
        rotationVelocity = inputVector.x * lookSensitivity * Time.deltaTime;

        cinemachineTargetPitch = ClampAngle(cinemachineTargetPitch, bottomClamp, topClamp);

        cameraFollowTransform.transform.localRotation = Quaternion.Euler(cinemachineTargetPitch, 0f, 0f);

        transform.Rotate(Vector3.up * rotationVelocity);
    }

    private void ApplyGravity()
    {
        velocity += gravityForce * gravityMultiplier * Time.deltaTime;
        moveDirection.y = velocity;

        Debug.Log(moveDirection);
    }

    private bool CheckSprint()
    {
        if (gameInput.GetPLayerInputActions().Player.Sprint.inProgress)
        {
            isSprinting = true;
        }
        else
        {
            isSprinting = false;
        }

        return isSprinting;
    }

    private static float ClampAngle(float lfAngle, float lfMin, float lfMax)
    {
        if (lfAngle < -360f) lfAngle += 360f;
        if (lfAngle > 360f) lfAngle -= 360f;
        return Mathf.Clamp(lfAngle, lfMin, lfMax);
    }
}

问题原因

  1. HandleMovement()重置了y轴分量:每次调用该方法时,都会执行moveDirection = new Vector3(inputVector.x, 0f, inputVector.y),将y轴设为0,覆盖了ApplyGravity()中设置的y轴重力值。
  2. 未将重力分量传入CharacterController.Move():当前代码仅将水平方向的移动量传给了Move()方法,没有包含重力的y轴位移。

修复方案

1. 修改HandleMovement(),保留重力y轴分量

将重置moveDirection的代码改为仅更新水平方向,保留y轴的重力值:

private void HandleMovement()
{
    inputVector = gameInput.GetMovementVectorNormalized();
    // 只更新水平方向,保留y轴的重力值
    moveDirection.x = inputVector.x;
    moveDirection.z = inputVector.y;

    moveDirection = transform.right * inputVector.x + transform.forward * inputVector.y;

    float targetSpeed = CheckSprint() ? sprintSpeed : moveSpeed;
    // 分离水平移动和重力移动
    Vector3 horizontalMove = moveDirection.normalized * targetSpeed * Time.deltaTime;
    Vector3 gravityMove = new Vector3(0f, velocity, 0f) * Time.deltaTime;
    // 合并后应用移动
    characterController.Move(horizontalMove + gravityMove);
}

2. 完善ApplyGravity(),添加地面检测

当角色接触地面时重置重力速度,避免无限累加导致速度过大,同时防止角色悬空:

private void ApplyGravity()
{
    // 角色在地面时,重置速度为小负数,确保贴合地面
    if (characterController.isGrounded && velocity < 0)
    {
        velocity = -1f;
    }
    else
    {
        velocity += gravityForce * gravityMultiplier * Time.deltaTime;
    }
    // 无需再给moveDirection.y赋值,直接在Move中使用velocity
}

修复后补充说明

  • 地面检测使用CharacterController.isGrounded,确保角色站在地面时不会持续受到重力加速度影响。
  • 分离水平移动和重力移动,避免移动速度影响重力的加速度效果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 09:06:02