Search code examples
javalwjgl

Getting movement speed to continue being useful after stopping


Coming up with a title for this question was really really difficult. So I'm making a game, and I have a method that is used for handling movement called handleVelocity(). The way it works, is that in my game, it updates every tick, looking for character movement. If character movement is below 1 or -1, then it stops the character from moving at all. So I would set the character's movement to 0. But then, the character can no longer move, and I don't know how to get him to continue moving. How do I handle this velocity so that it can continue to move, even after it has previously stopped. I'll post both classes below.

Player class:

import java.io.IOException;

import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureLoader;
import org.newdawn.slick.util.ResourceLoader;

public class Player {

    public Texture playerTexture;

    // Positions & speed
    public float xPos = 20.0f; // This is initial
    public float yPos = 0.0f; // Same as above.
    public float acceleration = 15;

    public static int gravityForce = 6;
    public static int jumpVelocity = 100;
    private float moveSpeed = 4f;
    private static int MAX_MOVE_SPEED = 9;

    public boolean isSupported = true; // Once again, initial value.
    public boolean goingRight, goingLeft, canJump;

    // movement methods & constants

    public void update() {
        handleVelocity();
        applyGravity();
        checkForSupport();
    }

    public void handleVelocity() {
        float minMoveSpeed = 1;
        if (this.moveSpeed < minMoveSpeed && this.moveSpeed > -minMoveSpeed) {
            this.moveSpeed = 0;
        } else {
            float dampening = 0.00002f;
            double sign = -(int) Math.signum(moveSpeed);
            this.moveSpeed += (float) (dampening * sign);
        }
        xPos += this.moveSpeed;
    }

    public Texture grabTexture() {
        try {
            playerTexture = TextureLoader.getTexture("PNG",
                    ResourceLoader.getResourceAsStream("res/test_char.png"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return playerTexture;
    }

    private void checkForSupport() {
        if (yPos == 0) {
            isSupported = true;
        } else if (yPos > 0 /* and is not on a platform */) {
            isSupported = false;
        }
    }

    private void applyGravity() {
        if (!isSupported) {
            yPos -= gravityForce;
        }
    }

    public void printPos(String moveMethod) {
        System.out.println(moveMethod + " X: " + xPos + " Y: " + yPos
                + " Going Right: " + goingRight + " Going Left: " + goingLeft
                + " Speed: " + moveSpeed);
    }

    // movement methods

    private void accelerateX(float speed) {
        moveSpeed += (float) (speed * 0.0096);
        if (moveSpeed >= MAX_MOVE_SPEED) {
            moveSpeed = MAX_MOVE_SPEED;
        } else if (moveSpeed <= -MAX_MOVE_SPEED) {
            moveSpeed = -MAX_MOVE_SPEED;
        }
    }

    @SuppressWarnings("unused")
    private void accelerateY() {

    }

    public void moveRight() {
        printPos("Moving Right!");
        accelerateX(acceleration);
    }

    public void moveLeft() {
        printPos("Moving Left!");
        accelerateX(-acceleration);
    }

    public void jump() {
        printPos("Jumping!");
    }

}

Main class:

import com.hasherr.platformer.entity.Player;

import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import static org.lwjgl.opengl.GL11.*;

public class Main {

    private void display() {
        try {
            Display.setDisplayMode(new DisplayMode(1000, 550));
            Display.setTitle("Unnamed Platformer Game");
            Display.create();
        } catch (LWJGLException e) {
            e.printStackTrace();
            System.exit(0);
        }

        // OpenGL

        while (!Display.isCloseRequested()) {
            Display.update();
            Display.sync(60); // sync to 60 fps
            initGL();

            player.update();
            handleKeyboardInput();
        }

        Display.destroy();
    }

    private boolean keyboardInUse() {
        boolean keyboardInUse;
        if (!(Keyboard.isKeyDown(Keyboard.KEY_A)
                || Keyboard.isKeyDown(Keyboard.KEY_D) || Keyboard
                    .isKeyDown(Keyboard.KEY_SPACE))) {
            keyboardInUse = false;
        } else {
            keyboardInUse = true;
        }

        return keyboardInUse;
    }

    private void handleKeyboardInput() {
        if (!keyboardInUse()) {
            player.goingLeft = false;
            player.goingRight = false;
        }

        if (Keyboard.isKeyDown(Keyboard.KEY_D)) {   
            player.goingLeft = false;
            player.goingRight = true;
            player.moveRight();
        } else if (Keyboard.isKeyDown(Keyboard.KEY_A)) {
            player.goingLeft = true;
            player.goingRight = false;
            player.moveLeft();
        } else if (Keyboard.isKeyDown(Keyboard.KEY_SPACE)) {
            player.jump();
        }

    }

    private void initGL() {
        // initial OpenGL items for 2D rendering
        glClear(GL_COLOR_BUFFER_BIT);
        glEnable(GL_TEXTURE_2D);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        glOrtho(0, 1000, 0, 550, 1, -1);

        // start rendering player image
        player.grabTexture().bind();
        glBegin(GL_QUADS);

        glTexCoord2f(0, 0);
        glVertex2f(player.xPos, player.yPos);

        glTexCoord2f(1, 0);
        glVertex2f(player.xPos + 150, player.yPos);

        glTexCoord2f(1, 1);
        glVertex2f(player.xPos + 150, player.yPos + 150);

        glTexCoord2f(0, 1);
        glVertex2f(player.xPos, player.yPos + 150);
        glEnd(); // stop rendering this image
    }

    Player player = new Player();

    public static void main(String[] args) {
        Main main = new Main();
        main.display();
    }
}

Solution

  • I know you've already accepted an answer, and the answer provided does do the job, however I feel this needs a better solution and more full explanation.

    The code doesn't work as is because in your moveLeft and moveRight methods you are calling accelerateX(acceleration). acceleration value is 15. Lets look at accelerateX method.

    private void accelerateX(float speed) {
        moveSpeed += (float) (speed * 0.0096);
        if (moveSpeed >= MAX_MOVE_SPEED) {
            moveSpeed = MAX_MOVE_SPEED;
        } else if (moveSpeed <= -MAX_MOVE_SPEED) {
            moveSpeed = -MAX_MOVE_SPEED;
        }
    }
    

    Its multiplying the supplied speed by .0096. 15*.0096 = .144. So here we're adding .144 to moveSpeed. Now let's go to handleVelocity

    public void handleVelocity() {
        float minMoveSpeed = 1;
        if (this.moveSpeed < minMoveSpeed && this.moveSpeed > -minMoveSpeed) {
            this.moveSpeed = 0;
        } else {
            float dampening = 0.00002f;
            double sign = -(int) Math.signum(moveSpeed);
            this.moveSpeed += (float) (dampening * sign);
        }
        xPos += this.moveSpeed;
    }
    

    This code sets moveSpeed to zero if its less than 1, and greater than -1 to force the player to stop moving.

    This can be fixed a number of ways and maintain our nice pseudo-physics. The answer you've accepted breaks the physics system by forcing the movement speed to some value. Maybe this is what you want in a platformer, maybe not. Your choice.

    I suggest you decrease minMoveSpeed to something smaller, like .01.