Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Libgdx pause/resume not called when window is being dragged (Windows)

I am creating a Java game with libgdx. Really liking the engine so far but I noticed an issue when dragging the game window. Rendering seems to stop (which is okay) but I cant find any events that get called during this state. Is there any way I can trap it? It not a big deal for rendering as I cap the deltaTime, but for input the keyUp events don't get fired which messes up my movement code for the player (if you are to release the keys while dragging the window). Is there anything I can do? Thanks!

like image 764
Arbel Avatar asked Feb 04 '14 13:02

Arbel


1 Answers

The problem you describe lies in the native window implementation of the LWJGL Display: This lightweight window does not loose focus while it is being moved around. Therefore calling

Display.isActive()

while moving the window around will still return true, even though the display is no longer updated.

I have found a workaround for this problem that works for my own project. One can add a parent Canvas to the LWJGL Display by calling

Display.setParent(canvas);

With this approach one is able to listen to window events by listening to the parent class. Adding a parent canvas to the display is also, what LwjglApplet does, so one is able to run libgdx applications inside an applet.

I have written an LwjglFrame class, that does essentially follow the same approach as LwjglApplet, with the difference, that the canvas is added to a JFrame:

import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Dimension;
import javax.swing.JFrame;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;

public class LwjglFrame extends JFrame{

    private final Canvas canvas;
    private LwjglApplication app;

    public LwjglFrame(final ApplicationListener listener, final LwjglApplicationConfiguration config) {
        canvas = new Canvas(){
            public final void addNotify () {
                super.addNotify();
                app = new LwjglApplication(listener, config, canvas);
            }

            public final void removeNotify () {
                app.stop();
                super.removeNotify();
            }
        };
        canvas.setIgnoreRepaint(true);
        canvas.setFocusable(true);

        setLayout(new BorderLayout());
        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        add(canvas, BorderLayout.CENTER);
        setPreferredSize(new Dimension(config.width, config.height));
        pack();
    }

    public LwjglFrame(final ApplicationListener listener, final boolean useGL2) {
        canvas = new Canvas(){
            public final void addNotify () {
                super.addNotify();
                app = new LwjglApplication(listener, useGL2, canvas);
            }

            public final void removeNotify () {
                app.stop();
                super.removeNotify();
            }
        };
        canvas.setIgnoreRepaint(true);
        canvas.setFocusable(true);

        setLayout(new BorderLayout());
        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        add(canvas, BorderLayout.CENTER);
    }

}

With this class, one is able to run libgdx inside a JFrame and then listen for ComponentEvents. Here is some example code for how to create an LwjglFrame and pause the game, whenever the frame is moved around:

public static void main(String[] args) {
    // create the config as usual
    final LwjglApplicationConfiguration cfg = createConfig();
    // create your ApplicationListener as usual
    final ApplicationListener application = createApplication();

    // create an LwjglFrame with your configuration and the listener
    final LwjglFrame frame = new LwjglFrame(application, cfg);

    // add a component listener for when the frame gets moved
    frame.addComponentListener(new ComponentAdapter() {
        @Override
        public void componentMoved(ComponentEvent e) {
            // somehow pause your game here
            MyGame.getInstance().pauseGame();
        }
    });

    // set the frame visible
    frame.setVisible(true);

}

The downside of this approach is, that one needs some dedicated pause state, that gets entered once the window is moved. This pause state must then be manually exited by the user to continue the game. The upside is, that even if one does not pause the game, the screen will at least get updated, while one is moving the window (other than when using the lwjgl native frame).

I have not yet found a reliable way, to pause the game only during movements of the window, and automatically continue the loop once the movement is finished. The problem here is, that JFrame is not sending on/off events for moving the frame, but instead continuously notifies movement events while a window gets moved around.

EDIT

You could also make your com.badlogic.gdx.Game implement ComponentListener:

public class MayGame extends Game implements ComponentListener{

    public void componentResized(ComponentEvent e) {}

    public void componentMoved(ComponentEvent e) {
        System.out.println("Window moved!");
        // pause the game somehow
    }

    public void componentShown(ComponentEvent e) {}

    public void componentHidden(ComponentEvent e) {}

}

Then you create your Game like this:

public static void main(String[] args) {
    // create the config as usual
    final LwjglApplicationConfiguration cfg = createConfig();
    // create your Game
    final game MyGame = new MyGame();

    // create an LwjglFrame with your game and the configuration
    final LwjglFrame frame = new LwjglFrame(game, cfg);

    // add the game as a component listener
    frame.addComponentListener(game);

    // set the frame visible
    frame.setVisible(true);

}
like image 84
Balder Avatar answered Oct 12 '22 23:10

Balder