I have a Board that contains tiles and the Tiles contain Obstacles. An obstacle can be one of the several types (wall, laser, ...).
Every round of the game I execute the board, which exectutes all the Obstacles. Once an obstacle has been executed and something has changed the GUI is notified and the affected tiles are refreshed.
In the GUI a tile is an OverLayLayout that contains overlapping images (JLabels), so all the overlapping images are visible.
Now the problem is when I execute them, it goes to fast, you can't anything happen. E.g. you don't see the laser beam appear, the player will be damaged but you didn't see it happen...
So I tried to let the game wait for e.g. 500ms so the user can see the GUI update. I've tried (but might have done it wrong): timers, swingtimers, countdownlatch, ... but I can't seem to get it to work.
Any ideas on how to fix this? Or maybe an alternative for the waiting?
Thanks in advance!
Update:
The execute in board looks like this:
Iterator<Obstacle> it = obstacles.iterator();
while (it.hasNext())
it.next().execute(this);
CountDownLatch countDown = new CountDownLatch(1);
setChanged();
notifyObservers(countDown);
countDown.await();
The update function of the GUI board (which is notified) looks like this:
final CountDownLatch countDown = (CountDownLatch) arg;
SwingUtilities.invokeLater(new Runnable(){
public void run() {
try {
updateUI();
wait(500);
} catch (InterruptedException ex) { }
countDown.countDown();
}
});
Without seeing your code, it's hard to identify the problem; I suspect you're blocking the event dispatch thread.
This tile-based game uses an instance of javax.swing.Timer to animate play. Press A to enable animation, and click on a free square to see the effect. You may be able adapt the approach to your game.
Related
Currently I have a start menu for a game with a button which transform my menu background image from a PNG into a GIF after a button press. Now, I want my code to wait until the GIF animation is over. After that, it should continue normally (by opening a new JFrame with the actual game).
I've read some stuff about a swing timer, although I'm unsure of how to implement this as I am a Java beginner.
private ImageIcon a = new ImageIcon("a.png");
private ImageIcon b = new ImageIcon("b.gif");
class AddInterestListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
bgLabel.setIcon(b); //replace PNG with GIF
//This here is where I want my delay to happen without freezing the GUI
JFrame game = new JFrame(); //Actual game frame starting etc.
}
}
Any suggestions?
You can't use blocking methods like Thread.sleep() or Thread.join() in Swing EDT. This will freeze the UI as Swing EDT handles all the UI refresh events.
You must implement this with events, first event blocks the UI second unblocks it. If you know the GIF animation length you can use SwingUtils.invokeLater() to run an async thread with that will fire the event after a fixed delay.
[Edit] apparently there was already an answer and I don't know if that answer , or my answer for that matter, fits in your software. One thing I can say for sure is that my method allows you to use custom scaling algorithms etc instead of the built-in one (but you probably don't need that either).
Afaik it is not possible to monitor the progress of a GIF displayed using SWING. You'll have to make your own GIF decoder/animator in order for you to 'detect' when the GIF is about to loop or end (yes animated GIFs can end).
For that I used a 3rd party loader from https://github.com/DhyanB/Open-Imaging to obtain the individual frames and timing information. No guarantee that this is still the best library out there as I found this little over a year ago.
From there on you'll need to write your own animator using javax.swing.Timer or similar.
I'm creating a board game using a GUI and JFrames/JPanels where you can play against the computer. I have a method called showPieces() which updates board GUI by changing the image icons on an array of buttons (which are laid out in a grid format). Once the icons have been updated the revalidate() and repaint() methods to update the GUI.
The showPieces() method has a parameter that needs to be passed to it every time it is called.
The main issue I'm having is I want the human to make a move, update the GUI, wait 1 second, the computer makes it's move and then loop until someone wins.
My basic code is the following:
do{
human.makeMove();
gui.showPieces(data);
try {
Thread.sleep(1000);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
computer.makeMove()
gui.showPieces(data);
}while(playing);
This cause the issue where when the human player makes their move, the GUI will freeze for one second and then after the delay, both moves are made at the same time.
I hope it makes sense, but I'm a novice with Java and may have to look more into threading as I don't understand it well enough.
Thread.sleep() is done on the Event Dispatch Thread which will lock the GUI.
So If you need to wait for a specific amount of time, don't sleep in the event dispatch thread. Instead, use a timer.
int delay = 1000; //milliseconds
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
//...Perform a task...
}
};
new Timer(delay, taskPerformer).start();
As with most all similar Swing questions, you're putting your entire Swing GUI to sleep by calling Thread.sleep(...) on the GUI's event thread (the EDT or Event Dispatch Thread), and when during this period the GUI will not be able to update its images or interact with the user whatsoever. The solution here is not to use Thread.sleep(...) but rather to use a Swing Timer to cause your 1 second delay.
Swing Timer Tutorial.
I'm currently working on a chess project.
I've set up a working code from console, and im working on setting up a GUI with swing. Debugging my application, I saw that my main and my GUI runs in different threads, so I came to the following question.
Is there a way to put my main thread to sleep while waiting for the user to click on a chess piece, then resume after the click?
To contextualize, I have a 8x8 array of buttons (board[i][j]) and for each button I set up a custom action that saves i and j into 2 static variables I declared into my main. So that after the button is clicked, I send the coordinates of that button to my main code that operates on the "real" matrix of chess pieces calling methods for controls.
Is that the correct way to go about this?
Is there a way to put my main thread to sleep while waiting for the user to click on a chess piece, then resume after the click?
It is the wrong way to think. A GUI should be event driven, rather than working in an infinite loop waiting for input (or for main to get input).
But the problems are (usually) from trying to 'match up' a command line based app and an app. with a GUI.
I say that because it sounds like the command line based app. did not go to the trouble of creating data structures for a ChessGameModel that can be acted on by the players and potentially shared amongst different classes that might need to interact with it. Classes like .. a command line based front end, or the various classes of a GUI, or a server that is running chess games between people over the net or..
Build the ChessGameModel and the rest will be a lot simpler.
What if when you click on a button you set a variable which is checked continually by your "main thread".
//In your GUIClass (GUI Thread)
btnInput1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
oProcess.setInput("Forward");
}
});
//In your Process Class (Main Thread)
private String input;
public void setInput(String input){
this.input = input;
}
public void checkInput(){
while(true){
if(input.equals("")
{
//Do nothing
Thread.Sleep(1); //Sleep so your loop doesn't use 100% of your processor
} else {
if(input.equals("Forward")
{
//Do the action
}
}
}
}
But you need to share the instance of your process class with the GUI Thread so you can use the same variable.
(Sorry for my spelling)
The answer to this is: Yes, but you shouldn't.
Putting a Thread to "sleep" is wasting CPU cycles. Instead you should strive to make the stuff you do on your main-thread after recieving input in a more "event-driven" way.
First off you should already have some class that contains the board. If you haven't: now's the time ;)
The next thing you want to do is to not make that class your GUI, because the GUI shouldn't be concerned with the gamelogic.
and then it's getting simple, assuming following GUI field:
private Board board = // your actual board;
// assuming Java 8
button.addActionListener((event) -> {
board.recieveAction(i, j);
});
I'm trying to code a simple game in Java. The basic structure is a single JFrame with different JPanels that I add/remove at different times. At startup, there is a JPanel that's a basic menu (start game, high scores, etc). Once the "Start" button is pressed it switches to a level selector panel with three buttons to select the difficult level of the game. Once any of the three buttons is pressed, it switches to another panel that will displays a three second countdown, then the actual game. All three buttons call the same method, just with a different difficulty value passed in.
I have all the separate pieces working fine, but I'm having troubles with the transition from the level selection panel to the countdown. If I don't use threads the screen freezes on button press and does not switch to the new panel. I've tried messing around with threads, but I don't know that much about them and have only had limited success (I've got it so it will successfully switch some of the time, but not consistently).
In terms of code, in the level selection panel I have something like this listening for button clicks:
private class ButtonClickedListener implements ActionListener {
public void actionPerformed(ActionEvent evt) {
gui.newLevel(1);
}
}
where in place of just gui.newLevel(1) I've messed around with starting new threads and calling the method from them.
The newLevel() method look like:
getContentPane().removeAll();
levelPanel = new LevelPanel(levelNum, this);
add(levelPanel);
validate();
levelPanel.start();
I use very similar code when switching from the start menu JPanel to the level selector panel (again, with an ActionListener on the buttons), which works just fine.
LevelPanel's start() method initializes values for the new JPanel and displays the countdown on screen (currently with the following code, although I messed with putting something like this in the newLevel() method instead) before displaying the actual game:
try {
Thread.sleep(1000);
//update countdown number
validate();
repaint();
Thread.sleep(1000);
//update countdown number
validate();
repaint();
Thread.sleep(1000);
//update countdown number
validate();
repaint();
} catch (Exception e) {
System.out.println(e);
}
//start game
I would really appreciate any help getting this code to work, and I'm pretty sure some sort of threading is the way to go but I'm not quite sure where/how. Any suggestions and/or code samples would be great!
Thanks in advance!
EDIT: I ended up rewriting the countdown using a timer instead of Thread.sleep(), which fixed part of the problem and the rest of it I eventually figured out and was entirely unrelated to GUI stuff, which is why I didn't think to check it in the first place.
never really never use Thread.sleep(1000); during EDT, this code caused freeze on GUI is un_resposible, untill a new event invoke EDT or mouse hover over can alive this container too
1) there are two ways how to dealy any event(s) in the Swing GUI, by implements
Swing Timer
delaying by using Thread.sleep(1000); in the SwingWorker
The layout and painting must be done in EDT. Use SwingUtilities.invokeAndWait to call the validate() and repaint()
You can start some code with a time delay using TimerTask:
Timer timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
invokeLater(); // This starts after [delay] ms
// and - if given - will run every [period] ms.
}
}, delay, period);
You could solve your problem with this, though it won't be a pretty solution.
// edit: (see comments) you should synchronize accesses to the gui properly, else it will give you errors.
Ok im really struggling with unlocking my GUI which is locked due to separate SwingWorker thread. Basically what my program does: Initializes webcam, and then grabs single frame and displays it as JLabel icon (doing single grab+display on Button click is ease, however i have immense difficulties in doing such operation consecutively in order to do some image processing). I am aiming to obtain such result:
Grab frame -> process it -> display as ImageIcon of Jlabel > ... repeat Grab frame >...
I need results while webcam is streaming, therefore i used SwingWorker publish and process. In my code "processing part" is not included as it is not necessary since i cant even obtain proper continuous frame grabbing. Generally my background thread will never finish unless cancelled (well thats the assumption as i want to process images as fast as possible with maximum frames per second - unless i should do it other way? - i guess separate thread for single frame grab&process would be bad idea due to fact that im aiming to get 10+ FPS). I know my SwingWorker thread works, since i made tests of saving consecutive images to C:\, and it did work, but my GUI is locked anyway, but at least i know that thread is running and grabbing frames.
Generally i have 2 problems:
no JLabel icon update
Locked GUI
My SwingWorker code:
private class FrameStream extends SwingWorker<Void, BufferedImage> {
#Override
protected Void doInBackground() throws InterruptedException{
BufferedImage processedImage = null;
while (!isCancelled()) {
processedImage = getPIC_COLOR(player);
publish(processedImage);
Thread.sleep(5000); // i made such delay to check whether program is not "choking" with data, but its not the case, without this delay everthing is the same
}
return null;
}
#Override
protected void process(List<BufferedImage> mystuff) {
Iterator it = mystuff.iterator();
while (it.hasNext()) {
img_field.setIcon(new ImageIcon(mystuff.get(mystuff.size()-1)));
}
}
#Override
protected void done() {
infoBAR.setText("FINISHED");
}
}
I am so desperate that i rewritten my code completely basing on tutorials example: Flipper as you can see it is ridiculously similar. All my previous efforts also locked gui so tried the 'tutorial way', nonetheless it did not work, which is a pity. Im in a dead end because i have no clue how to fix that. Im desperate for help since as you can see it seems exactly the same as the tutorial one, maybe something other causes issues: MY FULL CODE
Please help, i'm unable to solve it by myself.
One thing that looks a little different to me is your process method. Rather than specifying the last image, you might wish to iterate through all images like so:
#Override
protected void process(List<BufferedImage> mystuff) {
for (BufferedImage bImage : mystuff) {
img_field.setIcon(new ImageIcon(bImage));
}
}
I'm not sure if this will make a significant difference, but I believe that this is how process should be written. Another thought -- if the BufferedImages are large you may wish to create the ImageIcon in the background thread and publish the ImageIcon rather than the BufferedImage.
Nothing stands out as an issue. Steps I would suggest to further debug.
Instead of using a loop and the process method, just pull one frame and set the Icon once. You need to get that working before you try to make it loop. Your application should be able to set the icon that fact that it doesn't highlights a problem.
Second, because your GUI is locking up, there is something still happening on the EDT. Try, running a profiler or adding System.out.println in your code to find what is running while the GUI is locked.
Last, make sure you don't need to be repainting, revalidating the Label's container to make the image visible.
hmmmm, if I read your last post about this issue, there are maybe lots of possible mistakes (and I see your code), that's nothing to do with SwingWorker, another example just replace change Color for JButton#setBackground() with your Icon or ImageIcon
Have you tried to set the label to observe the image icon?
icon.setImageObserver(label);