I am making some sort of turn-based battle system in a JFrame where the player clicks a button when it's his turn. The problem is: how can the program wait for a mouse click on the button? It goes like this:
while it is the player's turn {
wait for mouse input
if input == attack (for example)
-> attack
else if input == item
-> use item
and so on
You don't wait for a click; you let Swing do that for you. Instead, you put whatever you want to do into an ActionListener and attach it to the button, so it gets executed when the button is clicked.
As far as the turns go, you just need a member variable someplace that keeps track of whose turn it is; the button handler then has to look at that variable to know what to do.
One good way to structure things, by the way, might be to have a Player class, and a Game class, and a member in Game called currentPlayer. Then the ActionListener (which keeps the Game object as a member variable of its own) could look at currentPlayer in the Game and simply invoke makeMove() on the appropriate Player object.
Related
I've read through a dozen or so actionlistener/loop related questions here, but I'm not sure I've found my answer. I started on my first large Java project, a text RPG that's spiraled into around 5K lines of logic and game features which was functioning as intended using just the console - when I decided I'd try to build a Java swing GUI for it instead. Here's my problem:
I use a Room object which handles the description of where the player is at and also has an array of options for the player to choose next which it creates dynamically based on what cell the room's id is in on a csv file and what is beside it. I stopped outputting this to the console and instead started creating JButtons based on the options array like so:
public void showNarrate(){
add(dd,gridConstraints);
optionCopy.clear();
int i = 0;
for(JButton j : optionButtons){
//adding and formatting buttons to gridBagConstraint I also set actionCommand for each button to the triggerValue (ID of the next room which the button should take the player to)
}
//I tried using a copy of my JButton array here so I could have something to iterate over in actionListener after clearing out the original
//(Since it needs to be cleared so the next Room's buttons can be built after the player chooses an option)
for(JButton j : optionButtons){
optionCopy.add(j);
}
optionButtons.clear();
//dd is a seperate drawingComponent I used for outputting room descriptions which may be totally unnecessary at this point :/
dd.repaint();
setVisible(true);
}
Over in actionlistener (Same class) this is how I tried to swing it:
for(JButton j : optionCopy){
if(e.getActionCommand().equals(j.getActionCommand())){
Main.saveRoom = Main.currentRoom;
Main.currentRoom = j.getActionCommand();
System.out.println(Main.currentRoom);
}
}}
Then in my main class I call:
narrator.narrate(currentRoom, saveRoom); which takes care of some other logic concerning locked doors, encounters, etc.
Also in Main loop are some other methods related to autosave and tracking which rooms the player has visited. I know from other q/a i'v read on here that this is all pretty bad design, and I'm sttarting to understand that now, but my issue is this:
The first room of the game loads up fine, when I click a button it outputs to console(Just for testing) the correct trigger value of the room the button should be calling, so I'm getting that far, but how can I call the same method over again now?
-If I call narrate from actionListener it will wind up calling itself again and complain about ConcurrentModification.
-If I try to keep a loop going in my Main class it will keep looping and won't allow for the player to actually choose a button.
I've never used threads before, which I wonder might be the answer,and the closest thing to a related answer I've found is this:
Java: Method wait for ActionListener in another class
but I don't think moving actionListener to Main class would resolve my problem which is actionListener winding up calling itself recursively. And as for the observer-observable pattern... I just can't understand it :(
I appreciate any and all help, I've learned a LOT trying to make this thing work without seeking help as much as possible but this has stumped me.
Your loop in actionPerformed only checks whether a JButton exists in your optionList with the given actionCommand. However this can be done before actually doing something:
boolean contained = false;
for (JButton j : optionButtons)
if (j.getActionCommand().equals(e.getActionCommand()))
contained = true;
if (contained) {
// change room
}
now you can call narrate because you have finished iterating over the collection beforehand and will not get a ConcurrentModificationException
I have 5 classes for this project, Button Minefield GUI MenuBar Game.
Button is an individual mine cell.
Minefield contains a 2d array of buttons, and operations that concern all of the buttons.
GUI is the portion above the MineField grid, displaying the reset button, time lapsed and how many flags remain,
Game puts all of the other classes in Panels and places them in the applet. No game logic here.
Here is a screenshot of my applet
I'm having problems when it comes to using inheritance. If I can solve this issue, I can do the other problems too. It has to do with invoking a method in the super class.
One of my problems:
When a Button mine cell is right-clicked, the mouse listener picks it up and changes the text of the JButton to "F" for flag. However, I want to update the counter of how many flags are available, which is a variable in the Minefield class. Finally, I want the GUI class to listen for changes to that variable and update the JLabel.
In the MouseListener for Button:
if (e.getButton() == MouseEvent.BUTTON3)
{
advanceCurrentState();
if (currentState == "FLAG")
super.setNumFlagsRemaining(-1); //update the Minefields variable
}
Back in Minefield:
public void setNumFlagsRemaining(int delta)
{
numFlagsRemaining += delta;
}
But this doesn't work. When I am debugging, super.setNumFlagsRemaining(-1) is creating a new instance of Minefield and I cannot update the variable inside the super class. I thought that it would update the existing object of Minefield, but I read that Java does not pass by reference, so I was confused as how to do it. If you need to see additional code please say so, I tried to not clutter this post with 5 whole classes and say, "fix it".
You do not provide enough code so I can only guess here.
I think you are confused with key word super
It actually mean to call method from the super class which you had inherited.
I doubt that JButton inherits anything from your class Minefield. Usually it is in opposite way.
Also count of the fields is stored and displayed in the JLabel. So you, actually, have to call method of the class which has this JLabel and takes care of total count.
I wrote a simple little maze game for a terminal which repeatedly asks the user to do something (e.g. "In which direction would you like to go? [N/E/S/W]"). I have a navigate() method running in a loop that fires off these questions, stores their answers and does something depending on the answer.
public enum Dir (N, E, S, W);
public void navigate() {
Dir nextDir = utils.askDirection("Which way do you want to go?");
// Do stuff with answer, like changing position of user in maze
}
Now, I've written a simple GUI for my game. I deliberately put all the references to the terminal in a ConsoleUtils class which implements a Utils interface (this has methods like askQuestion()) - the idea being that I could create a GuiUtils class and have my game either as a terminal game or as a GUI game.
The problem is that the navigate method asks the user a question and then "waits" for the response, which the Utils class gives it by using a Scanner to read the newest line of input. However if I use Event Listeners for the new N/E/S/W buttons in my GUI, they fire off events regardless whether the navigate method has asked for one or not.
--> Image of GUI
Is there any way I can combine this or do I need to write a new navigate method for the GUI?
(To be honest, I'm also not entirely sure whether my GUI class should instantiate a game class, in which case the logic for navigate could end up in a GUI method anyway, or whether the game should have a GUI. I haven't written any code for the event listener either yet, since I'm not sure which class should be calling which. This is probably a separate question.)
Your text based game has a loop that repeatedly asks questions to gather user input. Swing provides this loop for you by continually executing Runnable blocks of code that have been posted to the EventQueue. For example, when the user presses a button labeled E, code is posted to the queue that invokes your ActionEvent implementation to handle your game's interpretation of the move east command.
For reference, a complete example of a very simple guessing game is examined here. In pseudocode, the corresponding text based game might look like this:
initialize
loop
prompt "Guess what color!"
get chosenColor
if chosenColor = actualColor
say "You win!"
reset game
else
say "Keep trying."
end loop
A more elaborate game cited there includes the original text-based source.
So suppose I'm developing a chess-like program using Java's Swing. I added a MouseListener to handle user input. To make a move the user must click a valid piece and then click a valid place. What's the best way to track the 2 mouse clicks in a turn? I'm thinking in use some kind of variable to record if is the turn's first click or second.
You have to distinguish the two game states, using a variable is fine.. you can also think something as suggested by NomeN comment and use two different listeners by swapping them.
Your case is quite simple but in general the formalism you use to handle these things is a sort of finite state machine that describes the states of your game and how to pass from one to another.
In this case you can have some states like:
player 1 turn
player 2 turn
main screen
pause screen
option screen
and you decide how and when to pass from a state to another, for example
after player1 moved you pass to player2 turn
after player2 moves you go back to player1 turn
when game starts you go in main screen
if you start a new game then you go to player1 turn
if you press pause key while in game you go from turn to pause screen and when closed you go back to the turn that was going before pause
This is just to give you an idea, so for example your MouseListener could care about states:
enum State { TURN_P1, TURN_P2, MAIN, PAUSE, ... }
public State gameState
...
public void mouseClicked(MouseEvent e)
{
if (gameState == TURN_P1)
{
...
if (move_is_legal and so on)
gameState = TURN_P2;
}
else if (gameState == TURN_P2)
{
...
if (move_is_legal and so on)
gameState = TURN_P1;
}
}
Oops, I answered too quickly. Yes, a structure that encodes a click location, looks for intervening motion events and then records the second click. There should be an initiated state, an accept state, and it should probably record a abort state (maybe a press of ESC).
I have a poker framework for which I am trying to develop a "player" for. Basically I am implementing an object that implements a Player interface defined by the framework. I am trying to put a GUI on top of this player, the way that game play works is that the Dealer invokes the act() method on my player and expects a return type of Action. The problem I have is that once the act() method is invokes, I update the GUI (written using Swing) to display the options available, however I now need the method NOT to return until the player has chosen an option. The options are displayed as JButtons, which when clicked are handled by an actionListener object. How can I make the act() method not return until the user has acted? I need the thread to sleep/wait until it is woken up by the event being triggered, am unsure of the syntax and best way to do this. Any ideas?
Thanks,
Aly
I think the approach is flawed. The Act method should not wait. Instead it should register for an event (lets call it the Acted event) on the Player instance. At the same time it should start a timer, of say 20 seconds, and if the Acted event is not raised before the timer runs out, the dealer should make the player fold (or check, depending on the situation) automatically and do the same for the next player in line.
That's just off the top of my head, but think about it.
If I undestood your ploblem, you need to use an ActionListener for this.
The ActionListener is an interface implemented in the class you want to be warned when an event occurs. When an specific event is triggered in other part of your code, this class is warned by an abstract method of the Action Listener interface.
It isn't easy to show you with a short answer, but I got an hello world example that can help you.
http://java.sun.com/docs/books/tutorial/uiswing/events/actionlistener.html
HIH