SatoNaka55
SatoNaka55

Reputation: 13

Swing going from menu to actual gameplay

So I am making a space invaders clone. Originally I had no problem getting my game to work with a simple main class that created the frame, created the gameplay and started the thread.

But then I tried to implement a start menu and it all went to crap. The menu appears with success but the gameplay does not appear when I press start.

I am running out of ideas and I am completely stumped. I am somewhat new as well to SO, so if there is anything I left out, I appreciate any help.

Here is the original with no menu that worked fine:

    public static void main(String[] args) {

    JFrame frame = new JFrame("SpaceRaiders");

    frame.setSize(600, 600);
    frame.setLocationRelativeTo(null);
    frame.setResizable(false);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    Gameplay gameplay = new Gameplay();
    frame.add(gameplay);
    frame.setVisible(true);

    Thread t1 = new Thread(gameplay);
    t1.start();


}

However, the moment I tried to implement a menu to then play the game, I am running into all sorts of trouble. I created a UI class as well as an actual "game" class like so:

public class UI {

JFrame frame, f2;
JPanel titrePanel, startButtonPanel, loadButtonPanel, p2;
JLabel nomJeu;
JButton startButton, loadButton;
Font fontTitre, fontStart;
Gameplay gameplay;

public void createUI(ChoixJeu cj) {
    frame = new JFrame("SpaceRaiders");
    frame.setSize(600, 600);
    frame.setLocationRelativeTo(null);
    frame.setResizable(false);

    frame.setLayout(null);

    frame.getContentPane().setBackground(Color.black);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    //------------------ECRAN MENU---------------------
    //Titre
    titrePanel = new JPanel();
    titrePanel.setBounds(100, 100, 400, 100);
    titrePanel.setBackground(Color.BLUE);

    Font fontTitre = new Font("Times New Roman", Font.BOLD, 50);
    Font fontStart = new Font("Times New Roman", Font.PLAIN, 20);
    nomJeu = new JLabel("SpaceRaiders");
    nomJeu.setForeground(Color.white);
    nomJeu.setFont(fontTitre);
    titrePanel.add(nomJeu);

    //Start button
    startButtonPanel = new JPanel();
    startButtonPanel.setBounds(200, 400, 200, 40);
    startButtonPanel.setBackground(Color.BLACK);

    startButton = new JButton("START");
    startButton.setBackground(Color.BLACK);
    startButton.setForeground(Color.WHITE);
    startButton.setFont(fontStart);
    startButton.setFocusPainted(false);
    startButton.addActionListener(cj);
    startButton.setActionCommand("start");
    startButtonPanel.add(startButton);

    //Load Button
    loadButtonPanel = new JPanel();
    loadButtonPanel.setBounds(200, 440, 200, 100);
    loadButtonPanel.setBackground(Color.BLACK);

    loadButton = new JButton("LOAD");
    loadButton.setBackground(Color.BLACK);
    loadButton.setForeground(Color.WHITE);
    loadButton.setFont(fontStart);
    loadButton.setFocusPainted(false);
    titrePanel.add(nomJeu);
    loadButtonPanel.add(loadButton);

    frame.add(startButtonPanel);
    frame.add(titrePanel);

    //------------------ECRAN MENU FIN---------------------   
    frame.setVisible(true);


}

And the game class...

public class Jeu {

ChoixJeu cj = new ChoixJeu();
UI ui = new UI();
Ecrans e = new Ecrans(ui);
Gameplay gp;

public static void main(String[] args) {

    new Jeu();


}

public Jeu() {

    ui.createUI(cj);
    Gameplay gameplay = new Gameplay();
    this.gp = gameplay;

}

public class ChoixJeu implements ActionListener {

    @Override
    public void actionPerformed(ActionEvent ae) {

        String yourChoice = ae.getActionCommand();

        switch (yourChoice) {
            case "start":
                e.montrerEcranJeu();
                new Thread(gp).start();
                ui.frame.add(gp);

                break;
            default:
                break;
        }

    }

}

}

I also tried to make a class/method that hides the menu panels

    public void montrerEcranJeu() {
    //Cache Menu
    ui.titrePanel.setVisible(false);
    ui.startButtonPanel.setVisible(false);


    //Montre Jeu
    // ui.frame.add(gameplay);
}

And just in case the Gameplay class. The run() method is at the bottom

public class Gameplay extends JPanel implements KeyListener, ActionListener, Runnable {

private Ship player = new Ship(new Point(200, 555));
Timer t = new Timer(5, this);


private ArrayList<Laser> lasers = new ArrayList<Laser>();
private int laserNb;
private boolean readytofire;
private boolean shot = false;


private ArrayList<Invader> invaders = new ArrayList<Invader>();

private boolean pause;

public Gameplay() {
    super();
    t.start();
    addKeyListener(this);
    setFocusable(true);
    setFocusTraversalKeysEnabled(false);

    for (int j = 0; j < 80; j += 20) {
        for (int i = 0; i < 20; i++) {
            invaders.add(new Invader(5 + i * 30, j));
        }
    }
}

public boolean addLaser(Laser a) {
    lasers.add(a);
    return true;
}

public boolean addPlayer(Ship p) {

    this.player = p;

    return true;
}


@Override
public void keyTyped(KeyEvent ke) {

}

public void keyPressed(KeyEvent e) {
    if (KeyEvent.VK_RIGHT == e.getKeyCode()) {
        moveRight();
    }
    if (KeyEvent.VK_LEFT == e.getKeyCode()) {
        moveLeft();
    }
    if (KeyEvent.VK_SPACE == e.getKeyCode()) {
        shoot();
        System.out.println("Space Action from Gameplay is working");
    }
}

@Override
public void keyReleased(KeyEvent e) {

}

@Override
public void actionPerformed(ActionEvent ae) {
    repaint();
}


public void moveRight() {
    if (player.getCentre().getX() >= 580) {
        player.setX(580);
    } else {
        double movement = player.getCentre().getX();
        movement += 10;
        player.setX(movement);
    }
    this.repaint();
}

public void moveLeft() {
    if (player.getCentre().getX() <= 20) {
        player.setX(20);
    } else {
        double movement = player.getCentre().getX();
        movement -= 10;
        player.setX(movement);
    }
    this.repaint();
}


public void shoot() {

    shot = true;
    if (readytofire) {
        Point top = new Point(player.getTopX(), player.getTopY());
        Laser laser = new Laser(top);
        addLaser(laser);

    }
}


public void moveShot() {
    if (shot) {
        for (Laser l : lasers) {
            l.setY(l.getTopLeft().getY() - 1);
        }
    }
}


@Override
public void paint(Graphics g) {
    setBackground(Color.black);
    super.paint(g);
    player.draw(g);
    for (Laser l : lasers) {
        l.draw(g);
    }
    for (Invader i : invaders) {
        i.draw(g);
    }

}

// public void paintComponent (Graphics g){
// Controle Thread
public void run() {

    while (true) {
        moveShot();

        for (Invader i : invaders) {
            i.moveAndUpdate();

        }
        //  for (Invader i : invaders) {
        //         if (){
        //            System.out.println("YOU ARE DEAD!");
        //        } 

        //  }
        try {
            Thread.sleep(10);
            readytofire = true;

        } catch (InterruptedException ex) {
            Logger.getLogger(Gameplay.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

}

}

Upvotes: 0

Views: 41

Answers (1)

MadProgrammer
MadProgrammer

Reputation: 347194

So, using null layouts is the beginning of your problems. I might recommend using CardLayout which is designed to help you dynamically switch between views. See How to Use CardLayout for more details. I'd also suggest taking the time to read through Laying Out Components Within a Container and finding one or more appropriate layouts to support your menu.

You're also making a lot of fundamental mistakes. Swing is not thread safe, so you should avoid updating the UI (or something the UI depends on) from outside the context of the EDT - see Concurrency in Swing for more information and How to Use Swing Timers for a possible solution.

As a general recommendation, you should avoid overriding paint and, in the case of classes which extend from JComponent, prefer paintComponent instead. You should also avoid call methods which might change the state of the component during a paint cycle, this can increase the number of repaint requests and degrade the performance of your program (ie, don't call setBackground inside paint).

Have a look at Performing Custom Painting and Painting in AWT and Swing for more details about how the paint system works and how best you can work with it.

You should also avoid KeyListener, this is likely to cause you issues when you introduce other, focusable, components into the picture. Instead, you should favour the Key bindings API instead

I've read through [insert link or tutorial], but it still doesn't help...

And forgive me if this doesn't happen all the time.

The point of providing you the tutorial links is to encourage you to learn something;

  • Learn where to find answers to your questions
  • Learn how the APIs work
  • Expand your knowledge and understanding of how the APIs work

Having said that, they're not always "obvious" as to the solution. What I do when I'm in this situation is start with one or more new projects, dedicated to just working on that aspect of the API I'm trying to understand. For here I can explore the concepts in isolation and when I "think" I understand them, try and implement them into the project I'm working on. This might take a number of iterations, but once it works, I have gained a much deeper understanding and appreciation of the API then I would have gained from a simple "copy-n-paste" solution

Upvotes: 1

Related Questions