Displaying correct information in JPanel - java

I'm having trouble updating JPanels with new data. I'm working on a game, the basic concept of what I'm trying to do is load a player and display the items he holds in a JPanel (set out in a box layout) I am using a thread and loop to update the player items. This works fine for one player but my game allows for additional players which the user can switch between. When I switch to the next player I want their details to be loaded and updated. This doesn't work.
This is just a snippet of the code but I'm hoping someone will be able to see my problem.
private JFrame frame;
private JPanel mainPanel;
private Box Item1 Item2, Item3;
static private JPanel centerPanel;
private boolean playerLoaded;
private Player currentPlayer;
private Thread gameThread;
public void run(){
while (running){
for (Player player:allPlayers){
playerLoaded = false;
currentPlayer = player;
player.setCurrentTurn(true);
while (player.isCurrentTurn()){
if (playerLoaded != true){
loadPlayer(player);
loadItems(player);
playerLoaded = true;
}
if ((playerLoaded == true){
updateItems(player);
try {
Thread.sleep(999);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
public void loadPlayer(Player player){
jlCurrentPlayer.setText("Player: " + player.getName());
}
public void loadItems(Player player){
int count = 1;
centerPanel = new JPanel(new GridLayout(1,3));
for (Item Item : player.getAllItems()){
if (count == 1) {
Item1 = Box.createVerticalBox();
Item1.setBorder(BorderFactory.createLineBorder(Color.BLACK));
JLabel jlItem = new JLabel(item.getName());
Item1.add(jlItem);
centerPanel.add(Item1);
}
else if (count ==2){
Item2 = Box.createVerticalBox();
Item2.setBorder(BorderFactory.createLineBorder(Color.BLACK));
JLabel jlItem = new JLabel(item.getName());
Item2.add(jlItem);
centerPanel.add(Item2);
}
else if (count ==3){
Item3 = Box.createVerticalBox();
Item3.setBorder(BorderFactory.createLineBorder(Color.BLACK));
JLabel jlItem = new JLabel(item.getName());
Item3.add(jlItem);
centerPanel.add(Item3);
}
mainPanel.add(centerPanel,BorderLayout.CENTER);
count++;
}
}
public void updateItemstats(Player player){
int count = 1;
if (player.isCurrentTurn()){
for (Item item : player.getAllItems()){
if ((count == 1) && (player.isCurrentTurn())) {
Item1.add(Box.createVerticalGlue());
Item1.add(new JLabel("Value: " + item.getstats().getValue()));
Item1.add(new JLabel("Quality: " + item.getStats().getQuality()));
}
else if (count ==2){
Item2.add(Box.createVerticalGlue());
Item2.add(new JLabel("Value: " + item.getstats().getValue()));
Item2.add(new JLabel("Quality: " + item.getStats().getQuality()));
}
else if (count ==3){
Item3.add(Box.createVerticalGlue());
Item3.add(new JLabel("Value: " + item.getstats().getValue()));
Item3.add(new JLabel("Quality: " + item.getStats().getQuality()));
}
count++;
}
}
}
JButton jbNext = new JButton("Next Player");
jbNext.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
currentPlayer.setCurrentTurn(false);
}
});
For my main frame I am using a Border Layout. Whats happening when I switch players is basically the updated information is sometimes a mix of player 1 and 2. e.g.
value 25
Quality 60
value 50
Quality 20.
In addition if player 1 has 1 Item and player 2 has 3 Items and I switch from player 2 to 1 sometimes player 1 will have all of player 2 items instead of his own.
I thought it might be an issue with the Item1 2 and 3 panels so I tried to remove them on next player btn click but this just caused an exception. This may well be the problem still and I may not be removing them correctly.
Can anyone out there help me out with this?

You should call setCurrentTurn(true) for the next Player in the next player action Listener.
Have you set the currentPlayer variable in Player to volatile? Important: Both the private Player currentPlayer and the private boolean currentPlayer in the Player.java must be volatile. (Have a look at http://javamex.com/tutorials/synchronization_volatile.shtml)
Also, use a Monitor and wait() instead of Thread.sleep that way you can use notify() to wake up the waiting Thread.
Here are some suggestions for your code, but be aware that you will have to do more (e.G. in the Player.java class that you did not show).
volatile List<Player> players = ...; // load your list of Player
volatile int currentPlayerIndex = 0;
volatile Player currentPlayer = null;
private final Object WAIT_MONITOR = new Object(); // yes, 'new Object()' !
[...]
public void run() throws InterruptedException {
playerLoop:
while(programRunning) { // maybe while(true)
synchronized (WAIT_MONITOR) {
loadPlayer(currentPlayer);
loadItems(currentPlayer);
updateItems(currentPlayer);
WAIT_MONITOR.wait(1000);
}
}
}
[...]
JButton jbNext = new JButton("Next Player");
jbNext.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
synchronized(WAIT_MONITOR) { // this causes that the contents of run() and this method do not run at the same time.
currentPlayer.setCurrentTurn(false);
if(players.size() > currentPlayerIndex + 1) // don't use >=
currentPlayerIndex++;
else
currentPlayerIndex = 0; // reset at the end of the list
currentPlayer = players.get(currentPlayerIndex);
currentPlayer.setCurrentTurn(true);
WAIT_MONITOR.notifyAll(); // that causes the wait() method above to be continued.
}
}
});

Related

Win condition method for tic tac toe not working only on the second row

I have a tic tac toe game using gridlayout to arrange buttons in a 3x3 grid, my problem is with a method called public void checkWinRowX(), in my active listeners for my buttons whenever a button is pressed it adds 5 to both the colum int and the row int for which ever row/col they're in. and the method for checking for x win is checking for a row to be equal to 15, then highlighting the three x
to recreate the problem use the full code link at the bottom, then put three x's in the middle row, they're supposed to highlight when there's three in a row but they don't.
these are the active listeners that affect the second row:
private class twoOneListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
if(four)
getTurn(twoOnebutton);
four = false;
String twoOneString = twoOnebutton.getText().toString();
if (twoOneString.equals("X"))
{
if(x4 == 0)
{
colOne += 5;
rowTwo += 5;
}
x4++;
checkWinColumX();
checkWinRowX();
}
}
}
//******2,2*********//
private class twoTwoListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
if(five)
getTurn(twoTwobutton);
five = false;
String twoTwoString = oneTwobutton.getText().toString();
if (twoTwoString.equals("X"))
{
if(x5 == 0)
{
colTwo += 5;
rowTwo += 5;
}
x5++;
checkWinColumX();
checkWinRowX();
}
}
}
//******2,3*********//
private class twoThreeListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
if(six)
getTurn(twoThreebutton);
six = false;
String twoThreeString = twoThreebutton.getText().toString();
if (twoThreeString.equals("X"))
{
if(x6 == 0)
{
colThree += 5;
rowTwo += 5;
}
x6++;
checkWinColumX();
checkWinRowX();
}
}
}
and this is the check row method:
public void checkWinRowX()
{
if(gameWon = false);
{
//first row
if(rowOne == 15)
{
oneOnebutton.setBackground(Color.YELLOW);
oneTwobutton.setBackground(Color.YELLOW);
oneThreebutton.setBackground(Color.YELLOW);
}
//Second row
if(rowTwo == 15)
{
twoOnebutton.setBackground(Color.YELLOW);
twoTwobutton.setBackground(Color.YELLOW);
twoThreebutton.setBackground(Color.YELLOW);
}
//Third row
if(rowThree == 15)
{
threeOnebutton.setBackground(Color.YELLOW);
threeTwobutton.setBackground(Color.YELLOW);
threeThreebutton.setBackground(Color.YELLOW);
}
}
}
full code here: https://pastebin.com/rYXrx3m4
I got it, twoTwos active listener wasn't the same as the others, idk what was different but I copied and pasted another listener into it and changed the objects
fixed :)

How to get the source of multiple JButtons in action listener?

I'm coding a memory matching game using pictures and a JButton array, but I've run into a problem when I try to compare two buttons that were clicked. How do you store the index of/get the index of the second button? All of my buttons in the button array are linked to the same actionListener but e.getSource() will only get the first button clicked, as far as I'm aware. I'd really appreciate some help. (I didn't want to paste in my entire code, because that's a lot, so I'm just putting in parts I think are relevant):
public DisplayMM(ActionListener e)
{
setLayout(new GridLayout(6, 8, 5, 5));
JButton[] cards = new JButton[48]; //JButton board
for(int x = 0; x < 48; x++) //initial setup of board
{
cards[x] = new JButton();
cards[x].addActionListener(e);
cards[x].setHorizontalAlignment(SwingConstants.CENTER);
cards[x].setPreferredSize(new Dimension(75, 95));
}
private class e implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
for(int i = 0; i < 48; i++)
{
if((e.getSource())==(cards[i]))//1st button that was clicked
{
cards[i].setIcon(new ImageIcon(this.getClass().getResource(country[i])));
currentIndex = i;
}
}
//cards[i].compareIcons(currentIndex, secondIndex);
}
}
Also, in my Panel class, I attempted to do something similar, but ended up moving it to the Display class because Panel didn't have access to the button array.
//Panel
public void actionPerformed(ActionEvent e)
{
/*every 2 button clicks it does something and decreases num of tries*/
noMatchTimer = new Timer(1000, this);
noMatchTimer.setRepeats(false);
JButton source = (JButton)e.getSource();
guess1 = source.getText(); //first button clicked
numGuess++; //keeps track of number of buttons clicked
JButton source2 = (JButton)e.getSource();
guess2 = source2.getText();
numGuess++;
if(numGuess == 1)
display.faceUp(cards, array, Integer.parseInt(e.getSource()));
else
display.compareIcons(guess1, guess2);
if(tries != 12 && count == 24)
{
displayWinner();
}
}
You can give your ActionListener class private fields, even if it's an anonymous inner class, and one of those fields can be a reference to the last button pushed. Set it to null after the 2nd button is pushed and you'll always know if the button press is for the first or second button.
e.g.,
class ButtonListener implements ActionListener {
private JButton lastButtonPressed = null;
#Override
public void actionPerformed(ActionEvent e) {
JButton source = (JButton) e.getSource();
if (lastButtonPressed == null) {
// then this is the first button
lastButtonPressed = source;
} else {
// this is the 2nd button
if (source == lastButtonPressed) {
// the dufus is pushing the same button -- do nothing
return;
} else {
// compare source and lastButtonPressed to see if same images (icons?)
// if not the same, use a Timer to hold both open for a short period of time
// then close both
lastButtonPressed = null;
}
}
}
}

Java: turn-based battle system (with gui)

EDIT (4/3/2017): Sorry, I was a noob back then.
I'm trying to make a turn-based battle system where the player clicks buttons on his turn. But I can't seem to find out how to code it. Below is the code on what I did.
What should happen here is that when I click the attack button(for example) the next turn will be the monster's turn but the playerTurn variable doesn't change when I click the button. playerTurn is always true. Can you help me correct this? It is a turn-based battle system.
public class BattleFrame extends JFrame implements ActionListener, Runnable {
private JButton atkButton = new JButton("Attack");
private JButton runButton = new JButton("Run");
private JButton itemButton = new JButton("Item");
private JButton magicButton = new JButton("Magic");
private JPanel panelButtons = new JPanel();
private Random rand = new Random();
private Boolean playerTurn;
private Thread t;
public BattleFrame() {
setSize(480, 390);
setLayout(null);
// I have not included the code with the setting of the JButtons
initPanel(); // initialize the panel with buttons
setResizable(false);
setVisible(true);
playerTurn = true;
t = new Thread(this);
t.start();
}
// I'm not so familiar with 'synchronized' but I tried it here but it doesn't change anything
public void actionPerformed(ActionEvent e) {
Object src = e.getSource();
if(src.equals(atkButton) && playerTurn) {
System.out.println("Attack!");
playerTurn = false;
}
else if(src.equals(runButton) && playerTurn) {
System.out.println("Run!");
playerTurn = false;
}
else if(src.equals(itemButton) && playerTurn) {
System.out.println("Item");
playerTurn = false;
}
else if(src.equals(magicButton) && playerTurn) {
System.out.println("Magic");
playerTurn = false;
}
}
public void run() {
while(true) {
if(playerTurn == false) {
System.out.println("Monster's turn!"); // just printing whose turn it is
playerTurn = true;
}
else System.out.println("player's turn!");
}
}
public static void main(String[] args) {
new BattleFrame();
}
}
A Boolean is an object, so gets compared by identity, not value.
assert new Boolean (true) == new Boolean(true);
The above will fail, as the two different Boolean objects are not the same object.
For general use, use the primitive type boolean, not the standard library class Boolean. Cases where you should use Boolean are pretty rare: it's one of those things that exists more for symmetry than any real practical reason. If you do use it, you need to use a.equals(b) not a == b.
For more details, see:
http://www.java-samples.com/showtutorial.php?tutorialid=221

How to interchange between 2 timers? in Java GUI

Okay, so basically what I'm trying to create a timer that counts up and down. I need the program to activate just one timer at any one time. There are two timers, one causing a variable to increment, the other to decrement. I can't seem to get it right, when I press the increment, the variable increments but never stops, even when I press the decrement button. How do I go about doing this? Also, another quick question : How do I return a value which is within a keypress method? Keypress are by default void, so I stumped.
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
public class TimerTutorial extends JFrame {
JLabel timerLabel;
JButton buttonAdd, buttonMin, buttonReset;
Timer timer;
Timer timer2;
public TimerTutorial() {
setLayout(new GridLayout(2, 2, 5, 5));
buttonReset = new JButton("Press to reset");
add(buttonReset);
buttonAdd = new JButton("Press to Add");
add(buttonAdd);
buttonMin = new JButton("Press to Minus");
add(buttonMin);
timerLabel = new JLabel("Waiting...");
add(timerLabel);
event e = new event();
buttonAdd.addActionListener(e);
buttonMin.addActionListener(e);
}
public class event implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (e.getSource() == buttonAdd) {
TimeClassAdd tcAdd = new TimeClassAdd();
timer = new Timer(1000, tcAdd);
timer.start();
} else if (e.getSource() == buttonMin) {
TimeClassMin tcMin = new TimeClassMin();
timer2 = new Timer(1000, tcMin);
timer2.start();
} else if (e.getSource() == buttonReset) {
timer.stop();
timer2.stop();
// This code does not work
// Need to revert counter to 0.
}
}
}
public class TimeClassAdd implements ActionListener {
int counter = 0;
public void actionPerformed(ActionEvent f) {
String status_symbol[] = new String[4];
status_symbol[0] = "Unused";
status_symbol[1] = "Green";
status_symbol[2] = "Yellow";
status_symbol[3] = "Red";
if (counter < 3) {
counter++;
timerLabel.setText("Time left: " + status_symbol[counter]);
} else {
timerLabel.setText("Time left: " + status_symbol[counter]);
}
}
}
public class TimeClassMin implements ActionListener {
int counter = 4;
public void actionPerformed(ActionEvent d) {
String status_symbol[] = new String[4];
status_symbol[0] = "Unused";
status_symbol[1] = "Green";
status_symbol[2] = "Yellow";
status_symbol[3] = "Red";
if (counter >= 3) {
counter = 3;
timerLabel.setText("Time left: " + status_symbol[counter]);
counter--;
} else if (counter == 2) {
timerLabel.setText("Time left: " + status_symbol[counter]);
counter--;
} else if (counter == 1) {
timerLabel.setText("Time left: " + status_symbol[counter]);
}
}
}
public static void main(String args[]) {
TimerTutorial gui = new TimerTutorial();
gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gui.setSize(500, 250);
gui.setTitle("Timer Tutorial");
gui.setVisible(true);
}
}
In case you start the second timer you will definitively have to stop the first one if it is still running (i.e. call timer2.stop() just before timer.start() and the other way round).
Otherwise both will interfere, i.e. they access the same fields (in this case the timerLabel). Depending on the timing this might then look like if the second timer is continuously increasing the value. If e.g. the increase timer is always triggered shortly after the decrease timer, the output value will always be 3 - Red. The counter itself is not increased but the label is filled with this value over and over again and thus looks like it is ignoring the decreasing timer completely.
Nevertheless you should also stop each timer if its counter has reached the final value. There is no need to let it run any longer.
Regarding your second question: You cannot assign a return value but instead modify some field of your listener which you can then access outside of the action method.
One other problem: your reset button (or any button for that matter) won't do anything if you don't add an actionListener to it. In other words, you need to have code that looks like...
buttonReset.addActionListener(...);
somewhere in your program's code for the button to work.

How to implement actionlistener inside a Timer program?

I need this timer program to run indefinately, until someone presses the reset button. This timer program would increment or decrement with a single click of a button. For example, click once, and it will increment UNTIL someone says it to stop, or to decrement. Problem is, I think i've made the correct codes for this, but it simply wont run. There must be a logical error in it, can't seem to pinpoint where exactly, though. Can you tell whats wrong with my code?
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
public class TimerTutorial extends JFrame {
JLabel timerLabel;
JButton buttonAdd, buttonMin, buttonReset;
Timer timer;
Timer timer2;
public TimerTutorial() {
setLayout(new GridLayout(2, 2, 5, 5));
buttonReset = new JButton("Press to reset");
add(buttonReset);
buttonAdd = new JButton("Press to Add");
add(buttonAdd);
buttonMin = new JButton("Press to Minus");
add(buttonMin);
timerLabel = new JLabel("Waiting...");
add(timerLabel);
event e = new event();
buttonAdd.addActionListener(e);
buttonMin.addActionListener(e);
buttonReset.addActionListener(e);
}
public class event implements ActionListener {
public void actionPerformed(ActionEvent e) {
while (true) {
TimeClassAdd tcAdd = new TimeClassAdd();
timer = new Timer(1000, tcAdd);
timer.start();
timerLabel.setText("IT HAS BEGUN");
}
}
public class TimeClassAdd implements ActionListener {
int counter = 0;
public void actionPerformed(ActionEvent e) {
String status_symbol[] = new String[4];
status_symbol[0] = "Unused";
status_symbol[1] = "Green";
status_symbol[2] = "Yellow";
status_symbol[3] = "Red";
if (e.getSource() == buttonAdd) {
if (counter < 3) {
counter++;
timerLabel.setText("Time left: " + status_symbol[counter]);
} else {
timerLabel.setText("Time left: " + status_symbol[counter]);
}
} else if (e.getSource() == buttonMin) {
if (counter >= 3) {
counter = 3;
timerLabel.setText("Time left: " + status_symbol[counter]);
counter--;
} else if (counter == 2) {
timerLabel.setText("Time left: " + status_symbol[counter]);
counter--;
} else if (counter == 1) {
timerLabel.setText("Time left: " + status_symbol[counter]);
}
}
}
}
}
public static void main(String args[]) {
TimerTutorial gui = new TimerTutorial();
gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gui.setSize(500, 250);
gui.setTitle("Timer Tutorial");
gui.setVisible(true);
}
}
This while (true) will put your whole application to sleep:
while (true) {
TimeClassAdd tcAdd = new TimeClassAdd();
timer = new Timer(1000, tcAdd);
timer.start();
timerLabel.setText("IT HAS BEGUN");
}
Simply don't do this in a Swing application, at least not on the main Swing event thread since it ties up the event thread, allowing no other actions to take place. Besides since you are using a Timer, there's absolutely no need for the while loop in the first place.
Edit 1
Other problems:
Your TimeClassAdd is the ActionListener used by the Timer. The getSource returned from ActionEvent object that is passed into its actionPerformed method will be the object whose event triggered the event, here the timer not a button. So you cannot retrieve which button was pressed from this ActionEvent object.
Instead you will need to retrieve that information from the getSource() returned by the ActionEvent object passed into the the "event" class's actionPerformed method.

Categories

Resources