Walking & Jumping character with jME-physics

It's outlined here: http://www.ogre3d.org/wiki/index.php/OgreOde_Walking_Character



This code was directly ripped from my "CharacterEntity" class which I am using for a new project. Do note that it was designed for a 2.5D platformer and could require some changes to fit in a full 3D environment.

The basic idea is this, you have a physics sphere, which is used for the feet, and a cylinder on top of it which is attached with a joint. The cylinder is kept on top of the sphere all the time and is not allowed to fall over- this part is handled by user code. The feet have an additional joint with axis attached, the axis is used as a motor to cause the character to move forward & backward (e.g the sphere rolls). Jumping is done by simply applying an upward force.





This is the character material. Density should be balanced to prevent character from flying too far, but not too high at the same time to prevent large forces. Bouncing should be disabled in most cases. Mu should be low enough to prevent character from sliding, but not high enough to prevent walking on walls.

protected static Material characterMaterial = null;

    static {
        characterMaterial = new Material( "Character Material" );
        characterMaterial.setDensity( 1f );
        MutableContactInfo contactDetails = new MutableContactInfo();
        contactDetails.setBounce(0);
        contactDetails.setMu(1);
        contactDetails.setMuOrthogonal(1);
        contactDetails.setDampingCoefficient(10);
        characterMaterial.putContactHandlingDetails( Material.DEFAULT, contactDetails );
    }



Do note that some of this code may require changes to fit your application.
As you can see, this method detects large forces applied on the character if you want to cause damage to it in that case. It also detects when the character enters the ground; make sure to set onGround to false before updating the physics state.

public void createCharacterPhysics(){
        // here we compute the radius of a virtual cylinder
        // that would totally contain the character
        BoundingBox bbox = (BoundingBox) model.getWorldBound();
        float cRadius = bbox.xExtent > bbox.zExtent ? bbox.zExtent : bbox.xExtent;
        float cHeight = bbox.yExtent * 2f;

        logger.info("ENTITY created. " + charNode.getName() + "R: "+cRadius+", H: "+cHeight);

        PhysicsSpace physicsSpace = Level.getPhysSpace();

        // create the feet physics object
        // it's a sphere on the floor
        feetNode = physicsSpace.createDynamicNode();
        PhysicsSphere feetGeom = feetNode.createSphere("Feet");
        feetGeom.setLocalScale(cRadius);
        feetNode.setMaterial(characterMaterial);
        feetNode.computeMass();
        // I don't know what this does
        model.setLocalTranslation(0, -cRadius / model.getLocalScale().y, 0);
        charNode.attachChild(feetNode);
        feetNode.setUserData("Entity", new EntitySavable(this));

        physNode = physicsSpace.createDynamicNode();
        physNode.setAffectedByGravity(false);
        PhysicsCapsule torsoGeom = physNode.createCapsule("Torso");
        torsoGeom.setLocalScale(new Vector3f(cRadius, cHeight-4*cRadius, cRadius));
        torsoGeom.setLocalTranslation(0, cHeight - ((torsoGeom.getLocalScale().y)/2f+2f*cRadius), 0);
        Quaternion rot = new Quaternion();
        rot.fromAngleAxis(FastMath.HALF_PI, Vector3f.UNIT_X);
        torsoGeom.setLocalRotation(rot);
        physNode.computeMass();

        // NOTE: the model is attached to the torso physics, not feet
        physNode.attachChild(model);
        charNode.attachChild(physNode);
        physNode.setUserData("Entity", new EntitySavable(this));

        // create a joint to keep the capsule locked to the rolling sphere
        Joint joint = physicsSpace.createJoint();
        JointAxis axis = joint.createRotationalAxis();
        axis.setDirection(Vector3f.UNIT_X);
        joint.attach(physNode, feetNode);
        feetWalkAxis = joint.createRotationalAxis();
        feetWalkAxis.setDirection(new Vector3f( 0, 0, 1 ));
        feetWalkAxis.setAvailableAcceleration(10);
        feetWalkAxis.setRelativeToSecondObject(true);

        SyntheticButton collButton = feetNode.getCollisionEventHandler();
        contactDetect.addAction( new InputAction() {
            @Override
            public void performAction( InputActionEvent evt ) {
                ContactInfo contactInfo = (ContactInfo) evt.getTriggerData();
                Vector3f vec = contactInfo.getContactNormal(null);
                if (vec.dot(Vector3f.UNIT_Y) > 0.5f){
                    onGround = true;
                }
                Vector3f vel = contactInfo.getContactVelocity(null);
                if (vel.length() > 10){
                    System.out.println("POWERFUL CONTACT: "+vel.length());
                }
            }
        }, collButton, false );
    }



This method should be called every frame to prevent the character from tripping over.

private void preventFall(){
        Quaternion q = physNode.getLocalRotation();
        Vector3f[] axes = new Vector3f[3];
        q.toAxes(axes);

        q.fromAxes(axes[0], Vector3f.UNIT_Y, axes[2]);
        physNode.setLocalRotation(q);
        physNode.setAngularVelocity(Vector3f.ZERO);
        physNode.updateWorldVectors();
    }



Don't remember what this does. It's possible its for 2D physics only.

private void resetFeetRotation(){
        Quaternion q = feetNode.getLocalRotation();
        Vector3f[] axes = new Vector3f[3];
        q.toAxes(axes);

        q.fromAxes(Vector3f.UNIT_X, Vector3f.UNIT_Y, axes[2]);
        feetNode.setLocalRotation(q);
    }



Here's a good update method

public void update(float tpf){
        if (!allowFall){
            preventFall();
        }
        resetFeetRotation();
        super.update(tpf);
        onGround = false;
        contactDetect.update(tpf);
    }



Jumping is dead simple. You may want to allow the character to nudge left or right during the jump for platforming-style physics.


        if (onGround && scale > 0f){
            feetNode.addForce(new Vector3f(0, 500f * scale, 0f));
        }



Finally this is how you would make your character walk.

if (scale == 0){
            feetWalkAxis.setDesiredVelocity(0);
        }else{
            float desired = scale * maxRun;
            float accel = ((2f * desired) / walkupTime);
            feetWalkAxis.setAvailableAcceleration( accel );
            feetWalkAxis.setDesiredVelocity( desired * direction.getDirection().x );
        }

Nice, I don't think it would even take much work for full 3D :slight_smile:

basixs said:

Nice, I don't think it would even take much work for full 3D :)
It wouldn't. I had this working in a full 3D environment before going into 2D gameplay.
Momoko_Fan said:

basixs said:

Nice, I don't think it would even take much work for full 3D :)
It wouldn't. I had this working in a full 3D environment before going into 2D gameplay.


Then that begs the question why not post the full 3D code? Or have you since lost it?

Not that I mind at all--this is an awesome jumping-in point for me for my dodgeball game.

Not to spam, but what kind of Object is contactDetect?

It's amazing I'm making a FPS too, not a dodge ball game.  I've been following your posts for a good Physics FPS handler and it looks like this is it.  In regards to your question it looks like contact detect is your in game InputHandler usually named "input", but for some reason it's name "contactDetect".  Over the next couple days I plan to go over this code too.

I've changed this code for my purposes.



I wanted a locked-in speed for my character, so I made a Material with a Mu (which is the co-efficient of static friction) of 0 and applied it to my character. Then when I want to move him I use the setLinearVelocity(Vector3f) method to move the character around with keystrokes.



This is still in the proof-of-concept phase so the final production will be very different (based on where the camera is looking, obviously).

I noticed my code had lots of referenced to my game… Sorry about that. If anybody gets it to work in full 3D and independent then please share!

I get my new computer tonight, after which I will post my demo which shows how it works.

hmm I'm having an unusually hard time testing this can anyone assist??

Post your test class?

Looky here…http://www.jmonkeyengine.com/jmeforum/index.php?topic=10356.msg79935#msg79935  :smiley:

@Trussel,



did you find out what Class contactDetect is?



Thx,



snare

contactDetect is of type InputHandler.

I had a really great example of my dodgeball first-person shooter but my laptop's hard drive fried and I lost all of the source code on it. I'm sorry :(. I'll start another project soon and you can look at that when I get it done.

Thanks for answer!



Regards,



Snare

i Finally got it working after so many attempts oddly enough even though the scene monitor said that the physNode translation was 0,0,0 when i manually set it to 0,0,0 and called the physics update method in the main thread eight times it worked can i just ask what (direction) was in the walking sequence and i assume walkup time is the length that it takes the node to reach max acceleration and maxRun is the fastest the character can run at correct. should i post the code or it wouldn't make any sense to?



edit

I have two instances of the class out and it actually still screws up some times (ie feet geom hanging way overhead, mesh sinking into the ground(noticeably the feet Geom is on the same level of the base of the capsule), Sometimes i even get error messages) however the second instance(called after the first  :P) is always perfect and when i make only one instance run at a time it works every time is their a reasonable explanation or do i have another problem to fix?

Looks like you overwrite or mess up some references with the 2nd instance.

Are you creating multiple PhysicsSpaces or updating from different threads or something like that?.

Wheres the code? I'd really like to see it.

Yeah, that will be great if someone share it. I'd like to try it. BTW how this works with ThirdPersonController?