Jumping

Discuss any questions about BEPUphysics or problems encountered.
Post Reply
BoltonBeedz
Posts: 45
Joined: Fri Dec 07, 2007 12:58 am

Jumping

Post by BoltonBeedz »

Hi. I am working on a fps and i want it so when i press A on the controller or the space bar, the player jumps. I need to check if the player is in contact with the floor or on top of a box that isn't falling, otherwise he can fly by repeatedly jumping

It would be nice if the jump force could be resolved between the player and the object the player is on, eg if the player is elevated on a seesaw and jumps, the seesaw would be pushed down, and the player wouldn't jump as high.

So my question is this: How can an entity best jump?
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Jumping

Post by Norbo »

One way to do this would be to send a ray out from the player entity's feet down a couple of inches using Toolbox.findFiniteRayEntityHit. Only objects which are listed in the player's controller list ((Entity).controllers) would need to be compared against. The controllers store collision data between pairs of objects, so you can avoid doing tests on half of the controller entries as they will be the player entity itself.

Once a proper support is found (don't need to continue if no support available), the next problem is calculating the jump amount. Perhaps the easiest thing to do is apply an arbitrary impulse/velocity (depending on if the player entity is physically simulated or not). This still doesn't support the seesaw idea, though, so it can be expanded. The goal is to make the jump act somewhat like a contact point; we know the total 'corrective impulse' to be applied (it is the jump force the player can exert). The jump force must then be appropriately shared and applied in different portions to the support object and to the player (assuming the support object is physically simulated, otherwise all of the jump force propels the player entity).

While at the bottom of the post I will describe a simplified/less 'physical' system, the following should provide realistic results provided that I haven't miscalculated. Assuming playerEntityis physicallySimulated, supportEntity is physicallySimulated, Ra is the vector from the position of the player to the position of the ray-support entity intersection, Rb is the vector from the position of the support object to the ray-support entity intersection, jumpForceDirection is the normalized direction in which you want to apply the jump force (Vector3.Up might be a good choice), and the ray direction was along the Y axis, a scaling value used determine the proper force to apply to each object is:

Code: Select all

Ra = rayHit - playerEntity.getPosition();
Rb = rayHit - supportEntity.getPosition();
impulseTransform = 1 / playerEntity.mass + 1 / supportEntity.mass +
                           Vector3.Dot(Vector3.Cross(Vector3.Transform((Vector3.Cross(Ra, jumpForceDirection)), playerEntity.inertiaTensorInverse), Ra) +
                                            Vector3.Cross(Vector3.Transform((Vector3.Cross(Rb, jumpForceDirection)), supportEntity.inertiaTensorInverse), Rb), jumpForceDirection);
If, however, your player entity cannot rotate, the equation simplifies (the first cross product is removed):

Code: Select all

Rb = rayHit - supportEntity.getPosition();
impulseTransform = 1 / playerEntity.mass + 1 / supportEntity.mass +
                           Vector3.Dot(Vector3.Cross(Vector3.Transform((Vector3.Cross(Rb, jumpForceDirection)), supportEntity.inertiaTensorInverse), Rb), jumpForceDirection);
If your supportEntity is a static object, the equation simplifies further:

Code: Select all

impulseTransform = 1 / playerEntity.mass;
Now for the calculation of the impulse; the 'corrective impulse' at this contact point is simply the amount of force you want your jump to apply in a vector form (if your jumpForceDirection is always constant, the Vector3.Dot can be precalculated):

Code: Select all

Vector3 toApply = Vector3.Dot(jumpForce, jumpForceDirection) / impulseTransform;
Finally, apply this impulse:

Code: Select all

playerEntity.applyImpulse(rayHit, toApply);
supportEntity.applyImpulse(rayHit, -toApply);
Of course, the above assumed both entities involved were physically simulated; a more general approach is:

Code: Select all

                    
                    
                    taImpulse = Vector3.Cross(Ra, toApply); //Delete this line if player cannot rotate from vertical
                    ((PhysicallySimulated)playerEntity).linearMomentum += toApply; //If player is static, delete this line
                    ((PhysicallySimulated)playerEntity).angularMomentum += taImpulse; //Delete this line if player cannot rotate from vertical or if player is static
                    playerEntity.compoundLinearVelocity = playerEntity.linearMomentum / ((PhysicallySimulated)playerEntity).mass;  //If player is physically simulated
                    //playerEntity.compoundLinearVelocity = (playerEntity.compoundLinearVelocity * playerMassValue + toApply) / playerMassValue; //Use this line if player is static
                    playerEntity.compoundAngularVelocity = Vector3.Transform(playerEntity.angularMomentum, ((PhysicallySimulated)playerEntity).inertiaTensorInverse); //delete this line if player cannot rotate off vertical
                    // playerEntity.compoundAngularVelocity = Vector3.Transform(Vector3.Transform(playerEntity.compoundAngularVelocity, Matrix.Identity)  + taImpulse, Matrix.Identity);        //use this line if player is static, but you want it to rotate; change Matrix.Identities to an inertia tensor and inverse inertia tensor respectively that fits the object for proper behavior
                    if (supportEntity is PhysicallySimulated)
                    {
                        tbImpulse = Vector3.Cross(Rb, toApply);
                        ((PhysicallySimulated)supportEntity).linearMomentum -= toApply;
                        ((PhysicallySimulated)supportEntity).angularMomentum -= tbImpulse;
                        supportEntity .compoundLinearVelocity = supportEntity.linearMomentum / supportEntity.mass;
                        supportEntity .compoundAngularVelocity = Vector3.Transform(supportEntity.angularMomentum, supportEntity.inertiaTensorInverse);
                    }

I have not personally been able to test this code for this purpose, so I very much hope that I didn't make any mistakes.

If you wish to bypass worrying about the impulseTransform bit or angular considerations, you could simply see which object should use more the impulse by comparing the inverse masses, and applying a fraction of the impulse equal to (invMass(A or B) / (invMassA + invMassB) * jumpForce * jumpForceDirection.

The complexity of this task provides excellent reason for implementing character controllers directly into the engine, and I do plan to. Unfortunately, unless I am able to rapidly complete some of the large changes in the next version, it will likely make it into v0.5.0 rather than v0.4.0.

Good luck!

EDIT:
For those of you looking for information on jumping characters in the future:

During my implementation of character controllers for v0.5.0, I have found that the 'physically correct' jumping scheme I previously outlined is often inadequate because objects below the supporting entity do not 'feel' the impulse. This results in situations such as a character standing on a stack made of light boxes placed on a static object and the character jumps, but is only pushed slightly upward. This is because, according to the above method, the box is going to be moved the most, while the player, being heavier, is moved relatively little. However, the box is in a stack and cannot move downward. The engine solver handles the box's situation appropriately, and the character ends up going up about an inch, despite looking like it should have received the full jump impulse (as indeed it should have).

There is no easy way that I'm currently aware of to let the player receive the full jump in an entirely physically correct manner in the above case. It would have to be integrated into the solver more closely, which is not really worth it. Instead, you can choose to use the 'physically correct' method for objects floating in space and not in contact with any other bodies, and resort to a less correct method in situations where the body below has contacts (and thus possibly support). A simple way is to assume the support is essentially of infinite mass and apply the full jump impulse to both the support and the character, giving the player the push they expect for jumping (even if it isn't perfectly true to reality). I use a similar hybrid approach in my character controllers.
BoltonBeedz
Posts: 45
Joined: Fri Dec 07, 2007 12:58 am

Re: Jumping

Post by BoltonBeedz »

jinkys
Post Reply