Hot questions for Using Lightweight Java Game Library in input

Question:

I've created a class that takes lwjgl's keyboard input and turns it into a list of strings that are all the keys that are currently being pressed.

public class KeyHandler {

    ArrayList<String> keysPressed;

    public KeyHandler() {
        keysPressed = new ArrayList<String>();
    }

    public void checkKeys() {
        while (Keyboard.next()) {
            String keystring = Keyboard.getKeyName(Keyboard.getEventKey());
            if (!keysPressed.contains(keystring)) {
                keysPressed.add(keystring); // key has been pressed
            } else {
                keysPressed.remove(keystring); // key has been released
            }
        }
    }

    public void runKeys() {
    if (keysPressed.size() > 0) {
        for (String str : keysPressed) {
            System.out.println("Key handler got key:" + str);
            // run class for key
        }
    } else {
        // no keys have been pressed
    }
}

}

I'm trying to figure out a way so that 'runKeys' will run a class with that name, for example.

W.java

public class W {

    public static void exc() {
        player.moveZ(10);
    }

}

The reason for doing this is to avoid having to run through 50+ if statements to check for input


Answer:

  • Create a Map<Character, Consumer<Character>>.
  • Create a class for each character which implements Consumer<Character> interface.
  • Store objects of those classes in the map.
  • Call consume on the appropriate consumer object from the map using your detected keystroke character.

Hope this helps.

Question:

I try to implement a simple CTRL + V for some textfields inside my game. So I set a flag for CTRL in the KeyCallback whenever ctrl is pressed or released (i tested this one). When I try to type "V" now the CharCallback isn't called. Is it supposed to not work this way? If it is how do i do it right?

That's how I poll it inside the KeyCallback:

if(key == GLFW_KEY_LEFT_CONTROL && action == GLFW_PRESS) {
    controlPressed = true;
}
if(key == GLFW_KEY_LEFT_CONTROL && action == GLFW_RELEASE) {
    controlPressed = false;
}

I use glfwSetKeyCallback(...) to set the callback.


Answer:

This is intended behavior. The documentation for glfwSetCharCallback says (emphasis mine):

The character callback behaves as system text input normally does and will not be called if modifier keys are held down that would prevent normal text input on that platform, for example a Super (Command) key on OS X or Alt key on Windows. There is a character with modifiers callback that receives these events.

However, it also states that you can use a char mods callback, which will be called even when a modifier key is pressed.

This function sets the character with modifiers callback of the specified window, which is called when a Unicode character is input regardless of what modifier keys are used.

For example:

GLFWCharModsCallback charModsCallback = new GLFWCharModsCallback() {
    @Override
    public void invoke(long window, int codepoint, int mods) {
        // do something with the character and modifier flags
    }
};
glfwSetCharModsCallback(window, charModsCallback);

Question:

I use LWJGL for 3d graphics.

The thing i want to do is to change render type from "GL_TRIANGLES" to "GL_LINES" by pressing a specified key (For example - F1). Because of a high framerate normal

if(Keyboard.isKeyDown(key))
        {
            //Action
        }

would switch on and off up to 200 times during normal button press. Overall, its interesting to me, how this problem is usually solved, and whats the best way to deal with it.

So the thing i came up with is (Probably, very ineffective):

//Render class init
Key F1 = new Key(Keyboard.KEY_F1);

//Render method
renderMode = F1.ifPressed(delta);
if(renderMode)
{
    //GL_LINES render
}
else
{
    //GL_TRIANGLES render
}

//Key class
public class Key 
{
    int DELAY_TIME = 20;
    int delay = 0;
    int key;
    boolean isActive = false;
    boolean  state = false;

public Key(int key)
{
    this.key = key;
}

public boolean ifPressed(int delta)
{
    if(!isActive)
    {
        if(Keyboard.isKeyDown(key))
        {
            isActive = true;
            state = !state;
            return state;
        }
    }
    else if(delay <= DELAY_TIME * delta)
    {
        delay++;
    }
    else if(delay > DELAY_TIME)
    {
        delay = 0;
        isActive = false;
    }
    return state;

Answer:

One general solution is to have a list of currently active actions / pressed keys. When an input event PRESSED is fired (this is typically done by the framework / OS) for some key, you check if that key is in the list.

if (list.contains(key)) {
    // not first time, so fire held
    onKeyHeld(key);
}
else {
    list.add(key);
    onKeyPressed(key);
}

When an input event RELEASED is fired, you do the following:

list.remove(key);
onKeyReleased(key);

Mouse events are done in a similar way.

Better still you can replace those methods with some UserAction class, so that it can be bound to keys / mouse events. The code would change to something like:

bind(Key.SPACE, new UserAction("Jump") {
    public void onActionStart() {} // on key pressed
    public void onAction() {}      // on key held
    public void onActionEnd() {}   // on key released
});

I have used this solution in a game library and therefore can confirm that it works.

Question:

In my while loop for my OpenGL program i have some something like this:

     if (Keyboard.isKeyDown(Keyboard.KEY_W)) {
     ...
     }

Now of course this if is getting checked multiple times per button press. Is there a way to let it detect the button only once per press? For example like the KeyListener in Java Swing does.

I already handle movement speed with an elapsedTimevariable, maybe I can use that for that problem too?


Answer:

Keep track of when it comes down for the first time and when it goes back up, ideally this boolean is not defined in the local scope of the method that is called 60 times a second, but at a higher scope.

if (wKeyUp && Keyboard.isKeyDown(Keyboard.KEY_W)) {
    wKeyUp = false;
    onNewWKeyPress();
}
else if (wKeyUp == false)
{
    wKeyUp = true;
}

Question:

I'm trying to make a game in java and I'm using LWJGL. So I'm able to move a quad around the screen using arrow keys and it works very nicely. Although the code for that is in the Main constructor, which works but it doesn't look very nice. So I decided to put it in a method and call the method within the Main constructor to save some space in the constructor. However when I put the code in a method, and call it, the function doesn't work. The cube won't move when I press the arrow keys.

This is my move method(which doesn't do anything when called in the main constructor):

public static void moveSquare(float x, float y, float speed, int deltaTime){
    if(Keyboard.isKeyDown(Keyboard.KEY_RIGHT)){
        x += speed * deltaTime;
    } 
    if(Keyboard.isKeyDown(Keyboard.KEY_LEFT)){
        x -= speed * deltaTime;
    }
    if(Keyboard.isKeyDown(Keyboard.KEY_UP)){
        y -= speed * deltaTime;
    }
    if(Keyboard.isKeyDown(Keyboard.KEY_DOWN)){
        y += speed * deltaTime;
    } 

Here is my Main constructor where the game loops is. Some instances that i have are float x, float y, float speed, int width, and int height. just in case you see them referenced in the code and wonder where they came from.

public Main(){
    Display.setTitle("Squares!");
    try {
        Display.setDisplayMode(new DisplayMode(800,600));
        Display.create();
    } catch (LWJGLException e) {
        e.printStackTrace();
    }

    initGL();

    while(!Display.isCloseRequested()){

        int deltaTime = getDelta();

        drawSquare(x, y, width, height);
        moveSquare(x, y, speed, deltaTime);

        initClock();
        Display.update();
        GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
        Display.sync(64); 
    } 

    Display.destroy();
}

Answer:

A static method belongs to class, not to the objects. If you would like to access a non static field within your static method like x, y, speed... it would give you an error. But what you are actually doing here is passing a couple parameters to your move method which is doing a couple thing with them doesn't really matter what because that will only modify those copies within your method.

Let's say you have a Square class and you want to make it move with a move method that should not be static

public void move(float dt){
    //move stuff here
}

You don't even have to pass arguments only delta time because every square object will have its own copy(if not static) and move will use those.

Question:

How would I make a Box2D body rotate such that it faces the a point given when the user clicks their mouse?

I am trying to implement a mechanic you can visualize as a top-down flashlight.

Problems:

  • I feel like I am scaling the camera with PPM (pixels per meter) incorrectly
  • The light does not turn correctly
  • Do not know how to tween between positions

To do this, in the show() method, I have attached a ConeLight to a Box2D body using the following method:

    PolygonShape shape = new PolygonShape();
    shape.setAsBox(5 / PPM, 5 / PPM);

    BodyDef bdef = new BodyDef();
    bdef.position.set(160 / PPM, 200 / PPM);
    bdef.type = BodyType.DynamicBody;
    body = world.createBody(bdef);

    FixtureDef fdef = new FixtureDef();
    fdef.shape = shape;
    body.createFixture(fdef);

    rayHandler = new RayHandler(world);
    cone = new ConeLight
    (rayHandler, 40, Color.WHITE, 30, 160 / PPM, 200 / PPM, -90, 40);

Then, again in the show() method, I set up the camera:

    b2dcam = new OrthographicCamera();
    b2dcam.setToOrtho(false, Gdx.graphics.getWidth() / PPM, Gdx.graphics.getHeight() / PPM);

I am rendering it like this in the render() method:

    Gdx.gl20.glClear(GL20.GL_COLOR_BUFFER_BIT);
    world.step(1f/60f, 6, 2);
    b2dr.render(world, b2dcam.combined);
    rayHandler.setCombinedMatrix(b2dcam.combined);
    rayHandler.updateAndRender();

I am handling the input in this fashion:

public boolean touchDown(int screenX, int screenY, int pointer, int button) {

    if(button == Buttons.LEFT){
        body.setTransform(body.getPosition(), (float) (Math.atan2( (body.getPosition().y - screenY),
                                                       (screenX - body.getPosition().x) ) ));
    }
    return false;
}

The light rotates on mouse click, which means the listener is working, but it does not rotate to the correct point. I assume it has something to do with my math being incorrect, and the scaling done from meters to pixels being wrong.

Can someone help me on both of these issues? The intended behavior is illustrated below:

When the mouse is clicked, the body, and by association the ConeLight, should move to face the direction of the mouse click.

My full code can be viewed here: https://gist.githubusercontent.com/Elsealabs/1afaa812aafb56ecd3c2/raw/5d0959df795516c89fb7e6ab81aecc01dc8cd441/gistfile1.txt


Answer:

I never got an answer to this, but I figured it out with some help from some friends.

To handle this problem, I created a class that does a lot of the math automatically.

public class Rot2D {
  public static Rot2D fromDegrees(double angle) {
     return fromRadians(Math.toRadians(angle));
  }

  public static Rot2D fromRadians(double angle) {
     return new Rot2D(Math.cos(angle), Math.sin(angle));
  }

  public static Rot2D fromVector(double dx, double dy) {
     float length = (float) Math.sqrt(dx * dx + dy * dy);
     return new Rot2D(dx / length, dy / length);
  }

  public double cos, sin;

  private Rot2D(double cos, double sin) {
     this.cos = cos;
     this.sin = sin;
  }

  public Rot2D load(Rot2D that) {
     this.cos = that.cos;
     this.sin = that.sin;

     return this;
  }

  public Rot2D copy() {
     return new Rot2D(cos, sin);
  }

  public Rot2D rotate(Rot2D that) {
     double cos = (this.cos * that.cos) - (this.sin * that.sin);
     double sin = (this.cos * that.sin) + (this.sin * that.cos);

     this.cos = cos;
     this.sin = sin;

     return this;
  }

  public static double cross(Rot2D a, Rot2D b) {
     return (a.cos * b.sin) - (a.sin * b.cos);
  }
}

And in my game code I used the following code to implement the movement:

    if (Gdx.input.isTouched())
    {
        Vector3 tmp = camera.unproject(new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0));
        touch_target = new Vector2(tmp.x, tmp.y);
    }

    touch_angleWant = Rot2D.fromVector(
        touch_target.x - entity_player.getBody().getPosition().x,
        touch_target.y - entity_player.getBody().getPosition().y
    );

    double cross1 = Rot2D.cross(touch_angleCur, touch_angleWant);

    if (cross1 > 0.0)
        touch_angleCur.rotate(Rot2D.fromDegrees(5.0));
    else
        touch_angleCur.rotate(Rot2D.fromDegrees(-5.0));

    double cross2 = Rot2D.cross(touch_angleCur, touch_angleWant);

    if (Math.signum(cross1) != Math.signum(cross2))
        touch_angleCur.load(touch_angleWant);

     entity_player.getBody().setTransform(entity_player.getBody().getPosition(), (float) (touch_angleCur.getAngle()));

Question:

I have a Problem with Java and lwjgl the Import:

import org.lwjgl.input.Keyboard;

won't work. I have added the jars:

lwjgl-glfw.jar
lwjgl-opengl.jar
lwjgl-stb.jar
lwjgl.jar
joml

Answer:

You are using LWJGL 3 right? LWJGL 3 doesn't have a Keyboard or Mouse class, you have to use the functions provided by GLFW. http://www.glfw.org/docs/latest/input_guide.html

Question:

In my Opengl Engine i have the following code to manage the mouse input stuff:

protected static void Mouse(){
        mousex = Mouse.getX();
        mousey = Mouse.getY();
        mouseDx = Mouse.getDX();
        mouseDy = Mouse.getDY();
        mouseWheelState = Mouse.getEventDWheel() / 120;
        while(Mouse.next()){
            int eventButton = Mouse.getEventButton();
            if(Mouse.getEventButtonState()){
                for(MouseListener listener : MouseManager.listener){
                    listener.mousePressed(AGLMouseButton.valueOf(Mouse.getButtonName(Mouse.getEventButton())));
                }
            }
            else if(eventButton > -1){
                for(MouseListener listener : MouseManager.listener)
                listener.mouseReleased(AGLMouseButton.valueOf(Mouse.getButtonName(Mouse.getEventButton())));
            }
        }
    }

my problem is that mouseWheelState never gets zero. if i rotate the weel up, mouseWheelState is 1 until i rotate it down.

How i could fix this?


Answer:

Mouse.getEventDWheel() gets the delta for the mouse wheel movement. Even if you don't touch the wheel it will report small changes.

An easy way to solve this is to use a 'deadband', a region where the changes are not accepted.

mouseWheelState = Mouse.getEventDWheel();

if(Math.abs(mouseWheelState)<deadbandWidth)
{
  mouseWheelState = 0;
}
mouseWheelState /= 120;

Question:

I am using LWJGL, and the problem is, I can't get a square to draw on the screen after calling the addSqaure() method in the keyboard input section. I moved the code from inside the addSquare() method to the update section just to make sure it actually works, and it does. It's specific to the keyboard method. When I press the SPACE, it just prints the text intended.

Here is my code.

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

public class MainClass {

    public static void initDisplay()
    {
        try
        {   
            Display.setDisplayMode(new DisplayMode(800, 600));
            Display.setTitle("sqaure addddder");
            Display.create();
        }
        catch(LWJGLException e)
        {
            e.printStackTrace();
            System.exit(0);
        }
    }

    public static void initGL()
    {
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glOrtho(0, 800, 0, 600, 1, -1);
        glMatrixMode(GL_MODELVIEW);

        glClearColor(0,0,0,1);
    }

    public static void gameLoop()
    {
        while(!Display.isCloseRequested())
        {
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

            getInput();

            Display.update();
        }
    }

    public static void getInput()
    {
        while(Keyboard.next())
        {
            if(Keyboard.getEventKeyState())
            {
                if(Keyboard.getEventKey() == Keyboard.KEY_SPACE)
                {
                    addSquare(0.3f, 0.4f, 0.7f, 1f);
                    System.out.println("Square was addedd at point.");
                }else{
                    System.out.println("Square was not added.");
                }
            }
        }   
    }

    public static void addSquare(float red, float green, float blue, float alpha)
    {
        glColor4f(red, green, blue, alpha);

        glBegin(GL_QUADS);
            glVertex2f(200, 200);
            glVertex2f(300 ,200);
            glVertex2f(300, 300);
            glVertex2f(200, 300);
        glEnd();
    }

    public static void callJanitor()
    {
        Display.destroy();
        System.exit(0);
    }

    public static void main(String[] args) {

        initDisplay();
        initGL();
        gameLoop();
        callJanitor();
    }
}

What is the problem with drawing squares?


Answer:

Your addSquare method doesn't actually add a square, it draws a square once. It is only visible for one frame per space press because the key event that you are checking for only happens once, when the space bar is first pressed. If you change the body of your getInput method to:

if(Keyboard.isKeyDown(Keyboard.KEY_SPACE)){
    addSquare(0.3f, 0.4f, 0.7f, 1f);
}

It will draw a square each frame that the space bar is held down.

Question:

What the title says. Is it possible? As far as I know, there is no getKeyPressed or anything of the sort. I want to use a switch case for organization (and practice) as well as speed (or so I was informed).

Basically, the switch case clause(?) is just a boolean return. But how can I check if a key is pressed based on a value passed into it without a pasta bowl of if / else statements?

Obviously this code doesn't work, but I'm looking for something like it.

public void moveCamera() {
    switch (Keyboard.isKeyDown(!!!CASE CHECKING HERE!!!)) {
        case Keyboard.KEY_W:
            position.z -= MOVE_SPEED;
            break;

        case Keyboard.KEY_A:
            position.x += MOVE_SPEED;
            break;

        case Keyboard.KEY_S:
            position.z -= MOVE_SPEED;
            break;

        case Keyboard.KEY_D:
            position.x += MOVE_SPEED;
            break;
    }
}

Answer:

Here is a solution that I liked (but did not try) from @KysonTyner.

You can switch on getEventKey(). This will hit your current cases and then you can wrap the whole switch statement with if (getEventKeyState()) {switch/case}. There is no need to use an event listener. – Kylon Tyner

As for what I used, I abandoned the idea of a switch entirely, as I realized that the movement speed would multiply if there were more than one direction of input.

I wanted to support multiple directions of motion for diagonal movement, so it wouldn't be "ghosting" as keys were pressed.

What I did is separate the functions, one to test keyboard input, and one to move the camera. There doesn't have to be any special logic for the key input, as it simply passes an LWJGL Vector3f to the camera move function.

Then, in camera move, I normalized that vector and altered the positions accordingly.

This is a simplified version of my Camera class for demonstration. I removed everything except translation.

package spikespaz.engine.main;

import org.lwjgl.input.Keyboard;
import org.lwjgl.util.vector.Vector3f;

// Created by spike on 7/3/2017.
public final class Camera {
    private Camera() {}

    private float moveSpeed = 0.2f;
    private Vector3f position = new Vector3f(0, 0, 0);

    public Camera(Vector3f position, Vector3f rotation) {
        this.position = position;
    }

            public void updateKeyInput() {
        Vector3f direction = new Vector3f(0, 0, 0);

        if (Keyboard.isKeyDown(Keyboard.KEY_W)) {
            direction.x = 1;
        } else if (Keyboard.isKeyDown(Keyboard.KEY_S)) {
            direction.x = -1;
        }

        if (Keyboard.isKeyDown(Keyboard.KEY_SPACE)) {
            direction.y = -1;
        } else if (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT)) {
            direction.y = 1;
        }

        if (Keyboard.isKeyDown(Keyboard.KEY_A)) {
            direction.z = 1;
        } else if (Keyboard.isKeyDown(Keyboard.KEY_D)) {
            direction.z = -1;
        }

        translate(direction);
    }

    private void translate(Vector3f direction) {
        Vector3f normalized = direction.normalise(null);
        Vector3f.add(position, (Vector3f) normalized.scale(moveSpeed), position);
    }

    public float getMoveSpeed() {
        return moveSpeed;
    }

    public void setMoveSpeed(float moveSpeed) {
        this.moveSpeed = moveSpeed;
    }
}

Translate is relative. In the full class I have another function to set absolute position, without the move speed.

In the above example, the input vector for translate is simply a direction. The function should coordinate that based on the movement speed for each frame.

Question:

Hi so I have a simple task which is to move a quad around the screen using the arrow keys. I'm using the LWJGL to make this very simple "game." However I've come across a problem. The keyboard input works and all but there is a major problem, you can't hold down the arrow key. it only moves when you click the key and release it right away. holding down does nothing.

Here is my clock class which I just used for time in the game:

public class Clock {

    private static long lastFrame;

    public static long getTime(){
        return (Sys.getTime() * 1000) / Sys.getTimerResolution();
    }

    public static int getDelta(){
        long currentTime = getTime();
        int delta = (int) (currentTime - lastFrame);
        lastFrame = getTime();
        return delta;
    }

    public static void initLastFrame(){
        lastFrame = getTime();
    }

}

And this is the method I'm using for movement when using the keyboard:

public void move(){
    while(Keyboard.next()){
        if(Keyboard.getEventKeyState()){
            if(Keyboard.getEventKey() == Keyboard.KEY_RIGHT){
                this.x += this.speed * getDelta();
            }
            if(Keyboard.getEventKey() == Keyboard.KEY_UP){
                this.y -= this.speed * getDelta();
            }
            if(Keyboard.getEventKey() == Keyboard.KEY_DOWN){
                this.y += this.speed * getDelta();
            }
            else if(Keyboard.getEventKey() == Keyboard.KEY_LEFT){
                this.x -= this.speed * getDelta();
            }
        } 
    }
}

Answer:

Keyboard.getEventKeyState()

will return true if the key was pressed and false if the it was release. You can set flags to decide which direction you should move.

if (Keyboard.getEventKey() == Keyboard.KEY_A) {
    if (Keyboard.getEventKeyState()) {
        movingLeft = true;
        System.out.println("A Key Pressed");
    }
    else {
        movingLeft = false;
        System.out.println("A Key Released");
    }
}

And moving left according to movingLeft.

There is an other solution for exactly this problem is simply using

Keyboard.isKeyDown(Keyboard.KEY_A) 

Pretty self explaining, returns true if A is down, false if not.

Question:

I'm trying to use LWJGL in my opengl's project. I already added the LWJGL jars; native and src to the classpath, but I think i'm doing something wrong, because i can't find the input package


Answer:

If you are using LWJGL 2: type Keyboard. and a list of options will pop up (they are self explanatory) you can also use Mouse. (remember the capital)

If you are using LWJGL 3: sorry, I don't know the answer... I use 2 myself because there are a lot more tutorials on it and it helps the debugging :)