Physics body receiving continual force after contact has ended

This may be something that isn’t related to JME but the bullet class.

I have a scene with two objects.

object 1 - the player

object 2 - a cube

When the player contacts the cube the cube will move as intended pushed by the force of the player. What happens next is strange. If I start pushing the cube with the player the continue to move my player away from the cube the cube will continue to move as long as my player is moving.

It seems that the cube is taking the initial force of the object that contacts it and continues to apply the force until the pushing object stops regardless of if the object that initially caused the force is actually is touching it.

Here is a small sample video. Notice the first two times I push the cube it stops as I stop my player. The second two times I push the cube it travels on as I move my player even though my player isn’t touching the cube.

Well my code works for similar cases:) And yours I cannot see.

RigidBodyControl tempBody;
         CollisionShape sceneShape;
         if (spatial.getControl(PhysicsControl.class).getWeight() > 0){
             sceneShape = CollisionShapeFactory.createDynamicMeshShape(spatial);
         } else {
             sceneShape = CollisionShapeFactory.createMeshShape(spatial);
         }
         
         tempBody = new RigidBodyControl(sceneShape,spatial.getControl(PhysicsControl.class).getWeight());
         
         spatial.addControl(tempBody);
         bulletAppState.getPhysicsSpace().add(tempBody);
         tempBody.setGravity(spatial.getControl(PhysicsControl.class).getGravity());

playerControl = new PhysicsCharacter(gameManager.player.control.getCollisionCapsule().x,gameManager.player.control.getCollisionCapsule().y,gameManager.player.control.getCollisionCapsule().z,gameManager);
            playerControl.warp(gameManager.player.node.getLocalTranslation());
            playerControl.setJumpForce(gameManager.player.control.getJumpForce());
            playerControl.setPhysicsDamping(gameManager.player.control.getPhysicsDampening());

PhysicsCharacter is just a betterCharacterControl with a modified jump method. The movement solution is exactly the same one you find in the hello collision example.

So if you can’t track down the problem yourself then it should be pretty easy to convert to standard classes and put together a simple test case for others to try and see the issue.

…then if your test case works for you then you’ll have somewhere to backtrack. (It certainly feels like a Vector3f is being shared somewhere that it shouldn’t be… or that somehow you are modifying one of the Vector3f ‘constants’. I’m not that familiar with bullet integration, though.)

I found the issue but I’m still not sure if this is desired behavior or a bug. I change the physics dampening of my player control and friction of objects based on slope angle. This is to keep my player moving freely across slopes. When I stop I crank up the friction and turn off the physics dampening. This keeps me from sliding down slopes.

The physics dampening in the character control is effecting the physics dampening of my other objects. I manually set physics dampening on my rigid bodies and problem solved.

getPhysicsObject(spatial.getName()).rigidBodyControl.setAngularDamping(0.8f);
getPhysicsObject(spatial.getName()).rigidBodyControl.setLinearDamping(0.8f);

Maybe they were sharing the same Vector3f instance? It’s all in code we can’t see, though, so only you can tell for sure.

No , I stepped through my character control line by line. I can’t find anywhere the dampening is changing the dampening force in my physics space. I don’t want to post a dictionary online but here you go.

package com.jpony.physics;

import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.PhysicsTickListener;
import com.jme3.bullet.collision.PhysicsRayTestResult;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.collision.shapes.CollisionShape;
import com.jme3.bullet.collision.shapes.CompoundCollisionShape;
import com.jme3.bullet.control.AbstractPhysicsControl;
import com.jme3.bullet.objects.PhysicsRigidBody;
import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.Control;
import com.jme3.util.TempVars;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import com.jpony.GameManager;
import java.io.IOException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * A modified version of Normen Hansen's character control. This version
 * fixes the jumping issue and is only compatible with the JPony plug in.
 * @author beer money
 */
public class PhysicsCharacter extends AbstractPhysicsControl implements PhysicsTickListener, JmeCloneable {

    protected static final Logger logger = Logger.getLogger(PhysicsCharacter.class.getName());
    protected PhysicsRigidBody rigidBody;
    protected float radius;
    protected float height;
    protected float mass;
    protected float duckedFactor = 0.6f;
    /**
     * Local up direction, derived from gravity.
     */
    protected final Vector3f localUp = new Vector3f(0, 1, 0);
    /**
     * Local absolute z-forward direction, derived from gravity and UNIT_Z,
     * updated continuously when gravity changes.
     */
    protected final Vector3f localForward = new Vector3f(0, 0, 1);
    /**
     * Local left direction, derived from up and forward.
     */
    protected final Vector3f localLeft = new Vector3f(1, 0, 0);
    /**
     * Local z-forward quaternion for the "local absolute" z-forward direction.
     */
    protected final Quaternion localForwardRotation = new Quaternion(Quaternion.DIRECTION_Z);
    /**
     * Is a z-forward vector based on the view direction and the current local
     * x/z plane.
     */
    protected final Vector3f viewDirection = new Vector3f(0, 0, 1);
    /**
     * Stores final spatial location, corresponds to RigidBody location.
     */
    protected final Vector3f location = new Vector3f();
    /**
     * Stores final spatial rotation, is a z-forward rotation based on the view
     * direction and the current local x/z plane. See also rotatedViewDirection.
     */
    protected final Quaternion rotation = new Quaternion(Quaternion.DIRECTION_Z);
    protected final Vector3f rotatedViewDirection = new Vector3f(0, 0, 1);
    protected final Vector3f walkDirection = new Vector3f();
    protected final Vector3f jumpForce;
    protected float physicsDamping = 0.9f;
    protected final Vector3f scale = new Vector3f(1, 1, 1);
    protected final Vector3f velocity = new Vector3f();
    protected boolean jump = false;
    protected boolean ducked = false;
    protected boolean wantToUnDuck = false;
    
    private final GameManager       gameManager;
    public boolean                  tooSteep;
    public float                    maxSlope = 30;
    public boolean                  airTime = false;
    public boolean                  initialJump = false;
    public Vector2f                 capsuleAngle = new Vector2f();
    public Vector2f                 slopeAngle = new Vector2f();
    public float                    slope;
    
    /**
     * Only used for serialization, do not use this constructor.
     */
    public PhysicsCharacter(GameManager gameManager) {
        jumpForce = new Vector3f();
        this.gameManager = gameManager;
    }

    /**
     * Creates a new character with the given properties. Note that to avoid
     * issues the final height when ducking should be larger than 2x radius. The
     * jumpForce will be set to an upwards force of 5x mass.
     *
     * @param radius
     * @param height
     * @param mass
     */
    public PhysicsCharacter(float radius, float height, float mass,GameManager gameManager) {
        this.radius = radius;
        this.height = height;
        this.mass = mass;
        this.gameManager = gameManager;
        rigidBody = new PhysicsRigidBody(getShape(), mass);
        jumpForce = new Vector3f(0, mass * 5, 0);
        rigidBody.setAngularFactor(0);
    }

    @Override
    public void update(float tpf) {
        super.update(tpf);
        rigidBody.getPhysicsLocation(location);
        //rotation has been set through viewDirection
        applyPhysicsTransform(location, rotation);
    }

    @Override
    public void render(RenderManager rm, ViewPort vp) {
        super.render(rm, vp);
    }

    /**
     * Used internally, don't call manually
     *
     * @param space
     * @param tpf
     */
    public void prePhysicsTick(PhysicsSpace space, float tpf) {
        
        if (!gameManager.physicsManager.isOnGround()){
            return;
        }
        if (wantToUnDuck && checkCanUnDuck()) {
            setHeightPercent(1);
            wantToUnDuck = false;
            ducked = false;
        }
        TempVars vars = TempVars.get();

        Vector3f currentVelocity = vars.vect2.set(velocity);
        
        // dampen existing x/z forces
        float existingLeftVelocity = velocity.dot(localLeft);
        float existingForwardVelocity = velocity.dot(localForward);
        Vector3f counter = vars.vect1;
        existingLeftVelocity = existingLeftVelocity * physicsDamping;
        existingForwardVelocity = existingForwardVelocity * physicsDamping;
        counter.set(-existingLeftVelocity, 0, -existingForwardVelocity);
        localForwardRotation.multLocal(counter);
        velocity.addLocal(counter);

        float designatedVelocity = walkDirection.length();
        
        if (designatedVelocity > 0) {
            Vector3f localWalkDirection = vars.vect1;
            //normalize walkdirection
            localWalkDirection.set(walkDirection).normalizeLocal();
            //check for the existing velocity in the desired direction
            float existingVelocity = velocity.dot(localWalkDirection);
            //calculate the final velocity in the desired direction
            float finalVelocity = designatedVelocity - existingVelocity;
            localWalkDirection.multLocal(finalVelocity);
            //add resulting vector to existing velocity
            velocity.addLocal(localWalkDirection);
        }
        if(currentVelocity.distance(velocity) > FastMath.ZERO_TOLERANCE) rigidBody.setLinearVelocity(velocity);
        if (jump) {
            gameManager.player.setJump(true);
            //TODO: precalculate jump force
            Vector3f rotatedJumpForce = vars.vect1;
            rotatedJumpForce.set(jumpForce);
            rigidBody.applyImpulse(localForwardRotation.multLocal(rotatedJumpForce), Vector3f.ZERO);
            jump = false;
            initialJump = true;
        }
        vars.release();
    }

    /**
     * Used internally, don't call manually
     *
     * @param space
     * @param tpf
     */
    public void physicsTick(PhysicsSpace space, float tpf) {
        rigidBody.getLinearVelocity(velocity);
    }
 
    protected void checkSlope() {
    
    slope = 0;
    float lastSlope = 0;
    if(walkDirection.length() > 0){
    List<PhysicsRayTestResult> results = space.rayTest(rigidBody.getPhysicsLocation(),
    rigidBody.getPhysicsLocation().add(walkDirection.normalize().mult(getFinalRadius())));

     for (PhysicsRayTestResult physicsRayTestResult : results)
     {
            float angle = physicsRayTestResult.getHitNormalLocal().normalize().angleBetween(physicsRayTestResult.getHitNormalLocal().setY(0).normalize());
            
            lastSlope = Math.abs(angle*FastMath.RAD_TO_DEG-90);
            if (lastSlope > slope){
                slope = lastSlope;
            }
            if(slope > maxSlope)
            {
            tooSteep = true;
            return;
            }
     }
      tooSteep = false;
    }
    
    }
    /**
     * Move the character somewhere. Note the character also takes the location
     * of any spatial its being attached to in the moment it is attached.
     *
     * @param vec The new character location.
     */
    public void warp(Vector3f vec) {
        setPhysicsLocation(vec);
    }

    /**
     * Makes the character jump with the set jump force.
     */
    public void jump() {
        //TODO: debounce over some frames
        if (!gameManager.physicsManager.isOnGround()) {
            return;
        }
        jump = true;
    }

    /**
     * Set the jump force as a Vector3f. The jump force is local to the
     * characters coordinate system, which normally is always z-forward (in
     * world coordinates, parent coordinates when set to applyLocalPhysics)
     *
     * @param jumpForce The new jump force
     */
    public void setJumpForce(Vector3f jumpForce) {
        this.jumpForce.set(jumpForce);
    }

    /**
     * Gets the current jump force. The default is 5 * character mass in y
     * direction.
     *
     * @return
     */
    public Vector3f getJumpForce() {
        return jumpForce;
    }

    /**
     * Toggle character ducking. When ducked the characters capsule collision
     * shape height will be multiplied by duckedFactor to make the capsule
     * smaller. When unducking, the character will check with a ray test if it
     * can in fact unduck and only do so when its possible. You can check the
     * state of the unducking by checking isDucked().
     *
     * @param enabled
     */
    public void setDucked(boolean enabled) {
        if (enabled) {
            setHeightPercent(duckedFactor);
            ducked = true;
            wantToUnDuck = false;
        } else {
            if (checkCanUnDuck()) {
                setHeightPercent(1);
                ducked = false;
            } else {
                wantToUnDuck = true;
            }
        }
    }

    /**
     * Check if the character is ducking, either due to user input or due to
     * unducking being impossible at the moment (obstacle above).
     *
     * @return
     */
    public boolean isDucked() {
        return ducked;
    }

    /**
     * Sets the height multiplication factor for ducking.
     *
     * @param factor The factor by which the height should be multiplied when
     * ducking
     */
    public void setDuckedFactor(float factor) {
        duckedFactor = factor;
    }

    /**
     * Gets the height multiplication factor for ducking.
     *
     * @return
     */
    public float getDuckedFactor() {
        return duckedFactor;
    }

    /**
     * Sets the walk direction of the character. This parameter is framerate
     * independent and the character will move continuously in the direction
     * given by the vector with the speed given by the vector length in m/s.
     *
     * @param vec The movement direction and speed in m/s
     */
    public void setWalkDirection(Vector3f vec) {
        walkDirection.set(vec);
    }

    /**
     * Gets the current walk direction and speed of the character. The length of
     * the vector defines the speed.
     *
     * @return
     */
    public Vector3f getWalkDirection() {
        return walkDirection;
    }

    /**
     * Sets the view direction for the character. Note this only defines the
     * rotation of the spatial in the local x/z plane of the character.
     *
     * @param vec
     */
    public void setViewDirection(Vector3f vec) {
        viewDirection.set(vec);
        updateLocalViewDirection();
    }

    /**
     * Gets the current view direction, note this doesn't need to correspond
     * with the spatials forward direction.
     *
     * @return
     */
    public Vector3f getViewDirection() {
        return viewDirection;
    }

    /**
     * Realign the local forward vector to given direction vector, if null is
     * supplied Vector3f.UNIT_Z is used. Input vector has to be perpendicular to
     * current gravity vector. This normally only needs to be called when the
     * gravity direction changed continuously and the local forward vector is
     * off due to drift. E.g. after walking around on a sphere "planet" for a
     * while and then going back to a y-up coordinate system the local z-forward
     * might not be 100% alinged with Z axis.
     *
     * @param vec The new forward vector, has to be perpendicular to the current
     * gravity vector!
     */
    public void resetForward(Vector3f vec) {
        if (vec == null) {
            vec = Vector3f.UNIT_Z;
        }
        localForward.set(vec);
        updateLocalCoordinateSystem();
    }

    /**
     * Get the current linear velocity along the three axes of the character.
     * This is prepresented in world coordinates, parent coordinates when the
     * control is set to applyLocalPhysics.
     *
     * @return The current linear velocity of the character
     */
    public Vector3f getVelocity() {
        return velocity;
    }

    /**
     * Set the gravity for this character. Note that this also realigns the
     * local coordinate system of the character so that continuous changes in
     * gravity direction are possible while maintaining a sensible control over
     * the character.
     *
     * @param gravity
     */
    public void setGravity(Vector3f gravity) {
        rigidBody.setGravity(gravity);
        localUp.set(gravity).normalizeLocal().negateLocal();
        updateLocalCoordinateSystem();
    }

    /**
     * Get the current gravity of the character.
     *
     * @return
     */
    public Vector3f getGravity() {
        return rigidBody.getGravity();
    }

    /**
     * Get the current gravity of the character.
     *
     * @param store The vector to store the result in
     * @return
     */
    public Vector3f getGravity(Vector3f store) {
        return rigidBody.getGravity(store);
    }

    /**
     * Sets how much the physics forces in the local x/z plane should be
     * dampened.
     * @param physicsDamping The dampening value, 0 = no dampening, 1 = no external force, default = 0.9
     */
    public void setPhysicsDamping(float physicsDamping) {
        this.physicsDamping = physicsDamping;
    }

    /**
     * Gets how much the physics forces in the local x/z plane should be
     * dampened.
     */
    public float getPhysicsDamping() {
        return physicsDamping;
    }

    /**
     * This actually sets a new collision shape to the character to change the
     * height of the capsule.
     *
     * @param percent
     */
    protected void setHeightPercent(float percent) {
        scale.setY(percent);
        rigidBody.setCollisionShape(getShape());
    }

    /**
     * This checks if the character can go from ducked to unducked state by
     * doing a ray test.
     */
    protected boolean checkCanUnDuck() {
        TempVars vars = TempVars.get();
        Vector3f location = vars.vect1;
        Vector3f rayVector = vars.vect2;
        location.set(localUp).multLocal(FastMath.ZERO_TOLERANCE).addLocal(this.location);
        rayVector.set(localUp).multLocal(height + FastMath.ZERO_TOLERANCE).addLocal(location);
        List<PhysicsRayTestResult> results = space.rayTest(location, rayVector);
        vars.release();
        for (PhysicsRayTestResult physicsRayTestResult : results) {
            if (!physicsRayTestResult.getCollisionObject().equals(rigidBody)) {
                return false;
            }
        }
        return true;
    }

    /**
     * Gets a new collision shape based on the current scale parameter. The
     * created collisionshape is a capsule collision shape that is attached to a
     * compound collision shape with an offset to set the object center at the
     * bottom of the capsule.
     *
     * @return
     */
    protected CollisionShape getShape() {
        //TODO: cleanup size mess..
        CapsuleCollisionShape capsuleCollisionShape = new CapsuleCollisionShape(getFinalRadius(), (getFinalHeight() - (2 * getFinalRadius())));
        CompoundCollisionShape compoundCollisionShape = new CompoundCollisionShape();
        Vector3f addLocation = new Vector3f(0, (getFinalHeight() / 2.0f), 0);
        compoundCollisionShape.addChildShape(capsuleCollisionShape, addLocation);
        capsuleAngle.set(capsuleCollisionShape.getRadius()/2,capsuleCollisionShape.getHeight()/2);
        if (gameManager.physicsManager != null){
            gameManager.physicsManager.setGroundTolerance();
        }
        return compoundCollisionShape;
    }

    /**
     * Gets the scaled height.
     *
     * @return
     */
    protected float getFinalHeight() {
        return height * scale.getY();
    }

    /**
     * Gets the scaled radius.
     *
     * @return
     */
    protected float getFinalRadius() {
        return radius * scale.getZ();
    }

    /**
     * Updates the local coordinate system from the localForward and localUp
     * vectors, adapts localForward, sets localForwardRotation quaternion to
     * local z-forward rotation.
     */
    protected void updateLocalCoordinateSystem() {
        //gravity vector has possibly changed, calculate new world forward (UNIT_Z)
        calculateNewForward(localForwardRotation, localForward, localUp);
        localLeft.set(localUp).crossLocal(localForward);
        rigidBody.setPhysicsRotation(localForwardRotation);
        updateLocalViewDirection();
    }

    /**
     * Updates the local x/z-flattened view direction and the corresponding
     * rotation quaternion for the spatial.
     */
    protected void updateLocalViewDirection() {
        //update local rotation quaternion to use for view rotation
        localForwardRotation.multLocal(rotatedViewDirection.set(viewDirection));
        calculateNewForward(rotation, rotatedViewDirection, localUp);
    }

    /**
     * This method works similar to Camera.lookAt but where lookAt sets the
     * priority on the direction, this method sets the priority on the up vector
     * so that the result direction vector and rotation is guaranteed to be
     * perpendicular to the up vector.
     *
     * @param rotation The rotation to set the result on or null to create a new
     * Quaternion, this will be set to the new "z-forward" rotation if not null
     * @param direction The direction to base the new look direction on, will be
     * set to the new direction
     * @param worldUpVector The up vector to use, the result direction will be
     * perpendicular to this
     * @return
     */
    protected final void calculateNewForward(Quaternion rotation, Vector3f direction, Vector3f worldUpVector) {
        if (direction == null) {
            return;
        }
        TempVars vars = TempVars.get();
        Vector3f newLeft = vars.vect1;
        Vector3f newLeftNegate = vars.vect2;

        newLeft.set(worldUpVector).crossLocal(direction).normalizeLocal();
        if (newLeft.equals(Vector3f.ZERO)) {
            if (direction.x != 0) {
                newLeft.set(direction.y, -direction.x, 0f).normalizeLocal();
            } else {
                newLeft.set(0f, direction.z, -direction.y).normalizeLocal();
            }
            logger.log(Level.INFO, "Zero left for direction {0}, up {1}", new Object[]{direction, worldUpVector});
        }
        newLeftNegate.set(newLeft).negateLocal();
        direction.set(worldUpVector).crossLocal(newLeftNegate).normalizeLocal();
        if (direction.equals(Vector3f.ZERO)) {
            direction.set(Vector3f.UNIT_Z);
            logger.log(Level.INFO, "Zero left for left {0}, up {1}", new Object[]{newLeft, worldUpVector});
        }
        if (rotation != null) {
            rotation.fromAxes(newLeft, worldUpVector, direction);
        }
        vars.release();
    }

    /**
     * This is implemented from AbstractPhysicsControl and called when the
     * spatial is attached for example.
     *
     * @param vec
     */
    @Override
    protected void setPhysicsLocation(Vector3f vec) {
        rigidBody.setPhysicsLocation(vec);
        location.set(vec);
    }

    /**
     * This is implemented from AbstractPhysicsControl and called when the
     * spatial is attached for example. We don't set the actual physics rotation
     * but the view rotation here. It might actually be altered by the
     * calculateNewForward method.
     *
     * @param quat
     */
    @Override
    protected void setPhysicsRotation(Quaternion quat) {
        rotation.set(quat);
        rotation.multLocal(rotatedViewDirection.set(viewDirection));
        updateLocalViewDirection();
    }

    /**
     * This is implemented from AbstractPhysicsControl and called when the
     * control is supposed to add all objects to the physics space.
     *
     * @param space
     */
    @Override
    protected void addPhysics(PhysicsSpace space) {
        space.getGravity(localUp).normalizeLocal().negateLocal();
        updateLocalCoordinateSystem();

        space.addCollisionObject(rigidBody);
        space.addTickListener(this);
    }

    /**
     * This is implemented from AbstractPhysicsControl and called when the
     * control is supposed to remove all objects from the physics space.
     *
     * @param space
     */
    @Override
    protected void removePhysics(PhysicsSpace space) {
        space.removeCollisionObject(rigidBody);
        space.removeTickListener(this);
    }

    @Override
    protected void createSpatialData(Spatial spat) {
        rigidBody.setUserObject(spatial);
    }

    @Override
    protected void removeSpatialData(Spatial spat) {
        rigidBody.setUserObject(null);
    }

    @Override
    public Control cloneForSpatial(Spatial spatial) {
        PhysicsCharacter control = new PhysicsCharacter(radius, height, mass,gameManager);
        control.setJumpForce(jumpForce);
        return control;
    }

    @Override
    public Object jmeClone() {
        PhysicsCharacter control = new PhysicsCharacter(radius, height, mass,gameManager);
        control.setJumpForce(jumpForce);
        control.spatial = this.spatial;
        return control;
    }     

    @Override
    public void write(JmeExporter ex) throws IOException {
        super.write(ex);
        OutputCapsule oc = ex.getCapsule(this);
        oc.write(radius, "radius", 1);
        oc.write(height, "height", 1);
        oc.write(mass, "mass", 1);
        oc.write(jumpForce, "jumpForce", new Vector3f(0, mass * 5, 0));
        oc.write(physicsDamping, "physicsDamping", 0.9f);
    }

    @Override
    public void read(JmeImporter im) throws IOException {
        super.read(im);
        InputCapsule in = im.getCapsule(this);
        this.radius = in.readFloat("radius", 1);
        this.height = in.readFloat("height", 2);
        this.mass = in.readFloat("mass", 80);
        this.jumpForce.set((Vector3f) in.readSavable("jumpForce", new Vector3f(0, mass * 5, 0)));
        this.physicsDamping = in.readFloat("physicsDamping", 0.9f);
        rigidBody = new PhysicsRigidBody(getShape(), mass);
        jumpForce.set(new Vector3f(0, mass * 5, 0));
        rigidBody.setAngularFactor(0);
    }
}

and movement

private void thirdPersonMovement(float tpf){
       
    if (!allowMovement){
        walkDirection.set(0, 0, 0);
        gameManager.physicsManager.playerControl.setWalkDirection(walkDirection);
        node.setLocalTranslation(getCollisionNode().getLocalTranslation());
        return;
    }

    if (gameManager.cameraManager.control.isTrail() && !idle){
        gameManager.cameraManager.thirdPerson.trail(tpf);
    }
    
        playerDirection.set(getDirection()).multLocal(control.getSpeed(), 0.0f, control.getSpeed());
        playerLeft.set(getLeft()).multLocal(control.getStrafeSpeed());
        camLeft.set(gameManager.app.getCamera().getLeft()).multLocal(control.getStrafeSpeed());

        walkDirection.set(0, 0, 0);
        if (gameManager.inputManager.key(Keys.TURN_LEFT).triggered) {
            if (!control.isDisableTurn()){
              gameManager.physicsManager.yaw(node, control.getTurnSpeed() * tpf);
            }
        }
        if (gameManager.inputManager.key(Keys.TURN_RIGHT).triggered) {
            if (!control.isDisableTurn()){
              gameManager.physicsManager.yaw(node, -control.getTurnSpeed() * tpf);
            }
        }
        if (gameManager.inputManager.key(Keys.LEFT).triggered) {
            if (!control.isDisableStrafe()){
              walkDirection.addLocal(playerLeft);
            }
        }
        if (gameManager.inputManager.key(Keys.RIGHT).triggered) {
            if (!control.isDisableStrafe()){
              walkDirection.addLocal(playerLeft.negate());
            }
        }
        if (gameManager.inputManager.key(Keys.FORWARD).triggered) {       
            walkDirection.addLocal(playerDirection);
        }
        if (gameManager.inputManager.key(Keys.BACKWARD).triggered) {
            walkDirection.addLocal(playerDirection.negate());
        }
        if (gameManager.inputManager.key(Keys.JUMP).pressed) {
            gameManager.physicsManager.playerControl.jump();
        }
        gameManager.physicsManager.playerControl.setWalkDirection(walkDirection);
        node.setLocalTranslation(getCollisionNode().getLocalTranslation());
        
}

node = player node.

Setting the friction and dampening

       if (gameManager.inputManager.isIdle()){
            setFriction(spatial,spatial.getControl(PhysicsControl.class).getTraction());  
            playerControl.setPhysicsDamping(0);     
       } else {
            setFriction(spatial,spatial.getControl(PhysicsControl.class).getSurfaceResistance());  
            playerControl.setPhysicsDamping(gameManager.player.control.getPhysicsDampening()); 
       } 

That should be all of the relevant code.

Okay I found the problem. When I my character is moving each surface has a different level of friction like ice and dirt have low friction while mud would have higher friction aka resistance to walking force.

I have some low to no resistance surfaces I was pushing the cubes across. While moving my player the surface has little friction. When my player idles I turn up the friction to keep my player from sliding. The friction was causing the funny behavior. Turning up the physics dampening on my dynamic bodies was toning down the friction.

It really comes down to a balance of how realistic I want the walking surface resistance and traction to be versus how realistic I want the dynamic bodies to move. I struck a good balance at the moment.

P.S. This is what I get for trying to get all fancy with the physics :smile:

Well, I don’t know bullet well enough but it does seem odd to change the world state just to affect your player. Is there not some way of affecting friction just on the player instead of what he’s walking on?

Yes , but it still calculates in the other bodies friction. Also setting the objects friction directly gives me the benefit of the automatic collision detection done by bullet. If I set a terrain patch friction to ice the bullet control will know when the player is touching said patch the adjust accordingly.

Then you have the fact that when pushing bodies along each other the friction is calculated in. Hence why I turn down friction when I’m moving so if I push into an object it will slide smoothly if I don’t do this the object will catch on the surface. If I want the object to stick I can up it’s gravity and weight.

Doing it this way gives me a great control of detail of how my physics bodies react to each other. What I’m doing is breaking down the physics piece by piece so I can control every little aspect of an entity.

Here is a great example using friction and gravity set by my physics manager. I push the cube into the river and down stream it goes. Not perfect yet but it works.

Ok, well, just remember the next time you have a strange physics problem that it’s probably because of stuff like this and another edge case you’ve found.

So… as the rest of us are playing 20 questions with you to get to the heart of the code that’s actually at issue, remember to be as patient as we are when playing the game.