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

Unity 3D角色在地面Y轴持续抖动的问题求助

Unity 3D角色在地面Y轴持续抖动的问题求助

我在Unity 3D项目里用了MVC架构,写了个CollisionPoller来检测碰撞和处理移动逻辑。但现在遇到个头疼的问题:当同时使用MoveController和CollisionPoller时,角色的Y轴每帧都会有±0.01的抖动。

我试过把刚体设为Is Kinematic,这时候抖动消失了,但角色会直接穿过斜坡;也试过缩小胶囊碰撞体的高度,可一到斜坡上抖动又回来了。

下面是我的代码:

CollisionPoller 代码

public class CollisionPoller
{
    private readonly PlayerView _playerView;
    private const int MaxBounces = 5;
    private const float Threshold = 0.015f;
    private const float MaxSlopeAngle = 45f;
    private Bounds _bounds;

    public CollisionPoller(PlayerView playerView)
    {
        _playerView = playerView;
        _bounds = _playerView.Collider.bounds;
        _bounds.Expand(-2 * Threshold);
    }

    public Vector3 CollideAndSlide(Vector3 velocity, Vector3 position, int depth, bool gravityPass, Vector3 velocityInit)
    {
        if(depth >= MaxBounces)
            return Vector3.zero;
        
        var distance = velocity.magnitude + Threshold;
        if (!Physics.SphereCast(position, _bounds.extents.x, velocity.normalized, out var hit, distance))
            return velocity;
        var snapToSurface = velocity.normalized * (hit.distance - Threshold);
        var leftOver = velocity - snapToSurface;
        var angle = Vector3.Angle(Vector3.up, hit.normal);
            
        if(snapToSurface.magnitude <= Threshold)    
            snapToSurface = Vector3.zero;

        if (angle <= MaxSlopeAngle)     
        {
            if(gravityPass)
                return snapToSurface;
            leftOver = ProjectAndScale(leftOver, hit.normal);
        }
        else
        {
            var scale = 1 - Vector3.Dot(new Vector3(hit.normal.x, 0, hit.normal.z).normalized,
                -new Vector3(velocityInit.x, 0, velocityInit.z).normalized);
            var isGrounded = Physics.Raycast(_playerView.Transform.position, Vector3.down, _bounds.extents.y + 0.1f); 
            if (isGrounded && !gravityPass)
            {
                leftOver = ProjectAndScale(new Vector3(leftOver.x, 0, leftOver.z), 
                    new Vector3(hit.normal.x, 0, hit.normal.z)).normalized;
                leftOver *= scale;
            }
            else
            {
                leftOver = ProjectAndScale(leftOver, hit.normal) * scale;
            }
        }       
        return snapToSurface + CollideAndSlide(leftOver, position + snapToSurface, depth + 1, gravityPass, velocityInit);
    }

    private static Vector3 ProjectAndScale(Vector3 vector, Vector3 normal)
    {
        var magnitude = vector.magnitude;
        vector = Vector3.ProjectOnPlane(vector, normal).normalized;
        vector *= magnitude;
        return vector;
    }
}

MoveController 代码

public class MoveController : BaseController
{
    private readonly PlayerView _playerView;
    private readonly GameModel _gameModel;
    private readonly CollisionPoller _collisionPoller;
            
    public MoveController(PlayerView playerView, GameModel gameModel, CollisionPoller collisionPoller)
    {
        _playerView = playerView;
        _gameModel = gameModel;                         
        _collisionPoller = collisionPoller;
    }

    public void FixedUpdate()
    {
        var horizontalInput = Input.GetAxisRaw("Horizontal");
        var verticalInput = Input.GetAxisRaw("Vertical");
        
        var direction = new Vector3(horizontalInput, 0, verticalInput).normalized;
        var moveDirection = _collisionPoller.CollideAndSlide(direction, _playerView.Transform.position, 0, false, direction);
        moveDirection += _collisionPoller.CollideAndSlide(Vector3.down, _playerView.Transform.position + moveDirection, 0, true, Vector3.down);
        _playerView.Rigidbody.MovePosition(_playerView.Rigidbody.position + moveDirection * (_gameModel.CurrentPlayer.WalkSpeed * Time.fixedDeltaTime));    
    }
}

备注:内容来源于stack exchange,提问作者lolka

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.15 09:34:52