Reputation: 263
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?
Upvotes: 0
Views: 85
Reputation: 9941
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.
}
}
});
Upvotes: 1