[SOLVED] Issues with setting linear velocity against gravity

I’m building my first game using the physics engine and I’ve encountered some odd issues with jump(...). Currently, I have inputs settings dirty states that the main update thread uses to trigger jump actions; in my game I’m wanting to jump at an angle, but using the jump function with an angled vector causes the character to trace out a triangle with their flight path instead of a parabola. Finding this odd, I tried to get around the issue by setting the linear velocity at an angle; this cause behavior that is more odd as the unit vector parallel to gravity is completely ignored unless a jump command has been issues on the given CharacterControl previously. Below I’ve created a stripped down version of what I’m doing; have I improperly configured my physics space? or are there side effects with jumping and gravity that I’ve missed while reading over the documentation?

import com.jme3.app.SimpleApplication;
import com.jme3.system.AppSettings;

public class Example extends SimpleApplication {

    public static void main(String[] args) {
        Example example = new Example();
        AppSettings appSettings = new AppSettings(false);
        appSettings.setFrameRate(60);
        example.setSettings(appSettings);
        example.start();
    }

    @Override
    public void simpleInitApp() {
        ExampleState state = new ExampleState();
        stateManager.attach(state);
    }
}

import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.collision.shapes.CollisionShape;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.util.CollisionShapeFactory;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;

public class ExampleState extends AbstractAppState {

    private static final String JUMP = "jump";
    private static final String UP = "upVector";
    private static final String ANGLE_UP = "angleVector";
    private CharacterControl control = null;
    private SimpleApplication application = null;
    private BulletAppState bulletAppState = new BulletAppState();

    @Override
    public void initialize(AppStateManager stateManager, Application app) {
        application = (SimpleApplication) app;
        application.getStateManager().attach(bulletAppState);

        Spatial character = makePlayerSpatial();
        control = makePlayerControl(character);

        Spatial floor = makeFloorSpatial();
        RigidBodyControl floorControl = makeFloorControl(floor);

        application.getRootNode().attachChild(character);
        application.getRootNode().attachChild(floor);
        bulletAppState.getPhysicsSpace().add(control);
        bulletAppState.getPhysicsSpace().add(floorControl);
        setupInputs();
    }

    private Spatial makePlayerSpatial() {
        Spatial output = new Geometry("player", new Box(1, 1, 1));
        Material material = new Material(application.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
        material.setColor("Color", ColorRGBA.DarkGray);
        output.setMaterial(material);
        return output;
    }

    private CharacterControl makePlayerControl(Spatial character) {
        CollisionShape shape = CollisionShapeFactory.createBoxShape(character);
        CharacterControl control = new CharacterControl(shape, 0.2f);
        character.addControl(control);
        control.setFallSpeed(1000);
        control.setPhysicsLocation(new Vector3f(0, 5, -50));
        // Changing the gravity's direction has a lot of odd effects.
        control.setGravity(new Vector3f(0, -50, 0));
        return control;
    }

    private Spatial makeFloorSpatial() {
        Spatial output = new Geometry("floor", new Box(50, 0.1f, 1));
        Material material = new Material(application.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
        material.setColor("Color", ColorRGBA.Green);
        output.setMaterial(material);
        return output;
    }

    private RigidBodyControl makeFloorControl(Spatial floor) {
        CollisionShape shape = CollisionShapeFactory.createBoxShape(floor);
        RigidBodyControl control = new RigidBodyControl(shape, 0);
        floor.addControl(control);
        control.setPhysicsLocation(new Vector3f(0, 0, -50));
        return control;
    }

    private void setupInputs() {
        application.getInputManager().addMapping(JUMP, new KeyTrigger(KeyInput.KEY_W));
        application.getInputManager().addMapping(UP, new KeyTrigger(KeyInput.KEY_S));
        application.getInputManager().addMapping(ANGLE_UP, new KeyTrigger(KeyInput.KEY_X));
        application.getInputManager().addListener(jumpListener, JUMP, UP, ANGLE_UP);
    }

    private ActionListener jumpListener = new ActionListener() {
        @Override
        public void onAction(String name, boolean isPressed, float tpf) {
            if (isPressed) {
                if (name.equals(JUMP)) jump = true;
                if (name.equals(UP)) up = true;
                if (name.equals(ANGLE_UP)) angleUp = true;
            }
        }
    };

    private boolean jump = false;
    private boolean up = false;
    private boolean angleUp = false;

    @Override
    public void update(float tpf) {
        if (jump) {
            control.jump(new Vector3f(0, 30f, 0));
            jump = false;
        }
        if (up) {
            control.setLinearVelocity(new Vector3f(0.1f, 15f, 0));
            up = false;
        }
        if (angleUp) {
            control.jump(new Vector3f(40f, 30f, 0));
            angleUp = false;
        }
    }
}

This is probably because CharacterControl is doing it’s “magic” for stuff like jumping and especially dampening, which means resetting all influential forces.
BetterCharacterControl does make excessive use of that, e.g., it mostly removes all forces to stop slip.

Does your code work with a simple physics RigidBodyControl?

Having a triangle instead of a parabola means the resulting curve is linear instead of quadratic. It’s so late that I fail to think about potential reasons, but really, rule out the charactercontrollers first.

A linear velocity would continue to make your character fly, at least that’s what it should, unless it is removed in the very next frame.

What you could use is BetterCharacterControl#setWalkDirection with a positive y component, but there you’d need to do impulse calculations manually and would “cheat” to some degree. But it is probably the only thing working with existing solutions.

Oh, and what about changing BetterCharacterControl to adjust the jump vector?

1 Like

Swapping to RigidBodyControl looks like it’ll work for my needs.

1 Like

I know this doesn’t help, but I have always had problems with the jump() method of Bullet’s CharacterControl. :slightly_frowning_face:

But has it’s own dozen implications, e.g. a CharacterControl ensures it’s always upright (i.e. has no pitch) and also no roll, only yaw is allowed. That’s now possible with the RigidBodyControl, etc.

Darkchaos suggested you use BetterCharacterControl

i also suggest it because its based on physics fully, while CharacterControl is not fully physics based trully.

it also have some configuration issues, but once you configure it well, it works fine.