I am programming a chess game in java, and at the moment I am building a basic interface. It is simply an 8x8 array of buttons that will display in a window. I have coded for these buttons, and have gotten the board to display properly. However, when I connect this with the rest of the game, the game window crashes upon running and I have to force quit the java application. This is my code:
package Chess_Game;
import javax.swing.SwingUtilities;
import Chess_Interface.Iboard;
public class Game_Tester
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
Game G = new Game();
Iboard I = new Iboard(G.getBoard().getArray(), G.getSides());
I.setVisible(true);
while(!(G.isGameOver()))
{
boolean redo = true;
while(redo)
{
int row = 0;
int col = 0;
int nRow = 0;
int nCol = 0;
System.out.println("Click the piece you want to move.");
while(!(I.getBool())){}
if(I.getBool())
{
row = I.getRow();
col = I.getCol();
I.setBool(false);
}
System.out.println("Click the place you want to move to.");
while(!(I.getBool())){}
if(I.getBool())
{
nRow = I.getRow();
nCol = I.getCol();
I.setBool(false);
}
if(G.canMove(row, col, nRow, nCol))
{
G.move(row, col, nRow, nCol, I);
redo = false;
}
else
{
System.out.println("You cant move there! Try again!");
}
}
I.updateBoard(G.getBoard().getArray(), G.getSides());
}
}
});
}
}
The board displays properly when I comment out the main while loop (and everything inside of it), and I assume the problem lies somewhere inside there, but I have been unable to find it. I have also looked online for similar game loop problems, but all of those have been for games involving frame rates and movement across a java swing frame, something that is not present in my code.
Any help would be greatly appreciated.
You have several loops such as
while(!(I.getBool())){}
which could potentially run forever if I does not respond as expected. You could start by printing something out within these loops, and within the following blocks if(I.getBool()){...} to see at what point your application gets stuck.
Checking the user interface in a loop like this is not good practice. It is better to use Listeners to respond to the user interface.
Nor is running the main application on the Swing thread using SwingUtilities.invokeLater(new Runnable(), even though it avoids potential problems of updating the GUI from another thread.
In fact, this may be your root problem, as running the main application loop on the Swing thread (the thread used to run the GUI) like this probably prevents the GUI from ever responding properly. You are putting a task (the entire game) onto the GUI's queue, but that task never completes while(!(G.isGameOver())).
Related
I'm a beginner in Java programming and have come across an issue (probably an easy one to solve).
I am experimenting with Java GUI and wish to create a window in which the colours of an array are cycled through until there are no more colours. I believe I can do this using a for loop and cycling through the array, however I do not know how to loop through the background colour.
Any help and explanation would be appreciated.
public void flashColor() {
Color [] color = { Color.red,Color.orange,Color.green };
int i = 0;
for(i=0;i<color.length;i--){
getContentPane().setBackground(Color(i));
}
}
This line tells me:
getContentPane().setBackground(Color(i));
that yours appears to be a Swing GUI (a key bit of information that you left out of your question!), and so you need to take Swing threading into consideration. Your current code will in fact loop through all the colors, but it will do so immediately, and on the Swing thread so that the GUI will have no way to paint any of the colors other than the last one. The solution: use a Swing Timer and not a for loop. Inside the timer advance an index int variable and use it to show the color.
Something like:
getContentPane().setBackground(colorArray[0]);
int delay = 1000; // for 1 second
Timer myTimer = new Timer(delay, new ActionListener() {
int index = 0;
public void actionPerformed(ActionEvent e) {
index++;
if (index >= colorArray.length) {
((Timer)e.getSource()).stop(); // stop the timer
} else {
getContentPane().setBackground(colorArray[index]);
}
}
});
myTimer.start();
The code has not been tested, and you'll want to read the Swing Timer tutorial for the details.
Note the key here is that yes you need to loop, and to pause (so that the color can be seen) but you need to do your looping and pausing in a thread thread is off of the Swing event dispatch thread (or EDT). Yes you could do this using a SwingWorker, but that is a way more difficult way to do this. It is far easier to use a Swing Timer to do this for you. Note that it uses a background thread invisibly for you.
I'm attempting to "animate" a die roll in Java. I currently have an icon (called "diceImage") set up that, when a button is clicked (called "diceRoll"), updates to a new random image of a die face. What I would like to do is have it change image (to a random die face) numerous times over a couple of seconds before stopping on a final image.
The problem I have is not with generating a random number, or rolling it numerous times, it's with the updating the image numerous times within a loop. The code below, which rolls the die 10 times is what I have so far:
private void diceRollActionPerformed(java.awt.event.ActionEvent evt) {
for (int i = 1; i <= 10; i++) {
rollDice();
pause(100);
}
}
This links to the following two methods (the first of which generates the random number, and sets the icon image):
private void rollDice() {
Random r = new Random();
int randomNumber = r.nextInt(6) + 1;
diceImage.setIcon(new javax.swing.ImageIcon(getClass().getResource("/Game/Images/Dice " + randomNumber + ".png")));
}
The method below is "supposed" to pause the programme briefly between updating the image (this was taken from a programming course I was on where we had to animate an image of a car moving across the screen):
private void pause(int sleepTime) {
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
System.exit(-1);
}
}
All this programme seems to do is pause and then print the final dice roll. It doesn't show any of the "intermediate" faces. Does anyone have any ideas on why this isn't working?
Any help is greatly appreciated.
This question is asked several times a day. If you sleep in the event dispatch thread, you're preventing it from doing its job: react to events and repaint the screen.
Your animation should be done in another thread. Read the tutorial about concurrency in Swing, and use a Swing Timer.
The pause() you are using is when u need to animate moving stuff like a car for example , but just changing the icon of a JLabel which is the dice here doesn't need pause ( unless you just want it to be delayed a bit) ...
but anyways , to solve your issue , you need to call repaint() on that JLabel or updateGraphics(). Setting the Icon for the JLabel doesn't make the new Icon get displayed , you just need to repaint() it .
of course like JB Nizet said , for the app not to hang you need to call repaint on a new Thread .Thought you have to learn how to use Thread well cuz it can be very tricky at times .
good luck
First, I know that the way I'm trying to read the input is really bad, but I've tried to use Keylistener, and it doesn't work no matter what I try. I don't know what I'm doing wrong, so this is what I'm trying at the moment. I'm trying to call a thread in Controller.java from PreControls.java. If you could help me implement a working keylistener, it would me much appreciated, but I think fixing this thread problem will work too. I have tried debugging it, and the thread doesn't seem to start.
Code in PreControls.java:
Controller C = new Controller();
C.start();
System.out.print("Thread Should be started ");
Code in Controller.java:
package Game;
public class Controller extends Thread {
public void MyShipController(){
System.out.print("Thread Is started ");
String CharIn = "";
while(SpaceInvaders.GameOn = true){
CharIn = PreControls.ReadKeyPressed.getText();
if(CharIn.equalsIgnoreCase("a")){
SpaceInvaders.MyPos[0]--;
System.out.print("Move Left ");
}else if(CharIn.equalsIgnoreCase("d")){
SpaceInvaders.MyPos[0]++;
System.out.print("Move Right");
}else if(CharIn.equalsIgnoreCase(" ")){
//Fire Bullet
}
PreControls.ReadKeyPressed.setText("");
SpaceInvaders.MyShip.setLocation(SpaceInvaders.MyPos[0], SpaceInvaders.MyPos[1]);
jp1.repaint();
}
}
}
Sorry for not providing an SSCCEE. I'd need to send my whole project, and that defeats the point of doing a project.
ReadKeyPressed is the JTextArea that I'm having the letters inputted into.
jp1 is the JFrame
I'm working in the Eclipse IDE.
EDIT: forgot to add: After I start the applet, the Console reads "Thread Should be started" only, so it is a problem with the how I made the thread, or how I'm trying to create it.
Edit 2: My End goal is to detect when a (or left arrow) is pressed and move MyShip (a JLabel) to the left by 1 position and more it to the right by 1 position if d (or right arrow) is pressed.
Alright, I've accepted Williams answer as it made my thread run. And as suggested, I'm going to look into key bindings to detect when the keys are pressed. Thanks for all the help.
This = is an assinment operator.
while(SpaceInvaders.GameOn = true)
I think you wanted this ==
while(SpaceInvaders.GameOn == true)
Or better yet, and much cleaner, just
while(SpaceInvaders.GameOn)
Also, you should use key bindings instead of KeyListener, which my give you focus problems. See this answer for an example of using key bindings. Also see the tutorial
The code you've written in method MyShipController should be placed inside the run method of your thread. The run method is what the thread actually executes within.
As you've written it now, calling start on your Controller class will start your thread, and that thread will end immediately as its run method is empty.
public class Controller extends Thread {
#Override
public void run()
//public void MyShipController(){
System.out.print("Thread Is started ");
String CharIn = "";
while(SpaceInvaders.GameOn = true){
CharIn = PreControls.ReadKeyPressed.getText();
if(CharIn.equalsIgnoreCase("a")){
SpaceInvaders.MyPos[0]--;
System.out.print("Move Left ");
}else if(CharIn.equalsIgnoreCase("d")){
SpaceInvaders.MyPos[0]++;
System.out.print("Move Right");
}else if(CharIn.equalsIgnoreCase(" ")){
//Fire Bullet
}
PreControls.ReadKeyPressed.setText("");
SpaceInvaders.MyShip.setLocation(SpaceInvaders.MyPos[0], SpaceInvaders.MyPos[1]);
jp1.repaint();
}
}
}
However, I suggest your controller class extend Runnable rather than Thread.
I'm having a problem I'm making a pool game and I need the ballos to react when I simulate a hit, the program works like this, you click the direction and power to hit the ball and the click go, the go button is in the GUI class where my labels are created, the button calls a method from my main class that recieves the parameter and then with a while in it, changes the X and Y of the ball till the power is reduced to 0 and then stops, the code is working, but the ball moves until the while stops. So the while works and when the power int is 0 the while goes out and then the new X,Y are painted.
This is the funcion that the button calls, the button sends all the parameters
public void golpe(int pbola, int pvelocidad, String pdireccion, JLabel[] listalabels) throws InterruptedException{
listabolas[pbola].setVelocidad(pvelocidad);
listabolas[pbola].setDireccion(pdireccion);
while (listabolas[pbola].getVelocidad() > 0) {
moverBola(pbola, listalabels);
//System.out.println(listabolas[pbola].getPosX());
//System.out.println(listabolas[pbola].getPosY());
Thread.sleep(500);
//This line is supposed to change the X and Y of the object over and over
//but only does it till the end
listalabels[pbola].setLocation(listabolas[pbola].getPosX(), listabolas[pbola].getPosY());
}
}
Here is the function moverbola(), only copied one "if" so that the code doesn't look to big
private void moverBola(int pbola, JLabel[] listalabels) {
if (listabolas[pbola].getDireccion().equals("SE")) {
int pposX = listabolas[pbola].getPosX();
listabolas[pbola].setPosX(pposX + 1);
int pposY = listabolas[pbola].getPosY();
listabolas[pbola].setPosY(pposY + 1);
}
Swing is a single threaded framework. That is, all interactions with UI are expected to occur from within a single thread, known as the Event Dispatching Thread.
Any action that blocks this thread, will prevent the EDT from updating the screen or processing any new events.
Your while-loop is blocking the EDT, preventing it from painting any updates until after the while-loop is completed.
Take a look at Concurrency in Swing for more details.
There are a number of approaches you could take...
You could use a Thread, but this causes problems as you need to ensure that any changes you make to the UI are re-synced back to the EDT and this can become messy...
For example
You could use a javax.swing.Timer that ticks at a regular interval and you would update any internal parameters from within it's assigned ActionListener. Because the tick events occur within the EDT, it is save to update the screen from within it.
For example
You could use a SwingWorker to run the task in the background. It has methods for re-syncing updates back to the EDT, but might be a little over kill for your purposes...
Updated with a possible Timer example
Caveat- It is very hard to produce a reasonable example with only a code snippet, but, something like this might work
public void golpe(final int pbola, int pvelocidad, String pdireccion, final JLabel[] listalabels) throws InterruptedException{
listabolas[pbola].setVelocidad(pvelocidad);
listabolas[pbola].setDireccion(pdireccion);
Timer timer = new Timer(40, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
if (listabolas[pbola].getVelocidad() == 0) {
((Timer)evt.getSource()).stop();
} else {
moverBola(pbola, listalabels);
}
}
});
timer.setRepeats(true);
timer.start();
}
I am making a Conway's Game of Life program in java, and am trying to change it from the command line version to a GUI. From the command line I just printed an array which showed the generations (the objects such as blocks and blinkers are shown as a series of 1's and 0's where it is blank, and in the GUI I'm showing it as squares (white squares as blank and blue squares where it isn't). But where I'm getting stuck is when I make another method (which replaces the method which prints the array) which checks the grid array, if there is a zero then the square changes from white to blue, and vice-versa. The Conway's Life rules are dealt with in a separate class which is independent, and all this method does is after the rules have changed the array this method checks it.
The rules are done in methods in one class and the GUI components are done in another. But since I need instance of both how would I go about doing it?, merge the two classes (all the GUI classes into the Life one, embed them some how, I am completely stuck on what to do
public void runGUI() {
int x = getX(), y = getY();
x /= squareSize;
y /= squareSize;
for (int i = 0; i < LifeData.grid.length; i++) {
for (int j = 0; j < LifeData.grid[i].length; j++) {
if (LifeData.grid[i][j] == 0)
l.setCell(x, y, l.getCell(x, y) + 1);
else
l.setCell(x, y, l.getCell(x, y) - 1);
this.repaint();
}
}
}
That is what I have changed it to now but when compiling it is saying "non-static variable grid cannot be referenced from a static context" and "non-static method runGUI() cannot be referenced from a static context". When trying to run the method.
Make a separate thread that will execute the game of life and update the GUI.
Something like this
public class GameExecutor implements Runnable {
private static final int DELAY = 1000;
private GameOfLife game;
private boolean stop = false;
private Gui gui;
public GameExecutor(Gui gui, GameOfLife game) {
this.gui = gui;
this.game = game;
};
public void run(){
game.start();
while (!stop) {
game.step(); //execute a step
gui.update(game.getState());
try {
Thread.sleep(DELAY);
} catch (InterruptedException e) {}
}
}
}
Launch this in a thread at startup and pass it your gui. Don't forget to update the gui in the correct Swing thread.
Obviously you'll need to add some code to stop it, too :)