That was suspiciously easy

Slope experiment continues apace. You can now run over little hills without being launched into the air. Take a look!

Accomplishing this was relatively straightforward. Basically, after each simulation step, you check to see if the character is in contact with the ground. If not, and there wasn’t a jump initiated, you probe underneath their feet with a ray cast, then adjust their position and velocity accordingly if there’s a slope underneath them. It looks like so:

// If we haven't jumped, stick to the ground unless it's a cliff
if ( _onGround && !_jumpInitiated && !_standingMonitor.IsStanding ) {
    float closestFraction = 1;
    Vector2 closestPoint = Vector2.Zero;
    Vector2 closestNormal = new Vector2();

    foreach (var start in new[] {
        GetStandingLocation() + new Vector2(Width / 2f, 0),
        GetStandingLocation() + new Vector2(-Width / 2f, 0),
    } ) {
        _world.RayCast((fixture, point, normal, fraction) => {
            if ( fixture.GetUserData().IsTerrain && fraction < closestFraction ) {
                closestFraction = fraction;
                closestPoint = point;
                closestNormal = normal;
            }
            return 1;
        }, start, start + new Vector2(0, .9f));
    }
    if ( closestPoint != Vector2.Zero ) {
        _body.Position = new Vector2(_body.Position.X, closestPoint.Y - Height / 2 - .01f);
        // tweak the velocity to match the normal of the stuck-to slope
        float speed = _body.LinearVelocity.Length();
        // The rotation won't work properly unless we flip the y axis
        closestNormal = new Vector2(closestNormal.X, -closestNormal.Y);
        Vector2 rotated = Vector2.Transform(closestNormal, Matrix.CreateRotationZ(_facingDirection == Direction.Left ? Projectile.PiOverTwo : -Projectile.PiOverTwo));
        _body.LinearVelocity = new Vector2(rotated.X, -rotated.Y) * speed;
    } else {
        _onGround = false;
        IsStanding = false;
    }
}

Sadly, it doesn't work 100% of the time. About one time in 10, if you're moving at absolutely top speed and you hit the downward slope just right coming off the table top, you'll still catch air. That's because the character has moved so far horizontally in one frame that the ground has fallen more than the probe distance (.9 tiles) away from their feet. But I think I can live with that for now and consider what I have so far a proof of concept.

Leave a Reply