Unity 6 中角色可以移动,但按下 Jump 无法跳跃,如何排查?
结论
这段脚本已经通过 CharacterController.Move 处理移动,并自行计算重力。跳跃可以沿用这套逻辑:按下 Jump 且角色接地时,设置 velocity.y,再由 controller.Move 执行位移。先删除开头的 Rigidbody.AddForce。
仅凭脚本无法确定场景中究竟哪里出了问题。先检查按下跳跃键时是否有空引用异常,以及 Physics.CheckSphere 是否返回 true。当 gravity < 0、jumpHeight > 0 时,原有跳跃公式可以保留。
为什么开头的 AddForce 可能导致跳跃失败?
需要检查这一行:
GetComponent<Rigidbody>().AddForce(Vector3.up * 5, ForceMode.VelocityChange);
GetComponent<Rigidbody>() 查找的是与当前脚本挂在同一个 GameObject 上的组件:
- 如果该对象没有
Rigidbody,按下跳跃键时会发生NullReferenceException,当前这次Update()会中断,后面的接地检测和跳跃代码都无法执行。这可以解释为什么平时移动正常,按下跳跃键却没有反应。 - 如果该对象有
Rigidbody,脚本就同时使用物理受力和CharacterController.Move控制角色,需要先统一移动方案。对于当前脚本,保留CharacterController方案即可。
同一帧中两次调用 Input.GetButtonDown("Jump") 不会“消耗”按键事件。重复检测可以合并,但不能认为第二次检测因此收不到输入。
使用 CharacterController 完成跳跃
下面保留原有的移动速度、重力、跳跃高度和球形接地检测,删除 AddForce,并把水平和垂直位移合并到一次 Move 调用中:
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public CharacterController controller;
public float speed = 12f;
public float gravity = -9.81f * 2;
public float jumpHeight = 3f;
public Transform groundCheck;
public float groundDistance = 0.4f;
public LayerMask groundMask;
private Vector3 velocity;
private bool isGrounded;
void Update()
{
isGrounded = Physics.CheckSphere(
groundCheck.position,
groundDistance,
groundMask
);
if (isGrounded && velocity.y < 0f)
{
velocity.y = -2f;
}
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
Vector3 move = transform.right * x + transform.forward * z;
if (Input.GetButtonDown("Jump") && isGrounded)
{
velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
}
velocity.y += gravity * Time.deltaTime;
Vector3 motion = move * speed + Vector3.up * velocity.y;
controller.Move(motion * Time.deltaTime);
}
}
CharacterController.Move 接收位移量,不会自动为这段脚本计算重力,因此仍需保留 velocity.y += gravity * Time.deltaTime。
如果角色上的 Rigidbody 只是为原来的 AddForce 添加的,可以移除;如果其他脚本依赖它,也需要检查这些脚本,避免它们继续改变角色位置。
修改后仍不能跳,按什么顺序排查?
1. 先检查 Console 和组件引用
运行游戏并按下跳跃键,查看 Console 是否报错。确认 Inspector 中:
controller已指向角色的CharacterController。groundCheck已指向用于检测脚底位置的 Transform。- 场景中没有其他脚本持续覆盖角色的位置或垂直速度。
如果仍有异常,先修复异常,再检查输入和接地逻辑。
2. 确认 Jump 输入是否触发
临时在 Update() 开头加入:
if (Input.GetButtonDown("Jump"))
{
Debug.Log("Jump pressed");
}
如果按键时没有日志,先让 Game 视图获得焦点,再检查旧版 Input Manager 中是否存在名称完全一致的 Jump,以及它绑定的按键。
当前代码使用的是 UnityEngine.Input。将 Active Input Handling 设置为 Both 后可以使用这套输入 API,但仍需在 Input Manager 中正确配置 Jump。新 Input System 中同名的 Input Action 不会自动成为 Input.GetButtonDown("Jump") 的绑定。
3. 确认 isGrounded 是否为 true
如果输入日志正常,把下面的诊断代码放在 Physics.CheckSphere 赋值之后:
if (Input.GetButtonDown("Jump"))
{
Debug.Log($"Jump pressed, isGrounded = {isGrounded}");
}
如果角色站在地面上却打印 false,检查以下配置:
- 地面是否有启用的 Collider。
groundMask是否包含地面所在的 Layer;如果是Nothing,就检测不到地面。groundCheck是否位于脚底附近。- 检测半径是否足以让球体与地面 Collider 相交。
groundMask是否误包含角色自身的 Layer,导致接地结果失真。
可以在脚本中加入辅助绘制,选中角色后,在 Scene 视图观察检测球的位置:
void OnDrawGizmosSelected()
{
if (groundCheck == null)
{
return;
}
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(groundCheck.position, groundDistance);
}
groundDistance = 0.4f 是否合适取决于角色尺寸和 groundCheck 的位置,不能仅凭数值判断。
4. 输入和接地都正常时,检查垂直运动
确认运行时 Inspector 中的 gravity 为负数、jumpHeight 为正数。已有组件可能保存了 Inspector 数值,修改脚本中的初始值不一定会更新这些数值。
如果跳跃分支确实执行、velocity.y 也变成正数,但角色仍不上升,检查头顶是否有碰撞体阻挡,以及其他脚本或动画是否在同一帧修改角色位置。
备注:内容仅供参考。
