I'm making a above mode 2D Zombie Shooter, and I'm having some problems with a thread. here's the deal. I had the character shoot a bullet every time i pressed space. the problem was that if you held space, it would shoot one, then pause, and then shoot a lot of bullets. there were a bunch of ways to fix this, but i want this way because it leaves room for future changes to shoot speed. here is the code for the thread that is causing the problems:
package threads;
import Game.GameCore;
public class Shoot extends GameCore implements Runnable {
/**
* WHEN I START THIS THREAD, THE ENTIRE GAME FREEZES, AND I DO NOT KNOW
* WHY... NEED TO FIX. IT DOES NOT FIX THE PROBLEM TO TAKE OUT THE "SHOOT"
* OR THE "SLEEP"...
*/
public void run() {
while (shooting && gameRunning) { // shooting is made true when space is
// pressed, and set false when space
// is released. gameRunning is true
// if the game is running, which it
// is. removing either of these
// doesnt work either.
player.shoot(); // player comes from the GameCore class, and
// represents the player entity. if i remove this,
// nothing changes.
try {
Thread.sleep(bulletShootSpeedMillis); // bulletShootSpeedMillis
// is equal to 1000, but
// makes no difference
// to change
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
so here is the dang problem. the comments kinda point them out, but ill list them. if i remove the obvious things, such as player.shoot(); or Thread.sleep(bulletShootSpeedMillis); or even one of the things in the while loop, nothing changes. the issue is that when i initiallize the thread, with
else if (key == Integer.parseInt(commands[6])) {
shooting = true;
new Thread(new Shoot()).run();
}
the entire game just freezes. nothing happens at all. the moment i start the thread with space, my game freezes, and i cannot figure out why!!! the previous version with:
else if (key == Integer.parseInt(commands[6])) {
player.shoot();
}
it works just fine. Please help! Thanks in advance! :)
EDIT: thanks for the quick answer. needless to say, major learning experience with simple mistakes XD
Ayyyy!
new Thread(new Shoot()).run(); // ***** !!!!
You don't start a Thread by calling its run() method, since all that does is call the code in the same thread as the calling code. You start a new Thread by calling its start() method.
Related
In my game, if I select the good answer I play an applause sound then I move to the next question. So after calling a method say validate()in setOnMousePressed, I play the wav and do this :
try {
Thread.sleep(1700);
} catch (InterruptedException e) {
e.printStackTrace();
}
To pause everything until the sound is finished, then I call a method reset() to move to the next question.
The problem occurs when I click more than one time before 1700ms passes. At the next questions even without clicking anymore, it takes in count all the excess previous clicks and procceed to validate()and reset()so the final result is a hot mess.
How can I pause listening to mouse/key events until the sound is finished?
As Yahya mentioned, simply declare a boolean for e.g.
boolean listenForMouseEvents = true;
Set an if condition such that validate() can only be invoked when listenForMouseEvents is true for e.g.
if (listenForMouseEvents) {
validate();
}
Then once you click, set the listenForMouseEvents to false.
I've been working on this really basic Java RPG game, and when I tried using Thread.sleep for my monster to move slower, it seems to affect the player as well, in other words, the movement of the player had slow down when the monster appears, but turns back to normal when I move to another part of the map where the monster is not there
Here's the code for the monster's movement
int wolfRandNum;
if (isKilled() == false){
wolfRandNum = (int) (Math.random()*4);
if (wolfRandNum == 1){ // up
System.out.println("up");
if (playerRow -1 <= 0){
setCurrentEnemySprite(3);
} else {
moveUp();
playerRow--;
}
} else if (wolfRandNum == 2){ // down
System.out.println("down");
if (playerRow +1 >= 8){
setCurrentEnemySprite(0);
} else {
moveDown();
playerRow++;
}
} else if (wolfRandNum == 3){ // left
System.out.println("left");
if (playerColumn -1 <= 0){
setCurrentEnemySprite(1);
} else {
moveLeft();
playerColumn--;
}
} else{ // right
System.out.println("right");
if (playerColumn + 1 >= 9){
setCurrentEnemySprite(2);
} else {
moveRight();
playerColumn++;
}
}
}
The code above is in a method called update, which is from an enemy class.
Here's the where I call the method:
public void paintComponent(Graphics g) {
super.paintComponent(g); //required to ensure the panel is correctly redrawn
map.draw(g);
player.draw(g);
for (Character character : characterList) {
character.draw(g);
}
for (Enemy enemy : enemyList){
enemy.draw(g);
try {
enemy.update();
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (Item item : bagList) {
item.draw(g);
}
repaint();
}
Is there anyway to use thread.sleep without affecting the player's movement? If not, what other methods can I use to make the monster move slower but not the player?
I'm not really sure how to fix this problem, any advise would help a lot! Thank you :)
This is a event-driven Swing GUI, and so the answer to the question:
How to use Thread.sleep(...)
is that you don't.
You're coding this as if it were a linear console program, and this design won't work for event-driven non-linear programs. Calling Thread.sleep on the Swing event thread and especially in any painting method is a guarantee to put your entire GUI to sleep so that it becomes totally unresponsive. The proper solution is to create a non-GUI Model class to go with your GUI (your "View"), and to change how the view responds based on the state of the Model, i.e., the state of its key fields.
Also, if your game is being run in "real time", then you will need a game loop to drive your game. This can be done easily via a Swing Timer, although it does not provide absolute precision. If greater precision is required, then use the Swing Timer, but don't assume that each time slice is accurate, and instead measure each delta time and base your physics on the measured slice, not the assumed slice. Other ways to create a game loop include use of a background thread with a while loop and Thread.sleep within this thread.
All you are slowing down is the rendering (drawing) of your monster. As all drawing is done on the UI thread, this will, as you have rightly noticed, have a huge performance impact on your entire application. As a general rule, you should never call a blocking operation such as sleep in the UI thread.
Given you only have two characters, what I would consider trying is creating two threads, one for your character and one for your monster. You will do what you need to do in each of these threads, and then call repaint() when you need to redraw. If you want to slow down the monster, for example, you can call sleep in the monster thread.
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.
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();
I am having an issue with SWING GUI or at least I think it is the swing gui.
Here is my main code file:
/**
*
*/
package com.tda.t2.ctas.slasher;
import javax.swing.SwingUtilities;
import com.tda.t2.ctas.slasher.gui.mainFrame;
import com.tda.t2.ctas.slasher.utils.MyCloseListener;
public class SLASHer {
public SLASHer () {
}
/**
* #param args
*/
public static void main(String[] args) {
//EventQueue.invokeLater(new Runnable() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ConfigData myconfig = new ConfigData();
try {
//TdaUrlHelper window = new TdaUrlHelper();
//window.tdaFrame.setVisible(true);
mainFrame tdaFrame = new mainFrame();
tdaFrame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
Simple call to create the frame and open it. There are other files that I did not put here for space. But the problem that I have (and I have tried on several machines and operation systems) is that the buttons on the window seem to hang. I can select the window and click on the buttons and they highlight like they were hit but nothing happens. I have a tabbed plane and clicking on the other tabs also does nothing. Some times this last for about 15 seconds and other times it lasts several minutes. But it always eventually comes back and will respond to new input (ie it does not remember all the click around I did before it came back). The application overall is simple in that it sits waiting until a user does something before it does something so I am confused on why it seems to hang.
Any help would be appreciated.
Thanks
What is the offending code attach to the button that hangs? Check your console for exceptions, and put some System.out.println() statements at the top and bottom of that code. See if you see those print statements print out. Watch how long it takes for the one at the top to print and bottom one to print. If you see both statements then you know that whole block is executing, but if it takes a while to show the last statement you know you are hanging up the Swing thread (also known as EDT - event dispatch thread). Rule number one in Swing the UI can't repaint while it's executing your ActionListener.
In order to make a responsive UI you have to see the first and last statement appear on the console in under 10-100ms (visually almost instantaneous). If you really want to get fancy you can use System.currentTimeMillis() at the stop and bottom. Subtract the two values and println() it. That'll tell you exactly how long that listener ran for. If it's greater than 100ms you need to restructure your code by either improving your algorithm or off loading the long calculation on a thread (see this SwingWorker tutorial).
public void actionPerformed(ActionEvent event) {
System.out.println("Starting SomeProcess");
long start = System.currentTimeMillis();
// all your code belongs here
long duration = System.currentTimeMillis() - start;
System.out.printf("SomeProcess took %,d ms%n", duration );
}