Reputation: 477
I have two classes (Sampling
and Stacker
). The Sampling
class (my Main class) is extends JFrame
and has a JButton
with an ActionListener
to open the Stacker
class.
The problem is when the button is clicked, the Stacker
class will open but only a frame without any components. When I switch the main method into the Stacker
class, the program works fine. What is the problem?
Here is the code:
The Sampling
class:
public class Sampling extends JFrame implements ActionListener
{
private JButton openStacker;
Stacker st;
public Sampling()
{
setSize(300,300);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new FlowLayout());
setLocationRelativeTo(null);
openStacker = new JButton("Start Stacker!");
add(openStacker);
openStacker.addActionListener(this);
setVisible(true);
}
public void actionPerformed(ActionEvent e)
{
dispose();
st = new Stacker();
}
public static void main (String args[])
{
new Sampling();
}
}
The Stacker
game class:
public class Stacker extends JFrame implements KeyListener
{
int iteration = 1;
double time = 200;
int last = 0;
int m = 10;
int n = 20;
JButton b[][];
int length[] = {5,5};
int layer = 19;
int deltax[] = {0,0};
boolean press = false;
boolean forward = true;
boolean start = true;
public Stacker()
{
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setSize(400,580);
this.setUndecorated(false);
this.setLocationRelativeTo(null);
b = new JButton [m][n];
setLayout(new GridLayout(n,m));
for (int y = 0;y<n;y++)
{
for (int x = 0;x<m;x++)
{
b[x][y] = new JButton(" ");
b[x][y].setBackground(Color.DARK_GRAY);
add(b[x][y]);
b[x][y].setEnabled(false);
}//end inner for
}
this.setFocusable(true);
this.pack();
this.addKeyListener(this);
this.setVisible(true);
go();
}
public void go()
{
int tmp = 0;
Component temporaryLostComponent = null;
do{
if (forward == true)
{
forward();
} else {
back();
}
if (deltax[1] == 10-length[1])
{
forward = false;
} else if (deltax[1] == 0)
{
forward = true;
}
draw();
try
{
Thread.sleep((long) time);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}while(press == false);
if (layer>12)
{
time= 150-(iteration*iteration*2-iteration);
} else
{
time = time - 2.2;
}
iteration++;
layer--;
press = false;
tmp = check();
length[0] = length[1];
length[1] = tmp;
if (layer == -1)
{
JOptionPane.showMessageDialog(temporaryLostComponent, "Congratulations! You beat the game!");
repeat();
}
if (length[1] <= 0)
{
JOptionPane.showMessageDialog(temporaryLostComponent, "Game over! You reached line "+(18-layer)+"!");
repeat();
}
last = deltax[1];
start = false;
go();
}
public int check()
{
if (start == true)
{
return length[1];
}
else if (last<deltax[1])
{
if (deltax[1]+length[1]-1 <= last+length[0]-1)
{
return length[1];
}
else
{
return length[1]-Math.abs((deltax[1]+length[1])-(last+length[0]));
}
}
else if (last>deltax[1])
{
return length[1]-Math.abs(deltax[1]-last);
}
else
{
return length[1];
}
}
public void forward()
{
deltax[0] = deltax[1];
deltax[1]++;
}
public void back()
{
deltax[0] = deltax[1];
deltax[1]--;
}
public void draw()
{
for (int x = 0;x<length[1];x++)
{
b[x+deltax[0]][layer].setBackground(Color.DARK_GRAY);
}
for (int x = 0;x<length[1];x++)
{
b[x+deltax[1]][layer].setBackground(Color.CYAN);
}
}
public void repeat()
{
if(JOptionPane.showConfirmDialog(null, "PLAY AGAIN?","WARNING",JOptionPane.YES_NO_OPTION)== JOptionPane.YES_OPTION)
{
dispose();
new Stacker();
}else{
System.exit(0);
}
}
public void keyPressed(KeyEvent e)
{
if (e.getKeyCode() == KeyEvent.VK_SPACE)
{
press = true;
}
}
public void keyReleased(KeyEvent arg0)
{
}
public void keyTyped(KeyEvent arg0)
{
}
}
Upvotes: 1
Views: 223
Reputation: 208994
Just to put all my comments into an answer, and give you somewhere to start with:
Comment 1: Take out
go();
see that happens. I tested it and it will work. If you leave it there, even the frame's close button is jammed. You're blocking the edt with the while->Thread.sleep junk. You'll want to do some refactoring. You're code it hard to follow and I have no idea what you're trying to do, so I didn't even attempt itComment 2: If you're wondering why it works when you just run the main from the Stacker class, it's probably because you are running it outside the EDT,
public static void main(String[] args) { new Stacker(); }
.
What happens when you click the button, that action is performed within the EDT, and hence yournew Stacker()
will be run on the EDT. In which case the EDT gets blocked by your while loop. If you try run the program from the Stacker class, but wrap it in aSwingUtilities.invokeLater
, you will also notice the program fails to work. Swing programs should be run on the EDT though.Comment 2: Read the first few sections on Concurrency with Swing
So what you can do is use a Swing Timer (which operates on the EDT) for the game loop. What I did was refactor your code a bit. It doesn't operate the way you want it to yet, only because I didn't really understand the logic of your code. So I couldn't get it to work. What I did though, is put some of the logic into the Timer.
Timer timer = new Timer((int)time, new ActionListener(){
public void actionPerformed(ActionEvent event) {
if (forward == true) {
forward();
} else {
back();
}
if (deltax[1] == 10 - length[1]) {
forward = false;
} else if (deltax[1] == 0) {
forward = true;
}
draw();
}
});
And when the go()
method is called, it just starts the timer by calling timer.start()
. Basically what you need to know about the timer, is that every tick (the milliseconds you pass it), the actionPerformed
will be called. So you can update the game state in that method, just like you did in the while loop each iteration.
Take some time to go over How to Use Swing Timers
To get the game working properly, you still need to make some adjustments, but this should give you a head start.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Sampling extends JFrame implements ActionListener {
private JButton openStacker;
Stacker st;
public Sampling() {
setSize(300, 300);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new FlowLayout());
setLocationRelativeTo(null);
openStacker = new JButton("Start Stacker!");
add(openStacker);
openStacker.addActionListener(this);
setVisible(true);
}
public void actionPerformed(ActionEvent e) {
dispose();
st = new Stacker();
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable(){
public void run() {
new Sampling();
}
});
}
}
class Stacker extends JFrame implements KeyListener {
int iteration = 1;
double time = 200;
int last = 0;
int m = 10;
int n = 20;
JButton b[][];
int length[] = {5, 5};
int layer = 19;
int deltax[] = {0, 0};
boolean press = false;
boolean forward = true;
boolean start = true;
Timer timer = new Timer((int)time, new ActionListener(){
public void actionPerformed(ActionEvent event) {
if (forward == true) {
forward();
} else {
back();
}
if (deltax[1] == 10 - length[1]) {
forward = false;
} else if (deltax[1] == 0) {
forward = true;
}
draw();
}
});
public Stacker() {
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setSize(400, 580);
this.setUndecorated(false);
this.setLocationRelativeTo(null);
b = new JButton[m][n];
setLayout(new GridLayout(n, m));
for (int y = 0; y < n; y++) {
for (int x = 0; x < m; x++) {
b[x][y] = new JButton(" ");
b[x][y].setBackground(Color.DARK_GRAY);
add(b[x][y]);
b[x][y].setEnabled(false);
}//end inner for
}
this.setFocusable(true);
this.pack();
JPanel panel = (JPanel)getContentPane();
panel.addKeyListener(this);
this.setVisible(true);
panel.requestFocusInWindow();
go();
}
public void go() {
int tmp = 0;
Component temporaryLostComponent = null;
timer.start();
if (layer > 12) {
time = 150 - (iteration * iteration * 2 - iteration);
} else {
time = time - 2.2;
}
iteration++;
layer--;
press = false;
tmp = check();
length[0] = length[1];
length[1] = tmp;
if (layer == -1) {
JOptionPane.showMessageDialog(temporaryLostComponent, "Congratulations! You beat the game!");
repeat();
}
if (length[1] <= 0) {
JOptionPane.showMessageDialog(temporaryLostComponent, "Game over! You reached line " + (18 - layer) + "!");
repeat();
}
last = deltax[1];
start = false;
//go();
}
public int check() {
if (start == true) {
return length[1];
} else if (last < deltax[1]) {
if (deltax[1] + length[1] - 1 <= last + length[0] - 1) {
return length[1];
} else {
return length[1] - Math.abs((deltax[1] + length[1]) - (last + length[0]));
}
} else if (last > deltax[1]) {
return length[1] - Math.abs(deltax[1] - last);
} else {
return length[1];
}
}
public void forward() {
deltax[0] = deltax[1];
deltax[1]++;
}
public void back() {
deltax[0] = deltax[1];
deltax[1]--;
}
public void draw() {
for (int x = 0; x < length[1]; x++) {
b[x + deltax[0]][layer].setBackground(Color.DARK_GRAY);
}
for (int x = 0; x < length[1]; x++) {
b[x + deltax[1]][layer].setBackground(Color.CYAN);
}
}
public void repeat() {
if (JOptionPane.showConfirmDialog(null, "PLAY AGAIN?", "WARNING", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
dispose();
new Stacker();
} else {
System.exit(0);
}
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
System.out.println("Pressed");
press = true;
}
}
public void keyReleased(KeyEvent arg0) {
}
public void keyTyped(KeyEvent arg0) {
}
}
Notice the SwingUtilities.invokeLater
in the main
. That's how you can start up the program on the EDT. The link on Concurrency In Swing will give you more information.
Upvotes: 1