Bepu v2 Character Controllers

Discuss any questions about BEPUphysics or problems encountered.
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Bepu v2 Character Controllers

Post by Norbo »

I noticed that there's no air control. Is there a built-in way to implement this?
Nope, that's one of the features I decided not to implement for the demos. You could modify the CharacterControllers to create a StaticCharacterMotionConstraint while the character is in the air and give it appropriately low maximum force values. Or, since the robustness of a solved constraint is less important for air control, you could just do whatever you want to velocities to achieve your desired behavior.
Additionally, when the character stands on a moving object—such as a ship—and jumps, he doesn't have the same velocity as the moving object, meaning jumping is quite unpredictable in terms of where you'll land (sometimes not even on the same object). When a character stands, I'm assuming friction makes him move with whatever he's on top of. What would be the best way to go about giving him the same velocity as the object beneath him when he jumps?
It sounds like the simulation was given pose integrator callbacks that include damping. If you're using something like the DemoPoseIntegratorCallbacks, the damping implementation simply scales the current linear velocity by a value below 1 each time step. This has the effect of converging to a velocity of 0 over time. Since 0 velocity is measured according to a fixed 'world' frame of reference, jumping while on a moving boat will make the character appear to drift relative to the boat.

To address this, either use a smaller (or zero) linear damping or use a damping implementation that is aware of your frame of reference. In this case, perhaps wind speed or something. You could also use the ship's local frame of reference, although that's not exactly physical.

Incidentally, this is part of the reason why I didn't include air control in the demos. It's nonphysical and requires game-specific heuristics to produce decent results. On a moving boat, you'd probably want air control to be relative to the local frame of reference.
Finally, I noticed that unlike v1, v2 has no ApplyAngularImpulse method. Is there a different built-in method that I'm not aware of? If not, what's the alternative?
For an instantaneous angular impulse t, ApplyAngularImpulse(t) is the same as angularVelocity += t * inverseInertiaTensor. If you primarily care about changing the angular motion without regard for the inertia, you can just directly change the angular velocity too.
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Bepu v2 Character Controllers

Post by Norbo »

I went ahead and added some BodyReference impulse helpers (and fixed another character bug): https://github.com/bepu/bepuphysics2/co ... 03a0adcc90
tomweiland
Posts: 99
Joined: Wed May 08, 2019 12:17 am

Re: Bepu v2 Character Controllers

Post by tomweiland »

Norbo wrote: Wed Jun 05, 2019 11:04 pm I went ahead and added some BodyReference impulse helpers (and fixed another character bug): https://github.com/bepu/bepuphysics2/co ... 03a0adcc90
Awesome, thanks!
Norbo wrote: Wed Jun 05, 2019 10:00 pm you could just do whatever you want to velocities to achieve your desired behavior.
I'm thinking I'll probably go that way since it sounds a lot simpler. Is there a property I can access to quickly check if the character is standing on something? CharacterController doesn't seem to have one...
Norbo wrote: Wed Jun 05, 2019 10:00 pm It sounds like the simulation was given pose integrator callbacks that include damping. If you're using something like the DemoPoseIntegratorCallbacks, the damping implementation simply scales the current linear velocity by a value below 1 each time step. This has the effect of converging to a velocity of 0 over time.
I did pull the pose integrator callbacks out of one of the demos, although I can't remember which one. The actual application of the damping implementation would happen in the IntegrateVelocity method right? In there I only have this:

Code: Select all

if (localInertia.InverseMass > 0)
{
    velocity.Linear = velocity.Linear + gravityDt;
}
Norbo wrote: Wed Jun 05, 2019 10:00 pm Since 0 velocity is measured according to a fixed 'world' frame of reference, jumping while on a moving boat will make the character appear to drift relative to the boat.

To address this, either use a smaller (or zero) linear damping or use a damping implementation that is aware of your frame of reference. In this case, perhaps wind speed or something. You could also use the ship's local frame of reference, although that's not exactly physical.

Incidentally, this is part of the reason why I didn't include air control in the demos. It's nonphysical and requires game-specific heuristics to produce decent results. On a moving boat, you'd probably want air control to be relative to the local frame of reference.
So when the character is standing on a moving object, he moves with it. The friction is what's holding him in place, right? So if he jumps and is no longer touching the object, shouldn't the result be closer to what we see in real life? What I mean is, shouldn't the velocity he had when he was touching it carry over, or do the motion constraints interfere with that?
Is there really no simple way to have the character conserve his momentum after separating from a moving object?

I guess as a workaround I could potentially somehow get the velocity of whatever he's standing on, but that would require accessing his supporting contacts.

What would using "a damping implementation that is aware of your frame of reference" roughly look like? That sounds like a more physically-based solution than changing the frame of reference based on what the character is standing on. Using wind speed might be an option, but its direction rarely matches the ship's velocity's direction (ship goes up and down waves, thereby rotating, or it might be sailing perpendicular to the wind). Its magnitude is also not guaranteed to match since the ship will have to accelerate and decelerate at certain times.
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Bepu v2 Character Controllers

Post by Norbo »

Is there a property I can access to quickly check if the character is standing on something?
CharacterController.Supported for a boolean, CharacterController.Support for the collidable it's standing on.
The actual application of the damping implementation would happen in the IntegrateVelocity method right?
Yup. In the DemoPoseIntegratorCallbacks, it looks like:

Code: Select all

            if (localInertia.InverseMass > 0)
            {
                velocity.Linear = (velocity.Linear + gravityDt) * linearDampingDt;
                velocity.Angular = velocity.Angular * angularDampingDt;
            }
So when the character is standing on a moving object, he moves with it. The friction is what's holding him in place, right? So if he jumps and is no longer touching the object, shouldn't the result be closer to what we see in real life? What I mean is, shouldn't the velocity he had when he was touching it carry over, or do the motion constraints interfere with that?
Is there really no simple way to have the character conserve his momentum after separating from a moving object?
Without damping, linear momentum should be conserved and the character should continue on a ballistic trajectory. The motion constraints are removed when the character is in the air.

If you haven't already, updating to the latest version of the character would be wise just to avoid any of the bugs that got fixed.
What would using "a damping implementation that is aware of your frame of reference" roughly look like? That sounds like a more physically-based solution than changing the frame of reference based on what the character is standing on. Using wind speed might be an option, but its direction rarely matches the ship's velocity's direction (ship goes up and down waves, thereby rotating, or it might be sailing perpendicular to the wind). Its magnitude is also not guaranteed to match since the ship will have to accelerate and decelerate at certain times.
Since damping is effectively a very approximate version of aerodynamic drag, making damping converge toward the windspeed rather than zero would be a physically reasonable approach. It wouldn't match the ship's local frame of reference, but neither would aerodynamic drag in reality.

Choosing a local reference based on the ship you're on is indeed nonphysical for an open air ship, but can be useful for gameplay reasons or if simulating other circumstances. These details are far more important at higher speeds (or higher damping values)- consider a game that takes place on jets. If you're inside a jet, you wouldn't want damping relative to the external wind speed to slam you into the back of the cabin.

But to reiterate, if you're not using damping, characters should move ballistically like any other dynamic body when not supported. If they don't seem to be, something else is going on.
tomweiland
Posts: 99
Joined: Wed May 08, 2019 12:17 am

Re: Bepu v2 Character Controllers

Post by tomweiland »

Norbo wrote: Fri Jun 07, 2019 12:18 am If you haven't already, updating to the latest version of the character would be wise just to avoid any of the bugs that got fixed.
Just updated, it solved the issue of the character's linear momentum not being conserved, so that's great.

In terms of air control though, I'm a little stuck. I don't want to overwrite any velocity the character receives from other sources (like an explosion for example), but adding a certain velocity to the existing one will result in drastic accelerations. Is using motion constraints the only solution/workaround, or am I missing another simpler option?

Also, something I've been wondering: when I copy the CharacterControllers class into my project, it complains about the MathF class not being "accessible due to its protection level" (lines 553, 570, 593). I've just been swapping it out for the Math class, which seems to work fine, but I doubt this is intentional.
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Bepu v2 Character Controllers

Post by Norbo »

In terms of air control though, I'm a little stuck. I don't want to overwrite any velocity the character receives from other sources (like an explosion for example), but adding a certain velocity to the existing one will result in drastic accelerations. Is using motion constraints the only solution/workaround, or am I missing another simpler option?
Here's one option:

Code: Select all

currentMovementVelocity = dot(movementDirection, currentVelocity)
targetMovementVelocity = max(0, min(maximumSpeed, currentMovementVelocity + acceleration * dt))
changeInMovementVelocity = targetMovementVelocity - currentMovementVelocity
currentVelocity += changeInMovementVelocity * movementDirection
It measures the current velocity along the desired movement direction and then attempts to accelerate along it if permitted by the maximum speed. This has some quirks- the maximum speed is relative to the world frame of reference, and it's possible to 'surf' through the air half-life style.
Also, something I've been wondering: when I copy the CharacterControllers class into my project, it complains about the MathF class not being "accessible due to its protection level" (lines 553, 570, 593). I've just been swapping it out for the Math class, which seems to work fine, but I doubt this is intentional.
The MathF class is a part of .NET Core 2.0 (and higher). It sounds like there's a name collision with some other MathF type, or possibly the application target isn't .NET Core 2.0+. Notably .NET Standard 2.0 does not have MathF (which is why the library itself doesn't use it); I think that's coming in .NET Standard 2.1.
tomweiland
Posts: 99
Joined: Wed May 08, 2019 12:17 am

Re: Bepu v2 Character Controllers

Post by tomweiland »

Norbo wrote: Mon Jun 10, 2019 5:17 am Here's one option:

Code: Select all

currentMovementVelocity = dot(movementDirection, currentVelocity)
targetMovementVelocity = max(0, min(maximumSpeed, currentMovementVelocity + acceleration * dt))
changeInMovementVelocity = targetMovementVelocity - currentMovementVelocity
currentVelocity += changeInMovementVelocity * movementDirection
It measures the current velocity along the desired movement direction and then attempts to accelerate along it if permitted by the maximum speed.
After some playing around with it, I ended up with this (this is just for forwards/backwards, I do the same thing for left/right):

Code: Select all

float _currentZVelocity = Vector3.Dot(_character.ViewDirection * Math.Sign(inputDirection.Y), gameObject.collider.Velocity.Linear);
float _targetZVelocity = Math.Max(0, Math.Min(airSpeed, _currentZVelocity + 0.25f));
float _changeInZVelocity = _targetZVelocity - _currentZVelocity;
if (_changeInZVelocity > 0)
{
    gameObject.collider.Velocity.Linear += _changeInZVelocity * inputDirection.Y * _character.ViewDirection;
}
Without the if statement, any momentum the player had while running is immediately upon liftoff reduced to what is allowed by the airspeed.

The one most noticeable issue I've encountered is that if I run and jump, I continue with the proper momentum, however as soon as I press the key to move in the opposite direction of my current velocity, I lose all momentum and change direction. It almost looks like I'm hitting a wall mid-air. I could probably get around that with another few if statements, but maybe you have some insight as to how I could pull it off in a more elegant and efficient way?
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Bepu v2 Character Controllers

Post by Norbo »

I added a little example to the CharacterDemo showing one possible implementation:
https://github.com/bepu/bepuphysics2/co ... 67376d5c73

(The pseudocode snippet I posted earlier had a sleepygoof- the target velocity should not be clamped against zero, the change should be. Clamping the target to zero would cause drastic wall-smack velocity changes.)
tomweiland
Posts: 99
Joined: Wed May 08, 2019 12:17 am

Re: Bepu v2 Character Controllers

Post by tomweiland »

Norbo wrote: Fri Jun 14, 2019 9:53 pm I added a little example to the CharacterDemo showing one possible implementation:
https://github.com/bepu/bepuphysics2/co ... 67376d5c73
This is great.

After adapting my code a little, it now looks like the following:

Code: Select all

Vector3 _right = Vector3.Normalize(Vector3.Cross(_character.ViewDirection, new Vector3(0, 1, 0)));
Vector3 _worldMovementDirection = _right * inputDirection.X + _character.ViewDirection * inputDirection.Y; // Get input direction in world space
float _currentVelocity = Vector3.Dot(gameObject.collider.Velocity.Linear, _worldMovementDirection); // Calculate current velocity in world space input direction
float _airAcceleration = gameObject.collider.LocalInertia.InverseMass * _character.MaximumHorizontalForce * 0.2f; // Calculate acceleration
float _targetVelocity = Math.Min(_currentVelocity + _airAcceleration, airSpeed); // Calculate the target velocity
float _velocityChange = Math.Max(0, _targetVelocity - _currentVelocity); // Calculate the required change in velocity to achieve the target (however, don't allow deceleration due to input)
gameObject.collider.Velocity.Linear += _worldMovementDirection * _velocityChange; // Apply the velocity change
but when I press a key (while in the air) to move in the opposite direction of my current momentum, it still looks like I hit a wall - the velocity's direction changes seemingly instantly.

I've double checked the code a few times, and I'm pretty certain I have the same code as you, but maybe something slipped through?
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Bepu v2 Character Controllers

Post by Norbo »

A couple of notes:
1) If the character.ViewDirection is not on the horizontal plane, that version will create a _worldMovementDirection which is not on the horizontal plane. In the CharacterDemo version, a second cross product is used to complete the horizontal basis.
2) The airAcceleration needs to be scaled by the simulation time step duration. In that version, the full acceleration is being applied on every single update, which means it's accelerating 60 or 120 times (whatever your update rate is) faster than it should be. (Unless _airAcceleration already has the timestep duration premultiplied into it.)
tomweiland
Posts: 99
Joined: Wed May 08, 2019 12:17 am

Re: Bepu v2 Character Controllers

Post by tomweiland »

Norbo wrote: Wed Jun 19, 2019 12:50 am 1) If the character.ViewDirection is not on the horizontal plane, that version will create a _worldMovementDirection which is not on the horizontal plane. In the CharacterDemo version, a second cross product is used to complete the horizontal basis.
I've made sure it's horizontal.
Norbo wrote: Wed Jun 19, 2019 12:50 am 2) The airAcceleration needs to be scaled by the simulation time step duration. In that version, the full acceleration is being applied on every single update, which means it's accelerating 60 or 120 times (whatever your update rate is) faster than it should be. (Unless _airAcceleration already has the timestep duration premultiplied into it.)
Yeah, this was the issue. I had left that out because it's a constant but I totally forgot to account for the fact that if I didn't multiply by the time step duration, it would be way too large.

In terms of adding ladder support to the controller, I'm wondering if it's possible to make a certain body climbable for the character no matter the angle...basically to allow the controller to use it as a support even if it's vertical or on a slight overhang. Is something like this possible? If not, do you have any suggestions about how I should go about making ladders?
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Bepu v2 Character Controllers

Post by Norbo »

In terms of adding ladder support to the controller, I'm wondering if it's possible to make a certain body climbable for the character no matter the angle...basically to allow the controller to use it as a support even if it's vertical or on a slight overhang. Is something like this possible?
If you wanted to make use of the same motion constraints, you'd need to modify how their basis is created. The default behavior hits a singularity at 90 degrees, so that'd need to be conditionally swapped out. The relevant area is: https://github.com/bepu/bepuphysics2/bl ... rs.cs#L581

You'd also need to add some way for the character to look up the ladder-ness of a body so that you'd know to use the alternate basis path.
If not, do you have any suggestions about how I should go about making ladders?
Many games have adopted a fixed path approach for ladders. Rather than allowing free movement like Half-Life, interacting with a ladder attaches the character to the ladder and movement input just moves the character along a fixed path. This would be a little easier to handle since you could simply disable the character's motion constraints while it's on the ladder. Depending on whether the ladder is dynamic or static and on desired behavior, you could use constraints like the OneBodyLinearServo, BallSocket, or PointOnLineServo with a LinearAxisMotor/Servo.
tomweiland
Posts: 99
Joined: Wed May 08, 2019 12:17 am

Re: Bepu v2 Character Controllers

Post by tomweiland »

Norbo wrote: Wed Jun 19, 2019 10:06 pm Many games have adopted a fixed path approach for ladders. Rather than allowing free movement like Half-Life, interacting with a ladder attaches the character to the ladder and movement input just moves the character along a fixed path. This would be a little easier to handle since you could simply disable the character's motion constraints while it's on the ladder. Depending on whether the ladder is dynamic or static and on desired behavior, you could use constraints like the OneBodyLinearServo, BallSocket, or PointOnLineServo with a LinearAxisMotor/Servo.
I'll probably go this path. The ship moves with the waves, so any ladders attached to it will need to be dynamic - I'll have to look into all of those constraints and how to use them.

Performance-wise, are there any I should use over others when possible or is the difference negligible? Also, is there somewhere I can read a summar/description of each instead of slogging through the code?
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Bepu v2 Character Controllers

Post by Norbo »

Performance-wise, are there any I should use over others when possible or is the difference negligible?
Unless you've got thousands of them, it'll probably be hard to measure the difference.

I suspect the PointOnLineServo/LinearAxisServo combo would be very slightly slower than BallSockets due to being a pretty minimalist 3DOF constraint and not requiring two constraints, but servos would give you more behavioral control. (And the performance difference for ~dozens of constraints would likely be in the realm of microseconds.)
Also, is there somewhere I can read a summar/description of each instead of slogging through the code?
Sorta-kinda. The v1 documentation uses slightly different names and doesn't have a perfect 1:1 mapping, but it does have some visualizations which carry over:
https://github.com/bepu/bepuphysics1/bl ... traints.md
The list of built-in v2 constraints can be found in the Constraints namespace:
https://github.com/bepu/bepuphysics2/tr ... onstraints
Most constraint description types have some intellisense documentation regarding their function, though that is an area that is a bit incomplete.
tomweiland
Posts: 99
Joined: Wed May 08, 2019 12:17 am

Re: Bepu v2 Character Controllers

Post by tomweiland »

So I've been playing around with it, trying to get a PointOnLineServo working, but I'm having some issues. The code that I have I managed to piece together from the demos (although I couldn't find any that specifically use PointOnLineServos) and from reading the descriptions from v1 you provided, so this may be totally wrong :?

First I'm creating a body to represent the ladder:

Code: Select all

Box _collider = ColliderShapes.CreateBox(0.5f, 10f, 0.5f, 1, out BodyInertia _inertia);
gameObject.collider = new BodyReference(
    Globals.physics.simulation.Bodies.Add(BodyDescription.CreateDynamic(transform.Position, _inertia, new CollidableDescription(Globals.physics.simulation.Shapes.Add(_collider), 0.1f), new BodyActivityDescription(0.1f))),
    Globals.physics.simulation.Bodies
);
I have the servo set up like so:

Code: Select all

servo = new PointOnLineServo()
{
    LocalDirection = new Vector3(0, 1, 0),
    ServoSettings = new ServoSettings(1, 1, 50),
    LocalOffsetA = new Vector3(0, -4, 0),
    LocalOffsetB = new Vector3(0, 5, 0),
};
And then to attach the player to the ladder I do this:

Code: Select all

constraintHandle = Globals.physics.simulation.Solver.Add(playerCollider.Handle, gameObject.collider.Handle, servo);
I use this to remove the player from the ladder:

Code: Select all

Globals.physics.simulation.Solver.Remove(constraintHandle);
Shortly after I attempt to attach myself to the ladder, I get the following error (the stack trace leads to where I call the simulation.Timestep() method):
"If the change in heuristic cost hits invalid values, it's likely that there are invalid poses or velocities. A bugged velocity input or constraint triggering an explosion is likely."

Do the bodies need to be within a certain distance of each other in order to be able to link up with the constraint?

Like I said earlier, I may have set this up completely wrong so some guidance would be really appreciated here (even just a link to a demo that deals with PointOnLineServos specifically, because I couldn't find one).
Post Reply