0

I am looking for a way to dinamiclly switch between panels / between a panel or a canvas. More specific: I am developing a game. In my code there is a class that extends canvas and implements Runnable, and in the constructor of Game, it creates a new instance of a class called window. That is window class:

public class Window extends Canvas {
private static final long serialVersionUID = -299686449326748512L;

public static JFrame frame = new JFrame();

public Window(int width, int height, String title, Game game) {
   // JFrame frame = new JFrame();
    frame.setPreferredSize(new Dimension(width, height));
    frame.setMaximumSize(new Dimension(width, height));
    frame.setMinimumSize(new Dimension(width, height));
    frame.setTitle(title);

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setResizable(false);
    frame.setLocationRelativeTo(null);
    frame.add(game);
    frame.setVisible(true);
    game.start();
 }
}

I want to be able to remove game from the frame, activate another panel, and stop the execution of Game. I have already tried:

game.stop();
Window.frame.remove(game);

but it makes the program to crash. Those are start() & stop() methods:

/**
 * starts the game.
 */
public synchronized void start() {
    thread = new Thread(this);
    thread.start();
    running = true;
}
 /**
 * tries to stop the game.
 */
public synchronized void stop() {
    try {
        thread.join();
        running = false;
    } catch (Exception e) {
        e.printStackTrace();
    }
}

My main goal is to be able to play a cutscene if some event happend and I am trying to use vlcj for that purpose. If anyone has an idea that will allow me to execute this goal that would be great too.

camickr
  • 321,443
  • 19
  • 166
  • 288
Tair Galili
  • 111
  • 3
  • 3
    *I want to be able to remove game from the frame, activate another panel,* - check out the section from the Swing tutorial on [How to Use CardLayout](https://docs.oracle.com/javase/tutorial/uiswing/layout/card.html). It allows you to swap components on the same area of the frame. – camickr Apr 12 '20 at 18:04
  • Thread.join() is going to wait for your thread to complete. It does nothing to communicate that you want to stop. At the very least, `running=false` needs to happen before `thread.join();` that way your game loop can read the variable `running` and stop looping. – matt Apr 12 '20 at 18:13
  • 1.See examples of using `CardLayout` : [1](https://stackoverflow.com/a/46870789/3992939) , [2](https://stackoverflow.com/a/46013230/3992939) , [3](https://stackoverflow.com/a/61121540/3992939). 2. Why does `Window` extend `Canvas` ? – c0der Apr 13 '20 at 07:06
  • @c0der I really dont know why window extended canvas, you're right, it is unnecessary. Also, I am not sure card layout would fit my case... First, I am using a thread which hasn't been used in all of those examples, and second, my Game object that is being 'displayed' on the frame is a canvas and not a panel. How can I fit my situation? – Tair Galili Apr 13 '20 at 07:51
  • Why would the fact you're using a thread change anything? A java.awt.Canvas is a Component that can be added to a CardLayout. Did you try switching your `join` and running = false statements? Since I assume setting running to false will allow your thread's run method to finish. – matt Apr 13 '20 at 10:58
  • @matt is wright, I too think. Also, calling `join` on a `Thread` will block until it is done. That means, that if you are calling the `game.stop()` code on the *EDT* without making sure first that the `Runnable` is going to quit, then the call (and as a result the *EDT*) is blocked, which makes your program hang. – gthanop Apr 15 '20 at 19:34

1 Answers1

0

I've made an example, doing what I think you want without a card layout, and using a thread. I think this is a proof of concept, that what your asking is possible. Below, I will include a few things I would do to improve it.

import javax.swing.*;
import java.awt.*;
public class SwapCards{
    Thread gameLoop;
    volatile boolean running = false;
    double x = 0;
    double y = 0;
    double theta = 0;
    JFrame frame = new JFrame("swapped");        
    Canvas gamePanel = new Canvas(){
        public void paint(Graphics g){
            super.paint(g);
            g.setColor(Color.BLACK);
            g.drawOval((int)x, (int)y, 25, 25);
        }
    };
    Canvas nonGame = new Canvas(){
        public void paint(Graphics g){
            super.paint(g);
            g.setColor(Color.BLUE);
            g.fillRect(0,0,200, 200);
        }
    };
    public void step(){
        x = 100 + 50*Math.sin( theta );
        y = 100 + 50*Math.cos( theta );
        theta += 0.02;
        if(theta > 6.28) theta = 0;
    }

    public void startGameLoop(){
        frame.remove(nonGame);
        frame.add(gamePanel, BorderLayout.CENTER);
        frame.validate();
        running = true;
        gameLoop = new Thread(()->{
            while(running){
                step();
                gamePanel.repaint();
                try{
                    Thread.sleep(30);
                }catch (Exception e){
                    running = false;
                    throw new RuntimeException(e);
                }
            }
        });
        gameLoop.start();
    }
    public void stopGameLoop(){
        frame.remove(gamePanel);
        frame.add(nonGame, BorderLayout.CENTER);
        running = false;
        try{
            gameLoop.join();
        } catch(Exception e){
            throw new RuntimeException(e);
        }
    }
    public void buildGui(){
        JButton button = new JButton("action");
        button.addActionListener( evt->{
           if(!running){
               startGameLoop();
           } else{
               stopGameLoop();
           }
        });
        frame.add(nonGame, BorderLayout.CENTER);
        frame.add(button, BorderLayout.SOUTH);
        frame.setSize(400, 400);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);

    }
    public static void main(String[] args){
        EventQueue.invokeLater( new SwapCards()::buildGui );
    }
}

First off, Canvas is somewhat outdated, use a JPane and override paintComponent. That gives you more access to the power of swing.

In this example I am doing trivial work so the thread is absolutely overkill I could replace it with a javax.swing.Timer.

    Timer timer = new Timer(30, evt->{
       step(); 
       gamePanel.repaint();
    });

Then in the start and stop methods, I just call timer.start() or timer.stop() respectively.

Using a CardLayout makes it a bit clearer what you want to do, plus it has methods for navigating the cards. Eg. If you have a cut scene with a series of components you want to show, you can use cardLayout.next(parent).

When we create the layout:

cards = new CardLayout();
swap = new JPanel(cards);
swap.add(gamePanel, "game");
swap.add(nonGame, "nogame");
cards.last(swap);
frame.add(swap, BorderLayout.CENTER);

This will add the cards to swap and make it show "nogame". Then in the start/stop methods we just switch to the respective card.

cards.show(swap, "game");
matt
  • 10,892
  • 3
  • 22
  • 34