Rigidbody2D.Slide() leaves a gap when moving into walls

I’ve been experimenting with Rigidbody2D.Slide() for slope physics, and I ran into an issue. As you can see in the gif, when the rigidbody moves toward a wall that’s too steep to slide onto, a gap is left. My guess is that Slide() checks ahead a certain distance and then cancels the entire movement if it detects an obstruction, rather than moving the rigidbody as far as possible until the obstacle is actually hit.

WallGap

I looked into the PhysicsExamples2D project and noticed that the main difference there was that Slide() is called in Update() instead of FixedUpdate(). This appears to work, but from what I can tell it minimizes the problem more so than solving it. Update() just runs more frequently, which makes the gap so small it’s barely noticeable. On lower-end PCs with lower frame rates, though, the gaps could still be visible. Besides, moving physics logic into Update() doesn’t seem like the right approach.

Has anyone found a cleaner solution to this? Or am I simply missing something? Any help is appreciated!

  • Slide has no knowledge of when you are calling it.
  • It only reacts to the arguments you provide.
  • It uses standard physics queries so separation is based upon contact offset for polygons.
  • It doesn’t “check ahead”, it simply use the velocity you provide, queries in that direction, moves to just outside the point of contact, clips the velocity and repeats up to however many iterations you allow it to.
  • Slide has the option of asking how you want to move i.e. set position, use simulation move or just return the results.

I don’t know what solution you mean. Running it more frequently has no effect, I presume you mean smaller steps.

In the end, you’ll need to provide some code/configuration example of what you’re doing here so I can get a better sense of what is doing what.

Better still, a small reproduction example to determine if this is configuration or potentially a bug.

Thanks for the quick reply! If you would like to take a look at the project yourself, I’ve uploaded it to Drive:
https://drive.google.com/file/d/1NOCflNnn-ZH9xHOH9ZHvnJrP3Z_dMAWI/view?usp=sharing

The relevant code is all in “PlayerDraftSimple.cs”:

public class PlayerDraftSimple : MonoBehaviour
{
    // Exposed
    [SerializeField] private float _moveSpeed = 5f;
    [SerializeField] private float _gravity = 1f;
    [SerializeField] private float _maxGroundAngle = 30f;
    [SerializeField] private int _maxSlideIterations = 5;
    [SerializeField] private float _anchorDistance = 0.1f;

    // Internal
    private PlayerInput _playerInput;
    private Rigidbody2D _rb;
    private Rigidbody2D.SlideMovement _slideMovement;
    private float _xAxis;
    private ContactFilter2D _contactFilter;
    private Vector2 _velocity = Vector2.zero;

    private bool _isGrounded => _rb.IsTouching(_contactFilter) && _velocity.y <= 0;

    // Methods
    void Awake()
    {
        _playerInput = GetComponent<PlayerInput>();
        _rb = GetComponent<Rigidbody2D>();

        _slideMovement = new Rigidbody2D.SlideMovement
        {
            gravity = Vector2.zero,
            surfaceSlideAngle = _maxGroundAngle,
            maxIterations = _maxSlideIterations
        };

        _contactFilter = new ContactFilter2D()
        {
            useNormalAngle = true,
            minNormalAngle = 90 - _maxGroundAngle,
            maxNormalAngle = 90 + _maxGroundAngle
        };
    }

    void Update() => _xAxis = _playerInput.Move.x;

    private void FixedUpdate()
    {
        float gravity = _isGrounded ? 0 : _gravity;

        _velocity.x = _xAxis * _moveSpeed;
        _velocity.y = _isGrounded ? 0 : _velocity.y - gravity * Time.fixedDeltaTime;

        _slideMovement.surfaceAnchor = Vector2.down * (_isGrounded ? _anchorDistance : 0);

        Rigidbody2D.SlideResults slideResults = _rb.Slide(_velocity, Time.fixedDeltaTime, _slideMovement);
        _rb.linearVelocity = Vector2.zero;
    }
}

I’ve removed the jump and some other features for simplicity. For your information, the Rigidbody is kinematic and uses full kinematic contacts (for an isGrounded check). Collision detection is set to continuous.

Finally, it might be useful to know that the gap size appears to be consistent when repeatedly moving into the same wall, unless the rigidbody’s x position is manually changed or it moves up a slope:
WallGap1

Let me know if there’s anything else I can provide, and thanks for the help!

I’ve got to step away for now but I’ve just grabbed the project set a reminder for the morning to look at it.

I’ll see what’s going on and get back to you ASAP!

Thanks!

So I just debugged this and it’s actually a bug in the slide algorithm which is cutting short the final move.

I’ve now fixed it: Screen capture - 86f7125a06d86857b5987c52743a408b - Gyazo

Unfortunately I don’t have any workarounds you can use.

You can track the fix here: Unity Issue Tracker - Rigidbody2D.Slide doesn&#39;t always complete a movement under certain circumstances.

Ah, that’s good to see! Glad you managed to solve it. Do you happen to have a rough estimate for when changes like this typically make it into a public Unity release? I’m happy to wait, but I’d just like to know whether we’re talking weeks or years so I can decide if I should work on a custom slide implementation in the meantime.

Impossible to give you a guarantee but I’ve already created several PR including the backports to 6000.0 and all have already been reviewed. I’ve just added our release managers to it so they should go in the release queues in the next day or so. The weekend will slow things down though.

From then, it’ll be down to the next scheduled release for each.

For 6000 LTS that’ll be early October; two releases are scheduled in that month.

The bug link will be automatically updated when each PR lands and should show which version it’ll be in.

I’ll ping this thread when they land too.

That’s great news, I was expecting a longer wait. Thanks again for all the help.

The bug fix has landed and will be in 6000.2.8f1, 6000.3.0b5 & 6000.4.0a2.

Just waiting on the backport to 6000 LTS to land.

The final port has landed and will be in 6000.0.60f1.

Sorry for adding this here, I couldn’t find out whether there’s an @mention feature. If it’s not too much trouble, could I ask you to take a look at this thread? I had some additional issues with Slide(), and since you’re already familiar with the project, I thought you might be able to tell where the behavior I’m seeing is coming from. If you do decide to take a look, I’ve updated the Drive from last time, should you need the latest version. Either way, thanks for your consideration!