I am currently making this text-based rpg game with a simple GUI. I was happy to find the good start at it but then there is something I stumbled upon that made me stop coding for a while.
If you are making this in console, you could easily use this code to pause the movements of the characters for a while, like:
System.out.println("[enemy]");
Thread.sleep(1000);
System.out.println("The local guard waves his sword and tries to stab you in the back, but you quickly parried and tried for a counterattack but you failed.");
If you are doing this on a JTextArea, you'd use the setText but
if you use Thread.sleep it doesnt work and coding setText again, would erase the old text and replace it with the new text, so the records of the fight will not be fully displayed on the game. Is there a way to fix this?
You can use append to append instead of replace. That is the easy part.
The hard part: You have to change your program flow. In Swing there exists a single thread for dispatching GUI events, the event dispatching thread. You should not set the EDT to sleep or do any other long-running operations in it. This will freeze the GUI, it can't respond to anything and will not repaint.
Instead you should either start a new thread for the logic flow and dispatch operations that have to be executed on the EDT (everything that manipulates the GUI) with SwingUtilities.invokeLater or, in this case maybe better, SwingUtilities.invokeAndWait.
Or you should embrace an event driven control flow, e.g. you could use a Timer to output the second text later.
A program flow that works well with single-threaded console programs is not the right approach for multi-threaded GUI applications (and every GUI application is automatically multi-threaded).
for the setText part, you should have a variable which will hold the text and when you want to add a string, you append it and set the text again:
String text ="[enemy]";
textfield.setText(text);
text+= "\nblablabla ..";
textfield.setText(text);
UPDATE:
Some are suggesting to use the append method which is relatively good. Sometimes in the game you would like to append and sometimes replace the whole text (a new character talks), I would recommend something like this:
textfield.setText("[enemy]\n");
textfield.append("blablabla");
//When someone else wanna talk:
thread.Sleep(1000);
textfield.setText("[me]\n");
textfield.append("moreblablabla");
You could also use append() function. See JavaDoc.
jTextField.append("Foo\n");
jTextField.append("Bar\n");
You can use append() instead of setText(). Method append() will append new text at the end of the old text.
Related
I'm currently programming a game in java and am using an interface to handle input/output for the game. I currently have a text interface working properly. I'm using code similar to the following:
while (moveExists())
{
String in = interface.getInput();
processInput(in);
interface.displayOutput(this.getState);
}
The text only interface works because it pauses to wait for input, but I am not sure how to accomplish a similar behaviour in a GUI implementation. How may I 'wait' for input from an actionListener?
If not, I'll probably use code less like a game loop and more like a finite state machine so that I don't need to deal with two different threads trying to co-ordinate their actions.
You could simply start a GUI (that it has its own separated thread) containing a text area and maybe a button or something like that, then you add an ActionListener to the text area or button and then you execute the code you need when the Listener is triggered (i.e. some code has been inserted or button clicked).
Another story if you have also another 'background' thread that needs to run in a loop...
I created a text-based game similar to Zork and I need a gui to run it outside of Eclipse. I want to run it as a jar. (by the way I'm on a mac if that changes anything). I only need an output field and an input field. What would be the easiest way to achieve this?
And how much of my code would I need to change? (I used System.out.print for output and a Scanner for input)
If you want to crate GUI like console the simple way to do it is to add textarea component to your frame or or panel that has scroll bars through the viewport. Create a stream that feeds the component with text. Then simply redirect standard output to that stream. Finish. Start your GUI and enjoy the console.
If you don't want to run this on a terminal, you should probably use Swing with a JTextArea in which you append all the messages to the user, and a simple JTextField for the user to enter his commands.
Here's a quick example of JTextArea so you get an idea. You'll need to read more about events on Swing to make things like reacting to the user pressing the ENTER key to read the contents of the text field and run the game logic.
Note that the screenshot on the example above uses the "Metal" look and feel, but it should look much closer to a native application on the Mac.
I am writing a socket programming. It has GUI for server and client. In the server GUI there is a textfield which shows the word requested by user.
But I am having problem in showing the word.
I have tried
txtWord.setText(sentword);
It is not showing the word in the textfield. But when I write this
txtWord.setText(sentword);
JOptionPane.showMessageDialog(null, "the requesed word is: "+sentword);
then it shows the word in textfield and also shows it in the messagebox.
I have tried repaint() but it dint work.
Please suggest me some solution as soon as possible
as #Binyamin Sharet correctly commented, you have a Concurrency in Swing issue.
your Swing GUI doesn't care about long and hard tasks you're running in the background
even JTextField#setText() is declared as thread safe, output from Socket (i.e.) by default never notified Event Dispatch Thread
correct way could be to use a SwingWorker that has been created specifically to run long and hard tasks background to the Swing GUI and output to the GUI on event thread or EDT
or even easier is to use a Runnable in a Thread but making sure that all output to the Swing GUI is queued on the Swing event thread by placing it in a Runnable and calling it with invokeLater()
A dirty hack is to wrap code lines like so:
txtWord.setText(sentword);
JOptionPane.showMessageDialog(null, "the requesed word is: "+sentword);
into invokeLater(), but in this case your GUI will be unresponsive to Mouse or Keyboard events until Socket (in your case) ended
txtWord.requestFocus();
textField does not show up until the window is over the textField and back or it gains focus, until Clicking on it. So... just request focus.
Also if check the text size if you had set while creation.Sometimes text not displayed if there is mismatch in size
eg: txtWord.setSize(200, 24);
I have a simple program that utilizes Java Swing Timer to display an image for 400 miliseconds, in this period of time I just want to stop all ActionListeners or stop taking ActionEvents. I've got 40+ buttons and want a simple way to do this.
Is there anyway to do that in Java?
Can you determine that you are in this "image displayed" state? The image goes up and you set the state to "image displayed" or whatever. Go through your widgets and decide which ones are supposed to be dead while the image is up. Turn them into Observers of this state value. When the state changes, they either enable or disable, as appropriate. The image code doesn't do anything directly to any widget. It just declares that the state is now "image displayed". It's up to the Observers to decide what to do, if anything, with that information.
Or use the GlassPane. That works too. Of course, the GlassPane shuts down everything. If you need to be more selective, you need a more fine-tuned approach.
You can use a temporary GlassPane instance to consume all events by registering empty listeners to it.
Use an undecorated modal JDialog to display the image. Before you make the dialog visible you would start a Timer. When the Timer fires in 400 ms you close the dialog.
I've had similar issues and typically found that its a design issue that got me in that situation. Being the case, I still had to find away around it. To fix the issue, I kept a list of the elements that I wanted to disable (stop listening) and iterated through them at the beginning and end of the timer. For buttons it should be as simple as:
for(Component c : listOfToggledComponents){
c.setEnabled(shouldItBeEnabled);
}
For buttons, this will grey out the button. Similar things happen to other swing components.
I'm having a problem..
In my code I have it so that text should output to a JTextField. when I run the program, it doesn't. However, if I directy after my code for putting text into the JTextField put a JOptionPane then it works...
Anyone have an solution to make the JTextField update without having the JOptionPane after?
My code:
// Works:
JTextField.setText("String");
JOptionPane.showMessageDialog(null, "String");
// Doesn't Work:
JTextField.setText("String");
//JOptionPane.showMessageDialog(null, "String");
There are two reasons why this might fail:
You're calling setText() from outside the main (Swing) thread
You're calling setText() from in the main (Swing) thread
In the first case, wrap the call in SwingUtilities.invokeLater().
In the latter case, you set the text but you're blocking the Swing thread, so the change can't be rendered. You will need to create a background worker to do the work and use SwingUtilities.invokeLater() to update the text field from your worker thread.
[EDIT] See the Swing tutorial for an example how to use background thread and how to update the UI from there: http://download.oracle.com/javase/tutorial/uiswing/concurrency/interim.html
Why not just use repaint() after the text update?
If memory serves me right that should address the issue of the text not appending.