Properly implementing a delay in a game - java

This is a follow up to a previous question I had. I have a Battleships game with two boards. When the user clicks on the computer board an action occurs, along these lines:
public void mouseClicked(MouseEvent e)
// Get coordinates of mouse click
if (//Set contains cell) {
/add Cell to set of attacked cells
//Determine if set contains attacked cell.
// If yes, hit, if no, miss.
checkForWinner();
The checkForWinner method determines if the game has been won yet. If it hasn't it calls a nextTurn method which changes the current turn. If the currentTurn is set to Computer, a ComputerMove() method is automatically called.
When that method finishes, it again checksforWinner, changes turn and waits for the user to click on the grid to start the cycle again.
Ideally, I'd like to have sound effects, or at the very least a pause between moves. However, no matter how I use Thread.sleep, or TimerTask, or anything else, I can't get it to function correctly.
If I use a simple Thread.sleep(500) in the CheckforWinner method, or in the ComputerMove method, all that happens is the human's go is delayed for the set amount of time. As soon as his move is executed the computer's move is completed immediately.
I know very little about threads but I assume this is because all the initiation of the bouncing back and forth between methods begins with a method in the mouse listener.
Given the set up of my system, is there a way to implement a delay without radically changing things?
Edit: May as well include the classes:
public void checkForWinner() {
if (human.isDefeated())
JOptionPane.showMessageDialog(null, computer.getName() + " wins!");
else if (computer.isDefeated())
JOptionPane.showMessageDialog(null, human.getName() + " wins!");
else
nextTurn();
}
public void nextTurn() {
if (currentTurn == computer) {
currentTurn = human;
} else {
currentTurn = computer;
computerMove();
}
}
public void computerMove() {
if (UI.currentDifficulty == battleships.UI.difficulty.EASY)
computerEasyMove();
else
computerHardMove();
}
public void computerEasyMove() {
// Bunch of code to pick a square and determine if its a hit or not.
checkForWinner();
}

Ideally, I'd like to have sound effects, or at the very least a pause between moves. However, no matter how I use Thread.sleep, or TimerTask, or anything else, I can't get it to function correctly.
You should be using a Swing Timer. Something like:
Timer timer = new Timer(1000, new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
currentTurn = computer;
computerMove();
}
});
timer.setRepeats(false);
timer.start();

Related

Wait for Mouse Event

I am building a game of top trumps and I need my program to pause execution until a JButton is clicked. I have done a google search and there does not seem to be any wait for mouse event option in Java. I have created an alternative solution but I suspect it is eating up memory. This is working for and returns what I need. Is this potentially eating up memory? is there a more efficient solution to this? (I added a method that pauses execution for 1 second and this does seem to slow the amount of memory being used)
Update: I am creating a game of trumps and when my card information is printed the game needs me to pick a skill. I.E There is 5 buttons to choose from that return a string value;
public String getPick() {
pick = getMouseClick();
System.out.println(pick);
return pick;
}
private String getMouseClick() {
panel.addMouseListener(new MouseAdapter() {
public void mouseClicked(String e) {
setPick(e);
}
});
return pick;

Breaking recursion from listeners

I'm trying to break a special case that makes this code recursive.
I have a Javafx game where there are human and computer players each play when it's his turn and there can be many rounds.
A computer is supposed to play automatically and move to the next player immediately and show no direct indication to the UI (but it's possible to review what it did afterwards).
The problem is in the case where there are only computer players, we will come here the moment the currentBoardPane was loaded, enter the condition since all players are computers, set the board of the next player, and then without finishing the call, call this same function again:
currentBoardPane.addListener((e) -> {
if(gameManager.checkIfCurrentPlayerIsComputer()){
gameManager.playAutoMovesForCurrentPlayer();
gameManager.setNextPlayer(); // it does current player property = next player
//update board on scene
currentBoardPaneIndex = ((currentBoardPaneIndex + 1) % gameManager.getPlayers().size());
currentBoardPane.setValue(boardPanes.get((currentBoardPaneIndex))); //this is a recursive call
}
});
Instead of this, if I subscribe a listener to the currentPlayer property in GameManager then I still need to call setNextPlayer() from that listener which is again recursive.
I can make a special case if all players are a computer, then run the game from a while(true){} instead of listeners and binds but there has to be a better way to break this recursion.
Is there a way to not get into recursion while still having listeners and binds?
Notes:
currentBoardPane signifies the current game board on the screen and it's an ObjectProperty.
Making the following assumptions about your code:
Everything is currently running on the FX Application Thread
The currentBoardPane.setValue(...) causes the UI to update (so you update the UI each move)
then a "quick and dirty" way to do this is:
currentBoardPane.addListener((e) -> {
if(gameManager.checkIfCurrentPlayerIsComputer()){
gameManager.playAutoMovesForCurrentPlayer();
//update board on scene
Platform.runLater(() -> {
gameManager.setNextPlayer(); // it does current player property = next player
currentBoardPaneIndex = ((currentBoardPaneIndex + 1) % gameManager.getPlayers().size());
currentBoardPane.setValue(boardPanes.get((currentBoardPaneIndex))); //this is a recursive call
});
}
});
This delegates the updates to a new Runnable, schedules that runnable to execute on the FX Application Thread, and exits the handler immediately. Thus the call to currentBoardPane.setValue(...) is executed later and is no longer recursive.
In fact, if you do just a little more work:
private final Executor aiExecutor = Executors.newSingleThreadExecutor();
// ...
currentBoardPane.addListener((e) -> {
if(gameManager.checkIfCurrentPlayerIsComputer()){
Task<Void> makeMoveTask = new Task<Void>() {
#Override
protected Void call() {
gameManager.playAutoMovesForCurrentPlayer();
return null ;
}
};
makeMoveTask.setOnSucceeded(e -> {
//update board on scene
gameManager.setNextPlayer(); // it does current player property = next player
currentBoardPaneIndex = ((currentBoardPaneIndex + 1) % gameManager.getPlayers().size());
currentBoardPane.setValue(boardPanes.get((currentBoardPaneIndex))); //this is a recursive call
});
aiExecutor.execute(makeMoveTask);
}
});
then this is exactly the code you would use if computing the move took enough time that it would not be acceptable to block the UI while it was happening. (And if computing the move takes very little time, this will still work just fine.) This assumes that playAutoMovesForCurrentPlayer() doesn't update the UI.

how to pause the game to handle user input

I'm facing a problem since many days without finding an anwser.
In the code i will not put all my code because it will just complicate the question.
I have a game class which is rendering each frame.
public class MyGame implements ApplicationListener {
#Override
public void render() {
//handling event
handleEvent();
//update player position
updatePlayerPosition();
//rendering the player using a batch
renderPlayer();
}
public void handleEvent(){
//when the player prees on C i'm calling a method in another class
// when i do some processing
if (Gdx.input.isKeyPressed(Keys.C)) {
OtherClassForProcessing() ocp = new OtherClassForProcessing();
ocp.process();
}
}
//in this method i have to ask the user to choose an option
//i have to think to stop running this method until the
// user choose an option
//this method has to return a value
public static int displayChoice(List<Integer> ListOfInteger){
return 0;
}
}
public Class OtherClassForProcessing(){
public void process(){
int value= MyGame.displayChoice() ;
}
}
The question is how can i ask the user to choose an option in the displayChoice method.
What kind of widget will do this work.
I tried to use another screen for that , but the methode don't stop running .
How can i ask the program to stop until the user choose an option.
Thank you
what i tried is :
#Override
public static int displayChoice(List<Integer> ListOfInteger){
//i change the screen when i ask the user to choose from many options
setScreen(new PauseScreen());
a wile loop hwo run until the user choose an option from the other screen
while(PauseScreen.notYetChoosen){
Gdx.app.log("display message ", "the user not yet choose an ption");
}
return PauseScreenValue;
}
When i put the while loop:
the screen don't change from the game to the PauseScreen.
the loop run without stoping
the screen block
But when i remove the while loop the screen change to the PauseScreen but the method finish without waiting the user to choose an option.
EDIT
i tried to avoid using another screen unfortunantly even when i used a window the screen block
You can use a separate screen for this, I do not know your code structure exactly but here is how you can do it conceptually if you were trying to get input from a "pop up" or "pause screen" or something of that nature.
Inside of your Game Screen have a boolean which will be set when your "pop up" is displayed, for example isPaused, then you can use this boolean to skip over game logic while waiting for the screen to receive input.
For a more elegant approach you can use Game States which can represent which state your game is in. You can have a PLAYING state, GETTING_INPUT state, etc. Then you can run game logic depending on which state you are in.
EXAMPLE:
public void update (float deltaTime) {
if (deltaTime > 0.1f) deltaTime = 0.1f;
switch (state) {
case GAME_READY:
updateReady();
break;
case GAME_RUNNING:
updateRunning(deltaTime);
break;
case GAME_PAUSED:
updatePaused();
break;
case GAME_LEVEL_END:
updateLevelEnd();
break;
case GAME_OVER:
updateGameOver();
break;
}
}
EXAMPLE SOURCE AND MORE INFO:
LIBGDX SuperJumper Demo
https://github.com/libgdx/libgdx-demo-superjumper/blob/master/core/src/com/badlogicgames/superjumper/GameScreen.java
Response to your edit
The reason your code is not changing is because you have a while loop printing out to the Gdx Log until input is taken so your code is stuck in that while loop.
If you want to go this route, you can set a 'pause' variable inside your Game Screen to true and then set your screen to the pause screen. Inside of your Game Screens update logic, tell it not to update if paused.
public void update()
{
if(!paused)
{
//game logic
}
}

what is stopping my screen from rendering as planned?

#Override
public void render(float delta) {
Gdx.gl.glClearColor(0,0,0,1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
combattext.setText(combatstring);
stage.act();
stage.draw();
}
combatstring is a string that displays what happened in my game. when enemies attacks, it says what happened. when I attack, it says what happens. As of now my game has a loop when in combat that runs a turncounting instance. When it's time for a turn, it runs that participants turn, and when their turn is over it updates combatstring.
I have 2 combatstrings. One is for my characters, and one is for enemies. Here is an example of what happens when it's an enemies turn.
if(thisencounter.monster1turn){ //monster1's turn goes in here
thiscombat.getTarget(1); //get enemy's target
thiscombat.calculateVars(1); //calculate all combat variables for
// this turn
thiscombat.theyAttack();
combatstring = thisencounter.thisenemy.species+
" "+thiscombat.action+
" "+thiscombat.theirtarget.species+
" for "+thiscombat.effect;
thisencounter.monster1turn=false;
}
This works fine, my combattext label updates instantly when a monster goes.
For my characters, their turns make buttons visible on my screen. The buttons have listeners that determine actions, here is an example:
monster1.addListener(new ClickListener(){ //makes the first enemy's
//image clickable, but only
//when the attack button
//has been clicked
#Override
public void clicked(InputEvent event, float x, float y) {
thiscombat.ChooseTarget(1); //various methods that calculate damage,
//output what happened, and put us back in
thiscombat.calculateVars(4); //the loop to wait for next
//participant's turn
thiscombat.youAttack();
combatstring = thisencounter.thisspirit.species+
" "+thiscombat.action+
" "+thiscombat.yourtarget.species+
" for "+thiscombat.effect;
monster1.clear();
monster2.clear();
monster3.clear();
try {
combatloop();
} catch (InterruptedException ex) {
Logger.getLogger(map1field.class.getName()).
log(Level.SEVERE, null, ex);
}
}});
This also works fine, EXCEPT on the turn just before a monster goes. Since there is no wait between my turn and the monster's turn, the combattext just instantly displays what the monster does.
I assumed 'oh hey this is because there is no delay between the two turns'. So, I threw a thread.sleep(2000) just before where it updates the monsters combatstring.
But that does not fix it. It still does not show the text for my character that goes just before a monster. Also, since fighting starts right when you go to this screen in the game, it waits about 2 seconds before it displays anything on the screen.
For the life of me I don't understand why it behaves like this. Can someone explain what is happening to me? Is it because it isn't rendering before it gets to thread.sleep ?
If that's the case, how do I force it to render before it does thread.sleep, or what should I be using instead of thread.sleep ?

when shooting constantly repeats and goes faster every key stroke

Ok so i'm going to try to explain this, well I created a shoot method in a class that contains my bluespell, and all of it's constructors, well the problem is when I press space once it constantly shoots without me pressing it again, and if I press it twice the speed at which it fires doubles and it starts to contain more than one x and y position on my grid I just want the spell to fire when fired and I only need one item because I don't want there to be more than one instance of it on the grid I want it to be that the player cannot fire until the spell has left the grid, here's my code thanks oh and I only have it called in my key released seeing as it should only do it once the key has been released, but if that should change please let me know thanks :)
public void shootSpell(){
final BlueSpell b = new BlueSpell(GoodGuy.getx(), GoodGuy.gety() +1, BlueSpellWizard());
int delay = 100;
ActionListener taskPerformed = new ActionListener(){
public void actionPerformed(ActionEvent e){
if(b.gety() != 19){
WizardCells[b.getx()][b.gety()].setIcon(null);
WizardCells[b.getx()][b.changey(b.gety()+1)].setIcon(b.getIcon());
}
else{
WizardCells[b.getx()][b.gety()].setIcon(null);
b.changex(GoodGuy.getx());
b.changey(GoodGuy.gety() +1);
}
}
};
new Timer(delay, taskPerformed).start();
else if(key == KeyEvent.VK_SPACE){
GoodSpell.shootSpell();
}
Do not use a Timer! Your task should not repeat every 100 milliseconds. If I understand your code, you should run the code from your ActionListener in a new thread.
// Something like this,
new Thread(new Runnable() {
public void run() {
if (b.gety() != 19) {
WizardCells[b.getx()][b.gety()].setIcon(null);
WizardCells[b.getx()][b.changey(b.gety() + 1)].setIcon(b
.getIcon());
} else {
WizardCells[b.getx()][b.gety()].setIcon(null);
b.changex(GoodGuy.getx());
b.changey(GoodGuy.gety() + 1);
}
}
}).start();
You also need to do a check if the spell is currently in view/activate prior to initiating a new spell in the shoot() method...
public void shootSpell(){
//do a check here if a spell is already running!
final BlueSpell b = new BlueSpell(GoodGuy.getx(), GoodGuy.gety() +1, BlueSpellWizard());
int delay = 100;
//......
So in your method that is updating the spell going accross the screen you either need to have a flag in there if its still active, or if you are running it in a new thread, save that thread to a global var and check to see if the thread is running prior instantiating a new BlueSpell()

Categories