CharacterController implementation (JmePhysic)

I post here a controller which allow to move a physics component with only gravity (no torque/force) and catch collisions with other StaticPhysicsNode.

Good idea to move character in physics environnement!

Jme-jbullet do that, but actually i didn't see that on jmephysics!


import com.jme.input.InputHandler;
import com.jme.input.action.InputAction;
import com.jme.input.action.InputActionEvent;
import com.jme.math.Vector3f;
import com.jme.scene.Controller;
import com.jmex.physics.DynamicPhysicsNode;
import com.jmex.physics.PhysicsNode;
import com.jmex.physics.StaticPhysicsNode;
import com.jmex.physics.contact.ContactInfo;
import com.jmex.physics.material.Material;

/**
 * Control a ghost physic node to be moved without physic interaction,
 * like no physic, but catch collision with other StaticPhysicNode
 *
 * @author anykeyh@gmail.com
 */
public class CharacterController extends Controller {
   private static final long serialVersionUID = 3152691286715564793L;
   
   private int contactPointCount       = 0;
   private DynamicPhysicsNode nodeToControl;
   private boolean onGround          = false;
   private Vector3f physicDecal       = new Vector3f();
   private InputHandler inputHandler    = new InputHandler();
   
   private Vector3f tmpVec          = new Vector3f();
   
   private class CollisionHandler extends InputAction {
      @Override
      public void performAction(InputActionEvent evt) {
         ContactInfo info = (ContactInfo )evt.getTriggerData();
         
         DynamicPhysicsNode self;
         PhysicsNode other;
         self = nodeToControl;
         
         if(info.getNode1() == self) {
            other = info.getNode2();
         } else {
            other = info.getNode1();
         }
      
         if(other instanceof StaticPhysicsNode) {
            contactPointCount++;
            
            info.getContactNormal(tmpVec);
            
            if(other != info.getNode2()) {
               tmpVec.negateLocal();
            }
            
            if(tmpVec.y > groundTriggerValue) {
               onGround = true;
               self.clearDynamics(); //Clear gravity
            }
            
            tmpVec.multLocal(info.getPenetrationDepth());
            
            physicDecal.addLocal(tmpVec);
         }
      }
   }
   
   private float groundTriggerValue = 0.3f;

   private void resetGathering() {
      onGround = false;
      contactPointCount = 0;
      physicDecal.set(0,0,0);
   }
   
   private void applyTranslation() {
      if(contactPointCount > 0) {
         physicDecal.divideLocal(contactPointCount);
         physicDecal.addLocal(nodeToControl.getLocalTranslation());
         nodeToControl.getLocalTranslation().set(
               physicDecal
         );
      }
   }
   
   public DynamicPhysicsNode getNodeToControl() {
      return nodeToControl;
   }

   public void setNodeToControl(DynamicPhysicsNode nodeToControl) {
      if(nodeToControl!=this.nodeToControl) {
         if(this.nodeToControl!=null) {
            inputHandler.clearActions();
         }
         
         this.nodeToControl = nodeToControl;
         nodeToControl.setMaterial(Material.GHOST);
         inputHandler.addAction(new CollisionHandler(), nodeToControl.getCollisionEventHandler() , false);
      }
   }

   public boolean isOnGround() {
      return onGround;
   }
   
   public float getGroundTriggerValue() {
      return groundTriggerValue;
   }

   /**
    * Set the value of the up vector to see if player is on ground
    * @param groundTriggerValue
    */
   public void setGroundTriggerValue(float groundTriggerValue) {
      this.groundTriggerValue = groundTriggerValue;
   }

   @Override
   public void update(float time) {
      if(nodeToControl!=null) {
         resetGathering();
         inputHandler.update(time);
         applyTranslation();
      }
   }

   public CharacterController(DynamicPhysicsNode node) {
      setNodeToControl(node);
   }
   
}



You just have to add this controller on a dynamic physic node:

physicNode.addController(new CharacterController(physicNode));



It seems to work properly. Thanks to return here any issues you found ;)

And enjoy!