SphereCharacterController request move to position

Discuss any questions about BEPUphysics or problems encountered.
Post Reply
Uros
Posts: 3
Joined: Tue May 03, 2016 6:50 pm

SphereCharacterController request move to position

Post by Uros »

Hey there!

First of all, thank you so much Ross for the awesome engine. In the last weeks I've implemented it into my simple isometric 3d rpg/hack&slash game without any problems. Works like a charm. However I've stumbled upon a problem recently, and I just cant find a solution. Here's the story.

For npc's and player I am using SphereCharacterController. Most of the time, player is moved around using keyboard input, and the npc's are following the paths calculated by A*. For that part, it all works perfectly, updating the controller each frame like this:

Code: Select all

CharController.Speed = FixedCharSpeed;
Char.HorizontalMotionConstraint.MovementDirection = CharDirection;
However, there are cases, when the movement of a player or npc must be controlled by the animation (when performing special combo attacks for example), so that move dir and ammount perfectly fits animation. I am doing it by appending the animation root bone position change to position each frame like so:

Code: Select all

Position = Position + AnimPlayer.RootBoneDeltaMove;
So basically, I need a way to tell SphereCharacterController/HorizontalMotionConstraint to try to move to a specific position (or for a specific vector) each frame, but I simply cannot figure out how to do so. I do not want to reinvent the wheel here, as SphereCharacterController otherwise perfectly fits my needs and would like to stick with it.

The only idea I came up with was recalculating the speed each frame when animation is controlling the movement:

Code: Select all

CharController.Speed = AnimPlayer.RootBoneDeltaMove.Length() * SpeedScale;
Char.HorizontalMotionConstraint.MovementDirection = Vector3.Normalize(AnimPlayer.RootBoneDeltaMove);
But I do not know how to scale the speed (SpeedScale), since I guess it should consider many factors like char mass, traction, etc...??
I know I could teleport the char to the desired position by setting it's Body.Position, but then I'd loose all the collision response, which I still need as normally, even when animation is controlling the movement.

Please help me out! :)

Thanks a lot!

Regards,
Uros
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: SphereCharacterController request move to position

Post by Norbo »

There's two main components: computing the desired speed, and making sure the character can actually reach that speed.

The desired speed is the frame's displacement divided by the timestep, so first we'll need a displacement. For the sake of character movement, only the horizontal motion matters- the horizontal motion constraint can't cause vertical motion anyway. So, assuming it's a single frame displacement, project RootBoneDeltaMove onto the character's horizontal movement basis:

Code: Select all

var horizontalDisplacement = new Vector2(Vector3.Dot(character.HorizontalMotionConstraint.StrafeDirection, RootBoneDeltaMove), Vector3.Dot(character.HorizontalMotionConstraint.ForwardDirection, RootBoneDeltaMove));
The time in question is just a single frame, dt, so:

Code: Select all

var targetVelocity = horizontalDisplacement / dt;
var targetSpeed = targetVelocity.Length();
In defining the target speed, you don't have to worry about things like traction or mass; that's for later. The desired direction is based on the RootBoneDeltaMove as you already noted. Since the movement direction is in terms of the horizontal motion constraint's basis still, we can then just set the movement direction directly:

Code: Select all

character.HorizontalMotionConstraint.MovementDirection = horizontalDisplacement;
To ensure that the character can actually reach the speed, it must be able to apply enough force to accelerate to that speed. This is handled primarily through the HorizontalMotionConstraint.MaximumForce property. To change velocity from x to y instantly, an impulse of mass * (y-x) is required. Since we're talking about the required force to cause that change, we need to define how long we have to accelerate to that speed- in this case, we want to reach the speed in one timestep, or dt. If we applied an impulse of mass * (y-x) every single frame for a second, the effective force would be mass * (y-x) / dt. So, if you wanted to compute the force required to successfully change the velocity, it would look like:

Code: Select all

character.Body.Mass * (targetSpeed - currentVelocity) / dt
where currentVelocity is the current velocity along the desired motion vector, and targetVelocity is the speed computed in the first part. To get the current velocity, you can project the current body linear velocity onto the the 3d direction of movement:

Code: Select all

var currentVelocity = Vector3.Dot(character.HorizontalMotionConstraint.MovementDirection.X * character.HorizontalMotionConstraint.StrafeDirection + character.HorizontalMotionConstraint.MovementDirection.Y * character.HorizontalMotionConstraint.ForwardDirection, character.Body.LinearVelocity);
(If the character could be standing on mobile objects, you'd have to take into account the character velocity relative to the support velocity- conceptually, it's all the same, but it obfuscates the concepts here a bit so I left it out.)

You'll probably want to limit that force, though. If the animation does anything particularly quick, the resulting unbounded character movement force could be high enough to blast obstacles (or itself) into space. It may actually work fine without any dynamic modification to the maximum force- it'll fail to reach the goal sometimes, but that'll happen anyway when ramming into a wall.
Uros
Posts: 3
Joined: Tue May 03, 2016 6:50 pm

Re: SphereCharacterController request move to position

Post by Uros »

Hey, thanks for a quick and detailed reply! It works like a charm. Even without modifying the max force(since the controller has a rather high traction force anyway), so I will leave that part out for now, to stay on the safe side :).

I'll post a demo soon, so you can see your engine in action :).

Thanks again!
Uros
Posts: 3
Joined: Tue May 03, 2016 6:50 pm

Re: SphereCharacterController request move to position

Post by Uros »

Hello again Norbo!

I am mostly done integrating bepu physics engine into my game now :D It rocks! I have 2 more questions regarding the SphereCharacterController:

1. I want my characters never to fly off the ground(when they are controlled by SphereCharacterController), to stay glued to the ground at all times. Even if I set maximumGlueForce to a high value like 500000, they still fly off fairly easily. What would be the correct, and safest way to keep them on the ground at all times?

2. I changed the SphereCharacterController Body to be a cylinder or capsule instead of sphere, as it better fits my needs. It all seemes to work flawlessly. Is there anything I should be careful about? I presume sphere is the most efficient performancewise. Is cylinder or capsule much slower? I havent been able to detect any significant performance difference with about 10 active controllers at a time.

Thanks for your time!
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: SphereCharacterController request move to position

Post by Norbo »

1. I want my characters never to fly off the ground(when they are controlled by SphereCharacterController), to stay glued to the ground at all times. Even if I set maximumGlueForce to a high value like 500000, they still fly off fairly easily. What would be the correct, and safest way to keep them on the ground at all times?
Under what conditions is it flying off? The SupportFinder performs a ray test if the previous frame had traction and there are no current support contacts. Even with no direct contacts detected, this ray cast can provide traction. As long as the character has traction, the vertical motion constraint (and so glue force) should keep them attached. 500000 should be more than enough to keep it solidly stuck, barring something like kinematic intervention. Something odd might be going on- you may want to throw in some breakpoints to catch when the support finder fails to find a ray cast support after losing traction. The relevant code block starts at line 339 in SupportFinder.cs.

If the characters are large relative to the demo scale, it could be that the SupportFinder.MaximumAssistedDownStepHeight default of 1 is too small.
2. I changed the SphereCharacterController Body to be a cylinder or capsule instead of sphere, as it better fits my needs. It all seemes to work flawlessly. Is there anything I should be careful about? I presume sphere is the most efficient performancewise. Is cylinder or capsule much slower? I havent been able to detect any significant performance difference with about 10 active controllers at a time.
Sphere is indeed a bit faster than either the cylinder or capsule. It's got some special case collision detection. That said, you'd probably need thousands of characters for it to start to matter.

The main difference between a cylinder and a sphere (or other sufficiently rounded shape) is that it won't climb steps as well. It'll just thunk up against the side of any nontrivial obstacle. The non-sphere CharacterController uses a cylinder and has complicated stepping logic built in to support cleanly going up and down steps. That stepping logic can be a little expensive though- for my own project that needs many thousands of characters, I'm using a rounded cylinder (almost capsule-ish) with stepping logic disabled.
Post Reply