Unity Character Controller斜面移动异常:向法线反方向移动却偏移
问题描述
使用Character Controller控制玩家沿Vector3(0, -1, 1)方向移动,该方向与45度斜面的法线方向相反,理论上角色应保持静止,但实际却沿Vector3(0, 1, 1)方向移动。尝试修改Character Controller的各类参数后问题仍未解决,询问原因是否与碰撞有关。
代码示例
public class NewBehaviourScript : MonoBehaviour { private CharacterController _controller; void Start() { _controller = GetComponent<CharacterController>(); } void FixedUpdate() { Vector3 dir = new Vector3(0, -1, 1); _controller.Move(dir * Time.fixedDeltaTime); Debug.Log($"Dir: {dir}, Vel: {_controller.velocity}"); Vector3 origin = transform.position + transform.right; DebugExt.DrawColored(Color.red).Vector(origin , dir * 2); } }
场景截图



问题原因与解决方案
核心原因:Character Controller的碰撞响应机制
这个问题完全和Character Controller的碰撞处理逻辑有关,具体两点:
- 穿透修正的向量投影:当你调用
Move传入的位移向量会导致角色嵌入斜面时,控制器会自动修正位移——抵消掉向量中垂直于斜面法线的分量(避免穿透),只保留平行于斜面的切向分量。你传入的(0,-1,1)方向包含指向斜面内部的垂直分量,被抵消后剩余的切向分量正好是(0,1,1),这就是角色实际移动的方向。 - 接地状态的微小滑动修正:当角色与斜面接触时,控制器会判定角色“接地”,即使你调整了Slope Limit等参数,微小的位移精度误差也会触发内置的斜坡滑动逻辑,导致角色产生非预期的移动。
解决方案
- 静止在斜面上的正确方式:如果只是想让角色静止在斜面上,不需要手动传入朝向斜面的向量,只需在
Move中加入重力向量,控制器会自动处理斜面的静止状态(只要斜面角度不超过Slope Limit):void FixedUpdate() { float gravity = 9.8f; Vector3 moveDir = new Vector3(0, -gravity * Time.fixedDeltaTime, 0); _controller.Move(moveDir); } - 手动控制时的向量投影:如果必须手动控制朝向斜面的力,先将移动向量投影到斜面的切平面上,避免触发穿透修正:
void FixedUpdate() { Vector3 dir = new Vector3(0, -1, 1); // 假设通过碰撞检测获取斜面法线(可通过_controller.collisionFlags结合Raycast实现) Vector3 slopeNormal = Vector3.up; // 替换为实际法线 Vector3 moveDir = Vector3.ProjectOnPlane(dir, slopeNormal); _controller.Move(moveDir * Time.fixedDeltaTime); } - 验证法线方向:通过Gizmos绘制斜面的实际法线,确认你理解的法线方向与碰撞体的真实法线一致,避免方向判断错误。
内容的提问来源于stack exchange,提问作者ton3m
相关产品推荐
相关产品推荐

