Reputation: 986
I'm making a simple java game and trying to implement a launcher at the beginning of the game.
For example, a JFrame
with a button in it that starts my application when it's pressed.
What I'm trying to do is have the main method call a separate class that opens a JFrame
, and an ActionListener
for the JButton
that calls new Cliker();
when it's called.
However, when new Cliker();
is called outside of the main
method, it opens the game JFrame
, but not the JPanel
.
Why is this error happening, and how could I fix it? I am relatively new to programming so I apologize in advance for any unclear points about this question or my program.
I would greatly appreciate your help. If you need any more classes to help answer my question, just ask, or if you have any other questions feel free to let me know.
package com.Cliker;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import com.Cliker.Accessories.Music;
public class Cliker extends JFrame {
private static final long serialVersionUID = 1L;
int score = 0;
JButton ball = new JButton(new ImageIcon("res/textures/ball/ball.PNG"));
JFrame gameFrame = new JFrame("Cliker v1.0");
public static void main(String[] args){
new Cliker();
}
public Cliker() {
gameFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gameFrame.add(new MyPanel());
gameFrame.pack();
gameFrame.setVisible(true);
gameFrame.setResizable(false);
gameFrame.setLocationRelativeTo(null);
ClikerGame play = new ClikerGame();
play.run(this);
}
class MyPanel extends JPanel implements MouseListener {
private static final long serialVersionUID = 1L;
public MyPanel() {
setBackground(Color.CYAN);
ball.addMouseListener(new MouseListener() {
@Override
public void mousePressed(MouseEvent e) {
if (e.getSource() == ball) {
score++;
Music.music(4);
repaint();
}
}
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
});
ball.setBorder(null);
add(ball);
addMouseListener(this);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setFont(new Font("Fixedsys", Font.BOLD, 30));
g.setColor(Color.BLACK);
g.drawString(String.valueOf(score), getWidth() / 2, 40);
}
public Dimension getPreferredSize() {
return new Dimension(700, 500);
}
@Override
public void mousePressed(MouseEvent e) {
if (e.getSource() == this) {
this.setBackground(Color.RED);
gameFrame.setVisible(false);
ClikerEndGame sendData = new ClikerEndGame();
try {
sendData.finish(score);
} catch (Exception e1) {
JOptionPane.showInputDialog(null,"The game has encountered an error. Error code: 001");
e1.printStackTrace();
}
}
}
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent arg0) {
}
}
}
Next Class
package com.Cliker;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import com.Cliker.Accessories.Music;
public class ClikerGame implements Runnable {
private int x, y, xa = 2, ya = 2;
public void run(Cliker panel) {
JButton ball = panel.ball;
Music.music(2);
while (true) {
try {
x += xa;
y += ya;
if (x < 0 || x > 700 - 20 ) {
Music.music(1);
xa = -xa;
}
else if (y < 0 || y > 500 - 20 ){
Music.music(1);
ya = -ya;
}
Thread.sleep(15);
ball.setBounds(x,y,30,30);
} catch (Exception e) {
JOptionPane.showMessageDialog(null,"Error 1");
}
}
}
public void run() {
this.run();
}
}
Upvotes: 0
Views: 88
Reputation: 347334
Basically, you're violating the single thread rules of Swing, the ClikerGame
is running a while-loop
which is blocking the Event Dispatching Thread, preventing it from processing new events, including paint events.
The reason that it works from main
, is main
isn't called from within the EDT, basically, it was a fluke. When you call new Clicker()
from within the ActionListener
of your JButton
however, you are running from within the context of the EDT, hence your problem.
Take a look at Initial Threads and Concurrency in Swing for more details.
The "immediate" solution might look like using a Thread
, but this would also violate the single rules of Swing, as Swing is not thread safe and you should never update the UI from outside the context of the EDT.
A better solution might be to use a Swing Timer
to act as a pseudo loop instead. This is notified, at regular intervals, withing the context of the EDT
Maybe, something like...
public class ClikerGame {
private int x, y, xa = 2, ya = 2;
private Clicker clicker;
private Timer timer;
public ClikerGame(Clicker clicker) {
this.clicker = clicker;
timer = new Timer(15, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
x += xa;
y += ya;
if (x < 0 || x > 700 - 20) {
Music.music(1);
xa = -xa;
} else if (y < 0 || y > 500 - 20) {
Music.music(1);
ya = -ya;
}
Thread.sleep(15);
clicker.ball.setBounds(x, y, 30, 30);
clicker.ball.getParent().repaint();
} catch (Exception e) {
JOptionPane.showMessageDialog(null, "Error 1");
}
}
});
}
public void start() {
if (!timer.isRunning()) {
Music.music(2);
timer.start();
}
}
public void stop() {
timer.stop();
// Stop the music
}
}
Upvotes: 2