How do I implement a good game menu in Java? - java

I have been working on a Java game for a while that I started back in my college days. After taking considerable breaks due to my professional career, I'm getting back to it and realizing how awful the code is. I basically had a "monolithic main" with both GUI and game logic in the same class file. What I'm attempting to do, is abstract and "componentize" the GUI into smaller chunks. My executed class extends JFrame which I add and remove panels to/from when buttons are clicked. Ex.
public class GameFrame extends JFrame{
private JPanel[] _panels = {new MainPanel(), new OptionsPanel(), new DifficultyPanel()};
private int _currentPanelIndex = 0;
public GameFrame(){
...
add(_panels[_currentPanelIndex]);
...
}
}
Within each panel class, I have private ActionListeners for the buttons. In MainPanel, I'm getting the singleton GameFrame instance and setting the current panel kind of like:
class SinglePlayerButtonListener implements ActionListener{
public void actionPerformed(ActionEvent e){
GameFrame instance = GameFrame.getInstance();
JPanel[] panels = instance.getPanels();
int currentPanelIndex = instance.getCurrentPanelIndex();
instance.remove(panels[currentPanelIndex]);
int newPanelIndex;
for(newPanelIndex = 0; newPanelIndex < panels.length; newPanelIndex++){
if(panels[newPanelIndex] instanceof DifficultyPanel){
break;
}
}
instance.add(panels[newPanelIndex]);
}
}
I do not like this deep of coupling in my classes, but I can live with it. However, the real problem is that when I click the last button that takes me into the game, I can't have:
public void actionPerformed(ActionEvent e){
GameFrame.getInstance().startGame();
}
because this will cause my game to run in the Java Event thread, thereby disallowing any other events from being handled. Previously, I had a busy while loop:
...
while(waiting){
//Do nothing
}
remove(_panels[_currentPanelIndex]);
startGame();
...
...
public void actionPerformed(ActionEvent e){
waiting = false;
}
What is the best way to let my GameFrame class know that I've clicked the play button? Can I send an event or message to it from one of its sub-panels telling it to start, and if so, how do I keep the game from running in the Event thread? Should I handle it in the actionPerformed() method and simply spawn a new thread? I would prefer to keep it in the main thread. Thanks.

Go for Slick 2D (a lightweight OpenGL 2D game development framework with lots of cool stuff) or Nifty-GUI.
Slick has also some examples and tutorials and is easy to learn, for example the StateBasedGame can easily be used to implement multiple in-game menus (such as main menu, settings menu, in-game options, in-game pause/resume etc.)

You should have the game logic executing on it's own thread of execution, possibly when the user clicks a button. I don't understand your prior approach with the while(wait) - that would still create problems if your game logic did some time consuming calculation that prevented the even thread from doing updates because the even thread does not stop executing when you call startGame().
EDIT:
It's not clear from the code you pasted that the GUI updates and the game logic are happening on seperate threads of execution - you might want to demonstrate that in your code.

Related

Java-chess game, data exchange from GUI to other classes

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);
});

Lag before JFrame event handlers are added?

I'm working on a simple Java swing project. This is the code of the main class (name changed):
public class MainProg
{
private static MainProg program;
//mainWin is a JFrame
private MainWindow mainWin;
//Event handler class which extends MouseAdapter
private TrayManager trayMgr;
public static void main(String[] args)
{
program = new MainProg();
}
public MainProg()
{
mainWin = new MainWindow();
trayMgr = new TrayManager();
mainWin.startBtn.addMouseListener(trayMgr);
mainWin.setVisible(true);
}
}
As is clear, when the program starts, in main() it creates a new instance of the MainProg class, which then calls the constructor. In the constructor, it creates a new instance of the JFrame mainWin. It then attaches an event handler to a button on mainWin.
In the event handler class trayMgr, the only method is mouseClicked() which does nothing
except a System.out.println('Clicked');
The issue is, when I run this program in Netbeans, the JFrame is shown right away, but I seem to have to click the button 2-3 times before the message is printed in the console.
Is this just something specific to Netbeans, or do I have to change something to make the event handler be set before the window is made visible?
Your threading issue is not likely one that is causing your current problem, but there's the theoretic potential for problems, and I've seen some real problems associated with some of the more touchy look and feels. Quite simply you should queue your code that starts your GUI onto the Swing event thread. You do this by doing:
public void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(
public void run() {
program = new MainProg();
}
));
}
Someone else recommended using invokeAndWait(...) instead of invokeLater(...) but this can be risky especially if you inadvertently make this call from within the Swing event thread itself. For your situation you're better off using invokeLater(...).
But again, I think the main problem with the code you have shown was inappropriate use of MouseListener where an ActionListener should have been used. Learning to code any GUI library can be quite tricky, and for that reason, you can't assume anything. Check out the tutorials and learn from the experts. Also if you are considering coding Swing for the long haul, consider ditching the NetBean's code-generation utilities and learn first to code Swing by hand. You won't regret doing this.
Since you asked, the code I posted here is a Java SSCCE on a different topic. invokeLater is a way of running computations on the EDT. (There is also invokeAndWait, which would work fine here, but under some other conditions can cause a deadlock.)
In fact this example is perhaps a bit over-conservative. Some references say you can run Swing from the main thread the call to show() or setVisible(). However I have a program that misbehaves under Java 7 when I try that.

GUI threading in java

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.

Design for a JApplet animation using Swing and Threads

I'm trying to implement a small Applet that does some animation. I would like to do the animation in a JPanel and below the Animation JPanel, have play/pause, and skip buttons to control the animation. To do this correctly, I think I need to have the animation in a Thread. (correct?)
My Questions are: Whats is a good way to organize my Classes for this application? How to I get my animation thread to run inside a JPanel?
I've been looking in the tutorials at oracle.com and this is what I have so far...
Class AnimationApplet extends JApplet {
...
public void init(){
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
createGUI();
}
});
} catch (InterruptedException ex) {
Logger.getLogger(TabApplet.class.getName()).log(Level.SEVERE, null, ex);
} catch (InvocationTargetException ex) {
Logger.getLogger(TabApplet.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
private void createGUI() {
AnimationPanel ac = new AnimationPanel();
this.setConentPane(ac);
}
} // Class AnimationApplet
class AnimationPanel extends JPanel implements ActionListener {
...
}
Edit: I don't know if this will help, but here's exactly what I'm trying to do. I'm creating a guitar tab player so it would need to continue sliding the notes across the screen and then at certain points (16th notes), check if there are notes to animate. This seems kind of difficult because you can't divide ms by the beats per minute evenly. So for now, I'm thinking of dividing it into 4 or 5 speeds (slowest, slow, medium, etc);
Thanks,
Nate
No, you don't need to use Threads directly since you could simply use a Swing Timer to drive the animation (though understanding that this uses threading behind the scenes). I would have my animation component extend JPanel as you're doing and draw in its paintComponent method. I would avoid having any gui or "view" class extend a listener class as you're doing above as that's making a single class do too much. I'd either use anonymous inner listeners, or private inner class listeners or if complex separate stand-alone listener classes.
Another caveat -- make sure that your paintComponent method does painting and painting only, that it contains no program logic and avoid creating classes or reading in images from within this method. It needs to be lean and fast.
I like javax.swing.Timer for animation; here's a simple applet example.

Threads: Just what is it that makes them confusing? Two Runnables with Mouse Listener

I have a JWindow and a JFrame both I have made runnable and both implement a mouse listener.
I have alot of testing to do for a project of mine and to simplify it I wish to be able to automate most of it, so I have starting my own mouse recorder and replayer (uses Java Robot Class).
Kinda like a simplified AutoHotKey or AutoIt thing... but it'll run on my Ubuntu machine aswell as my Windows one!!!
The JWindow I have made is translucent and covers the entire screen, when you click on it it disappears and replays the click to the object behind and then reappears. This is the recording process. When the User right clicks the is set to not visible and the recorded actions are replayed.
During replay I want the option to be able to exit the entire application and so I though the best way to do this would be to make the JFrame and the JWindow Runnable.
The JFrame is simply their to offer a close option from the application.
So, in my Main class I have
public static void Main(String[] args){
recorder = new Recorder();
gui = new GUI();
Thread tr = new Thread(recorder);
Thread tg = new Thread(gui);
tr.setName("Recorder");
tg.setName("GUI");
tr.start();
tg.start();
}
My understanding is Recorder and GUI are runnable objects and they are made into threads via the new Thread command. When I use .start() I am beginning the execution of the thread and from here on the system decides which thread is running at any particular time.
Onto the Recorder and GUI classes.
public class Recorder
implements Runnable, MouseListener {
//Constructor and other code
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
//Record events
}else{
//Replay events
}
System.exit(0);
}
public void run() {
System.out.println("Recorder");
}
}
public class GUI
implements Runnable, MouseListener {
//Constructor, simply constructs JFrame and sets mouselistener to it
public void mouseClicked(MouseEvent e) {
System.exit(0);
}
public void run() {
System.out.println("GUI");
}
}
My application prints out Recorder and then GUI
Allows me to record my events
then right click on the JWindow to replay them...
but then when I click on the JFrame's close button or even in the frame because of the mouse listener it won't exit until all of the actions have been fully replayed?
One thing I did wonder is what ever I put in run is what is what keeps the thread running? So when System.out.println(""); is executed the thread dies? So I tried a while loop around them and my application successfully prints
GUI
GUI
GUI
RECORDER
RECORDER
GUI
RECORDER
etc
etc
So I can see that they threads are running concurrently... it's just that all of the other actions outside of the run don't seem to get executed... How can I include my mouse listener, etc within the threads execution?
You are confusing Threads with Objects. When you have an Object that is a Runnable that just gives a starting point for a Thread. However this doesn't mean that when another thread (in this case the Event thread that handles the MouseListener) calls a method in your Runnable that it executed by the thread that is executing the Runnable. When a method is called, it never switches to another thread. If you want this you need a mechanism. For instance a queue that the MouseListener can post tasks to, and in your Runnable.run() you then keep looking for a new task.
When swing initializes it starts it's own Event Dispatch thread. All your listener methods executes within this thread. It doesn't matter whether or not your listener object implements runnable.
See this tutorial to grasp the basics of multi-threading in context of Swing. http://java.sun.com/docs/books/tutorial/uiswing/concurrency/index.html
The actual answer to your question is in this part of the tutorial:
http://java.sun.com/docs/books/tutorial/uiswing/concurrency/cancel.html
But I suggest that you go through the whole tutorial.

Categories