For loop stalls my swing application, until the loop completes - java

*Problem solved - thanks for all your answers, they were very helpful!
I am making a small dice game for a school assignment, and i came across this problem.
I want to simulate the rolling of a die, by quickly cycle trough a number of die-icons.
This by itself is not what is causing the problem though.
If i make the "animation" directly in a JFrame it displays correctly. I have done that in the code below:
public class Example{
private static ImageIcon die1 = new ImageIcon("terning1.jpg");
private static ImageIcon die2 = new ImageIcon("terning2.jpg");
private static ImageIcon die3 = new ImageIcon("terning3.jpg");
private static ImageIcon die4 = new ImageIcon("terning4.jpg");
private static ImageIcon die5 = new ImageIcon("terning5.jpg");
private static ImageIcon die6 = new ImageIcon("terning6.jpg");
private static JLabel die = new JLabel(die1);
private static Random generator = new Random();
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(die);
frame.pack();
frame.setVisible(true);
for (int i = 0; i < 100; i++) {
int x = generator.nextInt(6) + 1;
switch(x){
case 1 : die.setIcon(die1);
break;
case 2 : die.setIcon(die2);
break;
case 3 : die.setIcon(die3);
break;
case 4 : die.setIcon(die4);
break;
case 5 : die.setIcon(die5);
break;
case 6 : die.setIcon(die6);
break;
}
//Make the loop wait for 50 millis
long a, b;
a = System.currentTimeMillis();
do {
b = System.currentTimeMillis();
} while ((b-a) < 50);
}
}
}
Now that works fine, but obviously it only works when i first open the JFrame. So i want to add a button, that makes the die roll.
But if i add a JButton with a actionlistener, and put the for-loop in the actionPerformed method, it stalls the program until the loop has finished, and only shows the last die in the loop.
Example:
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout());
frame.add(button);
frame.add(die);
button.addActionListener(new ButtonListener());
frame.pack();
frame.setVisible(true);
}
private static class ButtonListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
for (int i = 0; i < 100; i++) {
int x = generator.nextInt(6) + 1;
switch (x) {
case 1:
die.setIcon(die1);
break;
case 2:
die.setIcon(die2);
break;
case 3:
die.setIcon(die3);
break;
case 4:
die.setIcon(die4);
break;
case 5:
die.setIcon(die5);
break;
case 6:
die.setIcon(die6);
break;
}
//Make the loop wait for 50 millis
long a, b;
a = System.currentTimeMillis();
do {
b = System.currentTimeMillis();
} while ((b - a) < 50);
}
Any tips on how to solve this?
Thanks beforehand!

What's happening is that the "event dispatch thread", where all the Swing events happen, is having to wait for your code. Don't do long-running stuff on the event dispatch thread. This is a famous anti-pattern.
You should read the lesson from the Java tutorials, that starts at http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html, which describes this.
Two small points that are unrelated to your problem:
Your code will be much more manageable if you use an array of ImageIcon variables, instead of six separate variables.
You could use the sleep method of the Thread class instead of the "busy sleep" that you're using.

Your UI is locked because everything is in the same thread so it has to wait until the for loop is done executing.Your going to need to run your logic in a separate thread.

You run your actionPerformed(ActionEvent event) method in EDT because of that you can't update UI. For updating UI from your code try to use SwingWorker, it can update UI while background process running. You can found a lot of examples of it in the internet.
Or you can try to use Executors for background process and updating UI from EDT.

Swing's event handling code runs in EDT(even dispatch thread). The ActionEvent is also no exception. You can't do any long running task inside EDT. Put your dice rolling loop inside a new thread:
new Thread(){
public void run(){
// your dice rolling code
}
}.start();
and then update the GUI using SwingUtilities.invokdeLater().
Note: Instead of conditioning with switch-case you just could use an array of ImageIcon and access it with random generated index.
int x = generator.nextInt(6) + 1;
die.setIcon(imageIconArr[x]); // array of image icon
See this answer for details.

Related

How to sum the similar threads inside ExecutorService in java?

I'm trying to sum the similar threads inside the ExecutorService. I think I should use join() method, but I wasn't able to do it correctly.
My Code:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class Gui extends JFrame implements ActionListener{
private JTextField txtSeats, txtAgents, txtTime;
public int numFirst, numSecond, numThird;
private JTextArea [] bookSeat;
private JButton btn1, btn2;
private String output, name;
private Random ran;
private JPanel p1, p2;
private String [] agentNumber;
private int [] sum;
private int x, z;
public Gui() {//A constructor for the Gui class.
ran = new Random();//Initializing the Random class.
setLayout(new BorderLayout());//Setting the layout for the JFrame.
setTitle("");//the title of the program.
p1 = new JPanel();//Creating the first JPanel to add JButtons and JTextField on it.
p1.setLayout(new FlowLayout());//Setting the first JPanel's layout.
//Creating 3 JTextField with a title for each, and adding each of them to the first JPanel.
txtSeats = new JTextField("Number of seats");
p1.add(txtSeats);
txtAgents = new JTextField("Number of agents");
p1.add(txtAgents);
txtTime = new JTextField("Max waiting time");
p1.add(txtTime);
//Creating 2 JButton with a title for each, and adding each of them to the first JPanel.
btn1 = new JButton("Create seats");
p1.add(btn1);
btn2 = new JButton("Book");
p1.add(btn2);
//Registering the 2 JButton to the ActionListener so they work with the actionPerformed() method when they get clicked.
btn1.addActionListener(this);
btn2.addActionListener(this);
add(p1, BorderLayout.NORTH);//Adding the first JPanel to the main JFrame at the position NORTH.
//Giving some properties to the JFrame layout.
setExtendedState(JFrame.MAXIMIZED_BOTH);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setResizable(false);
}//End of the constructor.
public void createSeat() {//A method for creating a number of empty seats depending on the users wish.
//Storing the value of the first JTextField into an integer variable.
//With removing the spaces in it if there is any using "trim()".
numFirst = Integer.parseInt(txtSeats.getText().trim());
//Creating a JTextArea array for the number of seats the user wants to add.
//The size is entered by the user.
bookSeat = new JTextArea[numFirst];
p2 = new JPanel(); //Creating the second JPanel to add JTextArea on it.
p2.setLayout(new FlowLayout()); //Setting the second JPanel's layout.
//for-loop for adding the new JTextArea array into the second JPanel.
//With setting their title and background color and other properties.
for(int i = 0; i < numFirst; i++) {
bookSeat[i] = new JTextArea("Not booked");
bookSeat[i].setBackground(Color.WHITE);
bookSeat[i].setEditable(false);
p2.add(bookSeat[i]);
add(p2, BorderLayout.CENTER);//Adding the second JPanel to the main JFrame at the position CENTER.
setVisible(true);
}
}//End of createSeat() method.
#Override
public void actionPerformed(ActionEvent e) {//A method Overrode from the ActionListener interface.
if(e.getSource().equals(btn1)) {//First case: The JButton "Create seats" is clicked.
//Calling the createSeat() method; to create an empty seats based on the entered number of seats in the first JTextField.
createSeat();
//This method called on a container once new components are added or old ones removed.
//This call is an instruction to tell the layout manager to reset based on the new component list.
//revalidate() will trigger a call to repaint what the component thinks are 'dirty regions.'
//https://stackoverflow.com/questions/1097366/java-swing-revalidate-vs-repaint
revalidate();
}
else if(e.getSource().equals(btn2)) {//Second case: The JButton "Book" is clicked.
//Storing the values of the second and third JTextField into 2 integer variables.
//With removing the spaces in them if there is any using "trim()".
numSecond = Integer.parseInt(txtAgents.getText().trim());
numThird = Integer.parseInt(txtTime.getText().trim());
//Creating an ExecutorService object with fixed thread pool with maximum number of agents threads.
ExecutorService executor = Executors.newFixedThreadPool(numSecond);
//for-loop for the number of times the ExecutorService (thread pool) should run.
//It will keep creating threads until it reaches the maximum number of seats.
for(int i = 0; i < numFirst; i++) {
int count = i;
//Submitting Runnable task to the executor.
executor.execute(new Runnable() {
#Override
public void run() {//A method Overrode from the Runnable interface.
try {
//Getting the name of the current thread & store it in a String variable.
//Then we will have a long name like: pool-1-thread-n (n is a changing thread number).
//We use split("-"); to split it to 4 parts and we take the 4 part which is in position [3].
name = Thread.currentThread().getName();
agentNumber = name.split("-");
//Setting the new text and background color to the JTextArea array after they get booked.
bookSeat[count].setText("Booked by Agent " + agentNumber[3]);
bookSeat[count].setBackground(Color.RED);
revalidate();
//Generating random number between 0 and the waiting time entered by the user.
//And inserting this value inside the sleep() method. For a waiting time between each thread.
x = ran.nextInt(numThird + 1);
Thread.currentThread().sleep(x);
}
catch (Exception e) {}
}//End of run() method.
});//End of the executor.
}//End of the for-loop.
//Temporary solution for finding the similar elements in the array and return the sum for each
//equal threads. BUT NOT WORKING!!!
z = 0;
for(int i = 0; i < numFirst;i++) {
for(int j = 0; j < numFirst; j++) {
if(bookSeat[i].getName().equals(bookSeat[j].getName())) {
sum[i] = z + 1;
}
}
}
//for-loop for storing the booked seats in a String variable.
output = "";
for(int i = 1; i <= numSecond; i++) {
String allAgents = String.valueOf(i);
output += ("\n" + "Agent " + allAgents + " booked " + sum + " seats.");
}
//Displaying a Message Dialog with the information of each agent's booking.
JOptionPane.showMessageDialog(this,output);
//Shutdown the executor.
executor.shutdown();
//Wait until all tasks are finished.
while(!executor.isTerminated()) {
System.out.println("The booking has finished");
}
}//End of else if condition.
}//End of actionPerformed() method.
public static void main(String [] args) {//Main method.
new Gui(); //Calling the Gui.
}//End of Main method.
}//End of the class.
As you may see, I have tried with doing a nested for loop for checking the elements in the array if they were similar and return the sum of them. But this still didn't work!
z = 0;
for(int i = 0; i < numFirst;i++) {
for(int j = 0; j < numFirst; j++) {
if(bookSeat[i].getName().equals(bookSeat[j].getName())) {
sum[i] = z + 1;
}
}
}
Using Executor you loose direct access to the Thread instance which is needed for waiting for it (e.g. join()).
First create an array of Threads, then start them, then join() them. E.g.:
int numSecond = 3;
Runnable r = new Runnable() {
#Override
public void run()
{
// do your stuff inside thread
}//End of run() method.
};
IntStream.range(1,numSecond + 1).forEach( i -> {
Thread t = new Thread( r, "pool-1-thread-" + i);
t.start();
try
{
t.join();
}
catch ( InterruptedException t1 )
{
System.err.println( "thread interrupted" );
}
});
Above example names the thread as you expect in your code:
"pool-1-thread-" + i
If you are only interested in the numer then just pass in
"" + i
Using CompletableFutures might solve the problem also in an elegant way.
BTW1: Using a sleep(x) at the end of the thread's run method is absolutely senseless. It does not delay the start time of another thread. Why at all you want to do this? If you want to execute the logic in run() in a sequential way, there is no need to create use threads at all.
BTW2: Next time you asking something please boil down your code to a minimum which shows your problem. There is no need to setup a whole GUI with buttons, klick handlers etc.
BTW3: The access to shared (global) variables from inside the threads must be synchronized.

Coin flip simulation: Counting heads / tails

I am trying to simulate a coin flipper using Java and window builder. I have a button called "Flip" when that button is pressed the coin image changes depending on which number the random generator I created generates.
I now am trying to figure out a way to display the number of times the coin lands a heads or a tails in their respective JTextFields. I was thinking of using a counter, but I am struggling with how to update that into the text field, so far it only puts in that I have flipped each coin once.
I am very new to programming so any advice or guidance is much appreciated.
// this button flips the coin
btnFlip = new JButton("Flip");
btnFlip.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
int headCounter = 0;
int tails = 0;
// this implements the random flip of the coin when the checkbox Run Multiple
// flips is unchecked
if (chckbxNewCheckBox.isSelected() == false) {
Random r = new Random();
int flipper = r.nextInt(2);
if (flipper == 1) {
lblImages.setIcon(new ImageIcon(FinalPrep.class.getResource("/finalPrep/heads.png")));
textFieldHeads.setText(String.valueOf(headCounter));
} else {
lblImages.setIcon(new ImageIcon(FinalPrep.class.getResource("/finalPrep/tails.png")));
textFieldTails.setText(String.valueOf(tails));
}
}
}
});
btnFlip.setFont(new Font("Tahoma", Font.PLAIN, 15));
panel_1.add(btnFlip);
You have to create something like 2 layers. One that contains the window itself along with the title (think of it like a receptor) then you have to create another "layer" that contains the buttons and the textfields. Something like this:
import javax.swing.*;
public class MyFrame extends JFrame {
private JPanel panel;
private JTextField textField;
private JButton button;
public MyFrame(){
panel = new JPanel(); //Step 1. Creation of a receptor
tfCount = new JTextField(10); //Step 2.
button = new JButton("Press Me"); //Creation of buttons & textfields.
panel.add(tfCount); //Step 3.
panel.add(button); //Add those graphics to the receptor
this.setContentPane(panel); //Step 4 Adjust the receptor to the object
this.setVisible(true);
this.setSize(400, 400);
this.setTitle("My 1st GUI!");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
(Sorry for the random example but you did not post any sample of code so I am trying to explain as best as I can)
Then you should put a "flag" depending of the number that was generated for example:
boolean flag = true; //Flag
int counter = 0; //The counter for the heads
int x; //The number you received from the generator
int counter_b; //The counter for the tails
if(x%2 == 0){ //If the number is even
flag = false; // You set down the flag
counter++; //And counter raises its value
}
else
counter_b++;
tfCount.setEditable(true); //This makes your textfield editable
tfCount.setText(String.valueOf(counter)); //And this prints the counter's value
Keep in mind that the counters for the tails and the heads are just examples for you to understand how to use "flags". I hope that I was able to help you because I am new too!! :)

Basic GUI java program in Eclipse keeps crashing

I am trying to make a game and in said game, there are 21 sticks and each person takes turns taking 1-4 sticks until there are no sticks left, if you cant take anymore sticks you lose. I have successfully made this program in eclipse but now I want to add GUI to it so I have to change the code. This code isn't complete but it crashes whenever I press the Go button which is my actionListener. I would type in a number to the text field, press go and it will just crash. How can I fix this?
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Sticks extends JFrame {
JButton Go;
JTextField tf1, tf2;
static JTextField sttf;
JLabel startTake;
static JLabel errorTake;
JLabel uTake;
JLabel compTake;
public Sticks() {
setLayout(new GridLayout(5, 2, 5, 5));
startTake = new JLabel("How many sticks do you want to take? (1-4)");
add(startTake);
sttf = new JTextField();
add(sttf);
errorTake = new JLabel("Hello");
add(errorTake);
Go = new JButton("Go");
add(Go);
uTake = new JLabel("");
add(uTake);
compTake = new JLabel("");
add(compTake);
// tf1 = new JTextField();
// add(tf1);
// TakeP = new JLabel("One stick taken");
// add(TakeP);
event e = new event();
Go.addActionListener(e);
}
public static class event implements ActionListener {
public void actionPerformed(ActionEvent e) {
int numSticks = 21;
int numToTake = 0;
int randomNum = 0;
while (numSticks > 0) {
try {
int num = (int) (Double.parseDouble(sttf.getText()));
int NumSticks = numSticks - num;
errorTake.setText("There are: " + numSticks + " left");
Robot Rob = new Robot();
numToTake = (int)Math.random() * 4 + 1;
errorTake.setText("There are: " + numSticks + " left");
}
catch (Exception ex) {
ex.printStackTrace();;errorTake.setText("There is a problem");
}
}
}
}
public static void main(String[] args) {
Sticks gui = new Sticks();
gui.setDefaultCloseOperation(EXIT_ON_CLOSE);
gui.setVisible(true);
gui.setSize(600, 200);
gui.setTitle("Nice Game");
}
}
Everytime you click the "Go" button, your actionPerformed fires, and it doesn't wait for user input at all. This is your problem line.
public static class event implements ActionListener {
public void actionPerformed(ActionEvent e) {
//...
int num = (int) (Double.parseDouble(sttf.getText()));
//...
}
}
sttf.getText() always returns "" because sttf is empty, the program doesn't wait for user input unlike Scanner(System.in).
Makes sense that you don't get any Exceptions because it just runs and finishes the game without giving the user enough time to input anything. Are you sure the console doesn't print "Numbers only!", though? Because I've never tried to parse an empty String before.
Okay I've been reading this all wrong, sorry.
Your actionListener generates a new game everytime you click it, because you set your numSticks to 21 at each click. Looking forward, I don't think that's a good idea unless you want to take the same amount of sticks the whole way until the game ends. Same thing stands. If you input a value in sttf, the program won't wait for you to change it because it'd keep using that value until your while loop ends.

GUI hangman game- how can I get these different parts to work together?

I have (almost) working pieces of the game, but they don't work together. I am a complete beginner so I don't know how to work out where the problem is.
I have 3 classes:
GameControls has the main functionality of the game in it.
Hangman sets up a JFrame, calls the drawHangman method and contains the main method
drawHangman has the instructions for drawing the hangman graphic
I suspect this might not be the best way to do this so if the classes make no sense please let me know (and tell me why!).
Problem
GameControls runs fine on its own. The user can select letters to guess, the display works, and the livesRemaining count goes down if the guess is wrong.BUTif I run from the hangman main method, the windows appear and the controls don't work anymore.
Even though the livesRemaining count decreases, I don't know how to make the drawHangman part use that variable and implement the switch statement to draw the hangman.
My ideas
It seems to me that the way the classes/methods are written/designed might be causing issues (but I don't know how to diagnose and fix this. I have tried!)
I thought that the reason for problem 1 could be that when both windows open the program doesn't know where actions are coming from anymore and/or I need to add more ActionListener stuff.
As I'm so new to programming it is taking me ages to look and this and try out different things, I would really like some pointers. My code is below.
Hangman class
public class Hangman extends JFrame
{
public Hangman()
{
super("You have nine lives...");
setSize(600,600);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
drawHangman draw = new drawHangman();
add(draw);
setVisible(true);
}
public static void main(String[] args)
{
GameControls play = new GameControls();
Hangman game = new Hangman();
}
}
}
GameControls class
import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
public class GameControls extends JFrame implements ActionListener
{
String secretWord = "elephant"; //I'm assuming I will call a method that generates the secretWord
String clueGiven = "animal";
int letterN = 0;
int secretWordLength = secretWord.length();
int livesRemaining = 10;
boolean[] alreadyGuessed = new boolean[26];
JLabel secretWordLabel = new JLabel("Word to guess: ", SwingConstants.RIGHT);
JTextField displaySecretWord = new JTextField(secretWordLength);
JLabel clueLabel = new JLabel("Clue: ", SwingConstants.RIGHT);
JTextField clue = new JTextField(clueGiven, 15);
JLabel guessLabel = new JLabel("Choose a letter: ", SwingConstants.RIGHT);
String[] letters = {"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"};
JComboBox selectGuess = new JComboBox(letters);
JButton submit = new JButton("Submit");
JTextField displayGuessedLetters = new JTextField(15);
public GameControls()
{
super("Hangman Game");
setSize(340,170);
setBounds(600,0,340,170);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLookAndFeel();
selectGuess.addActionListener(this);
submit.addActionListener(this);
displaySecretWord.setEditable(false);
clue.setEditable(false);
displayGuessedLetters.setEditable(false);
//show _ for all the letters of the secretWord
for (letterN=0; letterN < secretWordLength; letterN++)
{
String displayedSoFar = displaySecretWord.getText();
displaySecretWord.setText(displayedSoFar + " " + "_");
}
JPanel pane = new JPanel();
GridLayout display = new GridLayout(4,2);
pane.setLayout(display);
pane.add(secretWordLabel);
pane.add(displaySecretWord);
pane.add(clueLabel);
pane.add(clue);
pane.add(guessLabel);
pane.add(selectGuess);
pane.add(displayGuessedLetters);
pane.add(submit);
add(pane);
setVisible(true);
}
public void actionPerformed(ActionEvent event)
{
/*this works just fine when I run just this part of the program
**but if I run it from the hangman main method it doesn't work.
**I think it might be because then it has an extra window
**so it doesn't know where the action is coming from for these bits here
*/
Object source = event.getSource();
boolean guessInWord;
String prevGuesses = displayGuessedLetters.getText();
char hideLetter = '_';
int guessInt = selectGuess.getSelectedIndex();
String guessChar = (String)selectGuess.getItemAt(guessInt);
if (source == submit)
{
//store word as alreadyGuessed
alreadyGuessed[guessInt]=true;
//check if it's in the word
guessInWord = (secretWord.indexOf(guessChar)) != -1;
boolean wordComplete = false;
if (guessInWord == true)
{
//print out the secretWord with the guessed letters showing
displaySecretWord.setText(" ");
for (letterN=0; letterN < secretWordLength; letterN++)
{
String displayedSoFar = displaySecretWord.getText();
char letterToCheck = secretWord.charAt(letterN);
int letterToCheckIndex = (int)(letterToCheck)- 97;
if (alreadyGuessed[letterToCheckIndex]==true)
{
displaySecretWord.getText();
displaySecretWord.setText(displayedSoFar + " " + letterToCheck);
}
else if (alreadyGuessed[letterToCheckIndex]==false)
{
displaySecretWord.getText();
displaySecretWord.setText(displayedSoFar + " " + hideLetter);
}
}
//check if the word is complete or not
String displayedSoFar = displaySecretWord.getText();
wordComplete = displayedSoFar.indexOf("_")== -1;
if (wordComplete == true)
{
//this bit opens but I haven't finished it yet
youWinPopup win = new youWinPopup();
}
}
else if (guessInWord == false)
{
//I can't figure out how to link this to my hangman drawing part!
livesRemaining --;
System.out.println(livesRemaining);
displayGuessedLetters.setText(prevGuesses + " " + guessChar);
}
}
}
private void setLookAndFeel()
{
try
{
UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
SwingUtilities.updateComponentTreeUI(this);
}
catch (Exception exc)
{
System.out.println("Couldn't use the system " + "look and feel: " + exc);
}
}
/*public static void main (String[] args)
{
GameControls play = new GameControls(); //runs fine from here, without the hangman part
}*/
}
drawHangman class
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
public class drawHangman extends JPanel
{
/**
*
*/
private static final long serialVersionUID = -3924721752542320241L;
public void paintComponent (Graphics comp)
{
Graphics2D comp2D = (Graphics2D) comp;
comp2D.setColor(Color.white);
comp2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
Rectangle2D.Float background = new Rectangle2D.Float(0F,0F,400,600/*(float)getSize().width,(float)getSize().height*/);
comp2D.fill(background);
//setting for colour etc
comp2D.setColor(Color.black);
BasicStroke pen = new BasicStroke(2.0f,BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);
comp2D.setStroke(pen);
int livesRemaining = 10;
switch (livesRemaining)
{
case '9':
Line2D.Float gallowsBase = new Line2D.Float(50F,500F,500F,500F);
comp2D.draw(gallowsBase);
break;
case '8':
Line2D.Float gallowsVertical = new Line2D.Float(150F,500F,150F,100F);
comp2D.draw(gallowsVertical);
break;
case '7':
Line2D.Float gallowsTop = new Line2D.Float(150F,100F,400F,100F);
comp2D.draw(gallowsTop);
break;
case '6':
Line2D.Float rope = new Line2D.Float(400F,100F,400F,150F);
comp2D.draw(rope);
break;
case '5':
Ellipse2D.Float head = new Ellipse2D.Float(362F,150F,76F,76F);
comp2D.draw(head);
break;
case '4':
Line2D.Float body = new Line2D.Float(400F,226F,400F,325F);
comp2D.draw(body);
break;
case '3':
Line2D.Float arm1 = new Line2D.Float(400F,226F,300F,275F);
comp2D.draw(arm1);
break;
case '2':
Line2D.Float arm2 = new Line2D.Float(400F,226F,500F,275F);
comp2D.draw(arm2);
break;
case '1':
Line2D.Float leg1 = new Line2D.Float(400F,325F,300F,400F);
comp2D.draw(leg1);
break;
case '0':
Line2D.Float leg2 = new Line2D.Float(400F,325F,500F,400F);
comp2D.draw(leg2);
/*youLosePopup lose = new youLosePopup();*/
}
}
/*public static void main (String[] args)
{
drawHangman draw = new drawHangman();
}*/
}
livesRemaining is an integer.
When you put it in a switch statement, your cases should be integers not characters.
case 6:
instead of
case '6':
Good luck.

JLabel changing on it's own

I've been working on what should be a relatively simple game, Mormon Sim. The goal is to hold a successful conversation while going door to door. It's pretty simple, on the click of a button, it changes a JLabel's text according to the button and method called. For example, using the Knock button will activate the method knockResponse, which will calculate if someone will come to the door or not. Based on the result of the calculation, someone will answer the door or you will be turned away. The problem is, after a few clicks, usually once the player gets past the knock check, the JLabel begins to revert itself back to other text, and back and forth between a few different things, sometimes completely disregarding the swing timers on them. I really have no idea what is going on here, and I can't really find a reason that this would happen. A whole week of thorough googling has yielded no similar problems or solutions. TL;DR Java has become self aware
import java.util.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.Timer;
//To do: ASCII art in JLabels
public class mainMenu implements ActionListener{
static JButton start = new JButton("Start!"), knock = new JButton("Knock"), talk = new JButton("Talk"), stats = new JButton("Stats");
static int level = 1, stamina = 100, knocking = 1, speech = 1, points = 0;
static JFrame frame = new JFrame("Mormon Sim");
static mainMenu game = new mainMenu();
static JLabel text = new JLabel(), knockSkill, speechSkill, staminaBar;
static JPanel DoorMenu = new JPanel();
JTextField name = new JTextField("Enter your name here", 25);
static String playerName;
public JPanel createStartMenu(){//JPanel for the start menu. Replaced on start click
JPanel startMenu = new JPanel();
JLabel instructions = new JLabel("<html>Welcome to Mormon Adventure, the first text-based mormon sim. Your goal is to <br>hold as many successful door to door<br>conversations as possible. Every successful conversation earns you a<br>skill point and a level. Level 15 is a win!<br>A conversation requires stamina as it goes on, find Orange Crush to replenish your stamina<html>");
startMenu.setLayout(null);
startMenu.setLocation(0, 0);
startMenu.setSize(500, 500);
start.setLocation(200, 300);
start.setSize(100, 50);
start.addActionListener(this);
startMenu.add(start);
instructions.setLocation(100, 100);
instructions.setSize(300, 200);
startMenu.add(instructions);
name.setSize(150, 25);
name.setLocation(100, 50);
name.addActionListener(this);
startMenu.add(name);
startMenu.setOpaque(true);
return startMenu;
}
public JPanel createDoorMenu(){//used for knocking, speaking, and going to the stat page. The problem is here
talk.setEnabled(false);
String knockText = "<html>You walk up the driveway of the next house on your<br>list. As you approach the door, you adjust your<br>tie, and smooth your hair. Your mouth is dry, and<br>you could really go for a bottle of orange crush.<br>You decide you should get one after this house.<br>Time to knock, someone is clearly home.<html>";
DoorMenu.setLayout(null);
DoorMenu.setLocation(0, 0);
DoorMenu.setSize(500, 500);
text = new JLabel(knockText);
text.setLocation(100, 150);
text.setSize(300, 200);
DoorMenu.add(text);
knock.setLocation(100, 400);
knock.setSize(100, 50);
knock.addActionListener(this);
DoorMenu.add(knock);
talk.setLocation(200, 400);
talk.setSize(100, 50);
talk.addActionListener(this);
DoorMenu.add(talk);
stats.setLocation(300, 400);
stats.setSize(100, 50);
stats.addActionListener(this);
DoorMenu.add(stats);
knockSkill = new JLabel("Knocking: " +knocking+ " Speech: " +speech+ " Level: " +level+ " Skill Points: " +points);
knockSkill.setLocation(100, 25);
knockSkill.setSize(500, 50);
DoorMenu.add(knockSkill);
DoorMenu.setOpaque(true);
return DoorMenu;
}
public JPanel createStatMenu(){//still doesn't do anything
JPanel statMenu = new JPanel();
return statMenu;
}
public static void knockResponse(){//generates a response from a knock based on knock level (1 <= x <= 10). Random number (1 <= y <= 10) is generated, level determines how many are successful
//max knock level will shatter the door. 50/50 chance of conversation or police. max speech + police will talk them into letting you go
knock.setEnabled(false);
Random rand = new Random();
int n = rand.nextInt(10) + 1;
if(n > knocking){//knock check loss
text.setText("<html>All you hear is someone yelling to go away...<br>Oh well. You chipper up and go to<br>the next house on your list. That orange Crush<br>will have to wait!<html>");
ActionListener taskPerformer = new ActionListener(){//delay the label reset
public void actionPerformed(ActionEvent evt){
text.setText("<html>You walk up the driveway of the next house on your<br>list. As you approach the door, you adjust your<br>tie, and smooth your hair. Your mouth is dry, and<br>you could really go for a bottle of orange crush.<br>You decide you should get one after this house.<br>Time to knock, someone is clearly home.<html>");
knock.setEnabled(true);
}
};
new Timer(1000, taskPerformer).start();
}
if(n <= knocking && knocking != 10){//successful knock check
knock.setEnabled(false);
stats.setEnabled(false);
text.setText("<html>Someone's coming to the door!<br>You straighten your hair and adjust your tie.<html>");
ActionListener taskPerformer = new ActionListener(){//delay the label reset
public void actionPerformed(ActionEvent evt){
text.setText("<html>The door opens, and you introduce yourself;<br>\"Hi! my name is " +playerName+ ". Have you accepted my homeboy JC as your lord and savior?\"<html>");//I couldn't really think of anything to make him say other than that
talk.setEnabled(true);
}
};
new Timer(1000, taskPerformer).start();
}
if(knocking == 10){//door breaks
text.setText("<html>You managed to shatter the door thanks to<br>your orange crush powered knocking skeelz.<html>");
n = rand.nextInt(1) + 1;
if(n == 0){//response check. Both are empty for now
text.setText("0");
}else if(n == 1){
text.setText("1");
}
}
}
public static void successCalc(){//calculate the success in talking to someone by
talk.setEnabled(false);
Random rand = new Random();
int n = rand.nextInt(10) + 1;
if(n <= speech && speech != 10){
level++;
points++;
text.setText("<html>After some time, your conversation is over.<br>That went well. Better than you thought.<br>As you leave, you feel a rumbling in your gut.<br>It soon becomes painful. You fall to<br>the ground, and a gleaming light comes from<br>your stomach, as a skill point bursts out.<br>You hear a faint \"dunananaaa...\"<html>");
talk.setEnabled(false);
knockSkill.setText("Knocking: " +knocking+ " Speech: " +speech+ " Level: " +level+ " Skill Points: " +points);
}else if(n > speech){
talk.setEnabled(false);
text.setText("<html>After a moment of you speaking, all they do<br>is start blaring death metal.<br>You decide it's time to leave!<html>");
}else if(speech == 10){
text.setText("<html>Your golden, heavenly voice convinces them<br>to convert immediately.<br>You run down to the nearest convenience store<br>and grab yourself an orange Crush. Bottled, of course.<html>");
}
}
public void actionPerformed(ActionEvent e){
if(e.getSource() == start){
frame.getContentPane().removeAll();
frame.setContentPane(createDoorMenu());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
frame.setResizable(false);
frame.setVisible(true);
}else if(e.getSource() == knock){
knock.setEnabled(false);
knockResponse();
}else if(e.getSource() == talk){
talk.setEnabled(false);
successCalc();
}
playerName = name.getText();
}
public static void createAndShowGUI(){
JFrame.setDefaultLookAndFeelDecorated(false);
frame.setContentPane(game.createStartMenu());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
frame.setResizable(false);
frame.setVisible(true);
frame.setLocationRelativeTo(null);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
public void run(){
createAndShowGUI();
}
});
}
}
Thanks for any help, I'm terrible at asking questions.
A Timer starts out repeating, by default. So, your timers are all firing every second, forever.
You ought to call setRepeats(false):
void doLater(ActionListener action, int delay) {
Timer timer = new Timer(delay, action);
timer.setRepeats(false);
timer.start();
}

Categories