when shooting constantly repeats and goes faster every key stroke - java

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()

Related

How to prevent repeated actions from a key being held down?

I'm trying to make a simple action in a Java FX "game" in which an image of a pig 'jumps' upwards every time the spacebar is pressed. Here is the code for the key event handlers and the Animation Timer that I'm using to actually carry out the action.
Key Handlers:
ArrayList<String> in = new ArrayList<String>();
s.setOnKeyPressed(
new EventHandler<KeyEvent>()
{
public void handle(KeyEvent e)
{
String code = e.getCode().toString();
if ( !in.contains(code) ){
in.add( code );
}
}
});
s.setOnKeyReleased(
new EventHandler<KeyEvent>()
{
public void handle(KeyEvent e)
{
String code = e.getCode().toString();
in.remove( code );
}
});
Animation timer:
new AnimationTimer()
{
double q = 200;
public void handle(long currentNanoTime)
{
double t = (currentNanoTime - startNanoTime) / 4000000.0;
if(in.contains("SPACE")){
q -= 20;
}
double y = q + t;
if(y >= 520){
gc.drawImage(background1, 0, 0, 1160, 740);
gc.drawImage(pig, 90, 520, 125, 100);
}else{
gc.drawImage(background1, 0, 0, 1160, 740);
gc.drawImage(pig, 90, y, 125, 100);
}
}
}.start();
So as you can see I'm having the animation timer simply cause the 'pig' to gradually fall down the y-axis, and when the spacebar is pressed, it is given a slight boost upwards.
The problem is that if the spacebar is held down, the pig just flies continuously upwards without stopping. I want this to be prevented so that the spacebar must be repeatedly tapped and not just held down. So I want only one 'jump' per spacebar press. Nothing that I've tried to workaround it has worked. How can I do this?
EDIT: I reworked the answer. The original solution used a counter which prevented the pressed key from having any impact for a certain period of time. Unfortunately, this was not what this question was about. :) The current solution is more straight forward and uses only a simple boolean lock.
Before answering the question, here are some annoying tips: I would suggest to use Map<KeyCode, Boolean> instead of List<String> to store information about what keys are currently pressed. It will simplify your code in terms of readability and give it a performance boost at the same time. Next, creating a dedicated object to store information about the pig (haha!) might be a good idea. Finally, using constants instead of hard coded literal values is a good practice.
Also, note that you don't actually need to store information about whether the spacebar is pressed or not and then refer to it from the timer thread. This would only be necessary if you WANTED the pig to be controlled by HOLDING the spacebar. But since you want it to jump only when the spacebar is pressed, you could tell the pig to switch into "jump" state directly from the handler. Of course, this won't solve your problem, because the onKeyPressed handler is invoked repeatedly when holding a key for a longer period of time. But I thought it was worth mentioning. :)
Now, to answer the question. If you want to quickfix your current solution and ignore all the "good practice" crap, focus only on the jumpLock field of the Pig class. The trick is to keep telling the pig to jump repeatedly as you are currently doing, BUT making sure that the pig will obey only when the jumpLock allows it to do so.
NOTE: The following solution assumes you will update the state of your game using a fixed interval like every 30 milliseconds. But as noted at the end, this solution can be easily modified to use FPS based timer.
The following class contains constants which you may want to change when tweaking your game in the future:
public final class Settings {
private Settings() {
}
public static final double BOOST_VELOCITY = 10.0;
public static final double GRAVITY = 0.3;
}
This class represents the pig. The x and y fields store information about current position of the pig. velocityX and velocityY are vectors containing information about the direction and "speed" of the pig in X and Y axis, respectively. jumpLock is a simple boolean flag which is actually a solution to your problem. Whenever user makes a jump, this lock is set to true. And it will remain so until it will be told to release the lock, which will happen when user releases the spacebar.
public final class Pig {
private double x;
private double y;
private double velocityX;
private double velocityY;
private boolean jumpLock;
public Pig() {
// ...
}
public void timeChanged() {
x += velocityX;
y += velocityY;
velocityY -= Settings.GRAVITY;
}
public void jumpBoost() {
if (!jumpLock) {
velocityY = Settings.BOOST_VELOCITY;
jumpLock = true;
}
}
public void releaseLock() {
jumpLock = false;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
}
Your handlers could look like this. Notice that Map<KeyCode, Boolean> is used to store information about currently pressed keys. It performs better than List<String> in this situation. Also adding the #Override annotation is a good practice even when overriding methods which are abstract:
final Map<KeyCode, Boolean> keyboard = new HashMap<>();
keyboard.put(KeyCode.SPACE, false);
scene.setOnKeyPressed(
new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent e) {
if (e.getCode() == KeyCode.SPACE) {
keyboard.put(e.getCode(), true);
// You could alternately call pig.jumpBoost()
// directly from this handler and not having to
// deal with the 'keyboard' map at all
// as illustrated with by pig.releaseLock()
// in the next handler
}
}
});
scene.setOnKeyReleased(
new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent e) {
if (e.getCode() == KeyCode.SPACE) {
keyboard.put(e.getCode(), false);
pig.releaseLock(); // IMPORTANT!!!
}
}
});
Finally, the following snippet of code must be executed repeatedly. This solution assumes this code will be executed in a fixed interval like every 30 milliseconds. If you are using FPS based timer (meaning there will be irregular interval between executions), you should pass the time which elapsed from the previous update as a parameter to the timeChanged() method, and multiply with it whatever necessary inside that method.
pig.timeChanged();
if (keyboard.get(KeyCode.SPACE)) {
pig.jumpBoost();
}
// Note that pig.releaseLock() could be called in else
// branch here and not in the onKeyReleased handler.
// Choose whatever solution suits you best.
// + draw image of the pig on pig.getX() and pig.getY() coordinates
Hope I got this right. I was almost asleep when writing this post and misunderstood the question at first. But I really need to earn some reputation points to be allowed to comment on an issue which is currently important to me. Haha! :D:D

Bukkit Delayed Task Inside a For Loop

I've been trying to make gun plugin for Bukkit, and I'm trying to build a burst fire feature. I have a for loop that contains a delayed task, and inside that delayed task is the code to create a bullet and all that. Theoretically, the code would add some bullets, wait one tick, and add some more bullets, wait one tick, etc until the for loop is done.
public void fire(final Player p, final Gun g) {
for(int i=0; i<shotsPerBurst; i++) {
Bukkit.getServer().getScheduler().scheduleAsyncDelayedTask(plugin, new Runnable() {
public void run() {
for(int i=0; i<bulletsPerShot; i++) {
Bullet b = new Bullet(p, g);
GunsV1.bullets.add(b);
}
}
}, 1L);
}
}
Eclipse demands that Player p and Gun g both be final, I don't know why, and when I try to run fire(p, g), nothing happens. How can I set up my code so the for loop will run with a delay of 1 tick between cycles?
To resolve one question:
Eclipse demands that Player p and Gun g both be final, I don't know why
You are passing the Player p and the Gun g to an new Thread/new Runnable and Eclipse tells you that those 2 Object's shouldn't be modified or changed because the Thread/Runnable also uses these 2 Object's inside the run method (as you can see).
I would suggest you to write your issue directly in here: http://bukkit.org/forums/plugin-development.5/ because there are also developer which know more and in detail about the Bukkit Server for Minecraft.
I will try to find a solution that fit's your needs now - but maybe you already try to find a solution in the forum.
Found this link for you - it may help you a little bit: http://www.programcreek.com/java-api-examples/index.php?api=org.bukkit.scheduler.BukkitScheduler
There's no easy way to run the for loop with a delay without freezing Bukkit's main thread. The best thing to do in this case is to use plugin.getServer().getScheduler().runTaskLater():
plugin.getServer().getScheduler().runTaskLater(plugin, new Runnable(){
public void run(){
//shoot the gun
}
},1L);//run after 1 tick
But, if you use this, the gun will only fire one shot. To fix this, you should keep running the scheduler:
public static void runTask(){
plugin.getServer().getScheduler().runTaskLater(plugin, new Runnable(){
public void run(){
//shoot the gun
runTask(); //run the task again
}
},1L);//run after 1 tick
}
But this way, the gun will keep firing every tick, and never stop. So, you should count the number of times it has ran, and stop running the task once the number is reached:
public static void runTask(final int timesLeft){
plugin.getServer().getScheduler().runTaskLater(plugin, new Runnable(){
public void run(){
//shoot the gun
if(timesLeft > 0){
runTask(timesLeft - 1); //run the task again after removing from the times left
}
}
},1L);//run after 1 tick
}
So, in the end, your loop method could look something like this:
public static void fire(final Player player, final Gun gun, final int timesLeft){
plugin.getServer().getScheduler().runTaskLater(plugin, new Runnable(){
public void run(){
Bullet bullet = new Bullet(player, gun);
GunsV1.bullets.add(bullet);
if(timesLeft > 0){
fire(player, gun, timesLeft - 1); //run the task again after removing from the times left
}
}
},1L);//run after 1 tick
}
and you could call it by using:
fire(player, gun, shotsPerBurst);
After experimenting with some longer delays and looking at the spigot reference, I realized that the ticks of delay are not ticks until the next task, but until the runnable runs. Knowing this, I was able to use the for loop to increase the ticks of delay proportionally:
public void fire(final Player p, final Gun g) {
for(int i=0; i<shotsPerBurst; i++) {
Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
public void run() {
for(int i=0; i<bulletsPerShot; i++) {
Bullet b = new Bullet(p, g);
GunsV1.bullets.add(b);
}
}
}, i*3);
}
}
Now it runs each task three ticks after the previous one, and fires in a burst

Refreshing JTable step by step in Java

I am doing a Pacman game using A* algorithm in Java. I searched a lot of questions. I found a solution for one step. But I want to refresh my table in while block and my solution is just refreshing JTable according to the last step(just showing the result) in while(calculated all steps). But I want to refresh and show Pacman's places(location) step by step in while block. It has to look like Pacmans are moving. But I couldn't. My codes is below:
btnStartGame.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
//randomly placement of Pacmans
table_1.setValueAt(yellowPacman, locationyellowX, locationyellowY);
...calculating heuristics
//after calculation
newXYellow =locationyellowX;
newYYellow =locationyellowY+1;
nodeYellow = 10 * newXYellow + newYYellow;
while(heuristics != zero){
...enemy pacmans' movement
//after enemy pacmans' movement calculating yellow pacman's movement
if((newXYellow>=0 && newXYellow<10 && newYYellow>=0 && newYYellow<10) && !wallList.contains(nodeYellow)){
//calculate heuristic again
manhattanDistance[0][0] = Math.abs(newXYellow-locationblackX[0])+
Math.abs(newYYellow-locationblackX[0]);
manhattanDistance[0][1] = Math.abs(newXYellow-locationblackX[1])+
Math.abs(newYYellow-locationblackX[1]);
manhattanDistance[0][2] = Math.abs(newXYellow-locationblackX[2])+
Math.abs(newYYellow-locationblackX[2]);
fyellow[0] = manhattanDistance[0][0] + manhattanDistance[0][1] + manhattanDistance[0][2];
selectedNodeXYellow = newXYellow;
selectedNodeYYellow = newYYellow;
timer2.start();//updated
while(delay != 0)
delay--;
delay = 4000;
locationyellowX = selectedNodeXYellow;
locationyellowY = selectedNodeYYellow;
nodeYellow = 10 * selectedNodeXYellow+ selectedNodeYYellow;
timer3.start();//updated
while(delay != 0)
delay--;
delay = 10000;
}//ending if
}//ending while
}
}//ending action
timer2 = new Timer(ONE_SECOND, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
table_1.setValueAt(null, locationyellowX, locationyellowY);//I wanted to delete old moves
timer2.stop();
}
});
timer3 = new Timer(ONE_SECOND, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
table_1.setValueAt(yellowIcon, locationyellowX, locationyellowY);//here I want to show each moves step by step in while block
timer3.stop();
}
});
UPDATE 1 :
Delay was just an idea. Maybe algorithm is calculated too quickly and timers cannot be fired. But it didn't work and also does not timer suppose to be fired at each one second? still I've seen the last values of JTable.
I suspect your main issue here is that you need to understand how the various threads in a Java GUI application work. Specifically, if you have code executing in the event dispatch thread then there will be no updates to the UI while that code is executing. In your case you are making multiple updates to a JTable and then returning control to the display which will then show the last value.
The solution here is to introduce a timer element that executes each step of your algorithm in each tick of the timer and then returns control to display the result. Check the javax.swing.Timer class for details. I also suggest you read the sections 'concurrency in Swing' and 'How to use Swing Timers' in the Java tutorials.

Resetting a new thread repetitively

In my program, there is a button, "Display", and another button, "Reset".
The user enters the number of prime numbers they want in the text field and then clicks the "Display" button. Then, the first x prime numbers will appear in the text area.
In the code, I have:
Declarations:
Thread go;
Thread newThread;
JLabel howManyLabel;
JTextField howMany;
JButton display;
JButton reset;
JTextArea primes;
Action Event:
public void actionPerformed(ActionEvent event) {
Object source = event.getSource();
if (source == display) {
display.setEnabled(false);
if (go == null) {
go = new Thread(this);
go.start();
} else {
newThread = new Thread(this);
newThread.start();
}
} else if (source == reset) {
display.setEnabled(true);
howMany.setText(" ");
primes.setText(" ");
}
}
Run method:
public void run() {
int quantity = Integer.parseInt(howMany.getText());
int numPrimes = 0;
int candidate = 2; // candidate = the number that MIGHT be prime
primes.append("First " + quantity + " primes:");
while(numPrimes < quantity) {
if (isPrime(candidate)) {
primes.append(candidate + ", ");
numPrimes++;
}
candidate++;
}
}
The run() method is in the same class, and simply calculates the first x amount of prime numbers.
I am trying to create a new thread every time the "Reset" button is called. The thread runs the first time, but then does not run again after I click "Reset". Can the run() method only work once?
Thanks in advance.
The run() method is just like any other method and can be invoked any number of times. The method that cannot be invoked multiple times is start()(according to Thread).
Your explanation does not seem to fit with the code you gave. You say you want to spawn a new thread when a user clicks reset, yet you only construct or start threads if the source is Display. Did you mean, rather, that you want to cancel the last thread and enable controls for the user to start again? In that case you should use a Future, not a generic thread.
Another thing is that UI components are generally not thread-safe. Swing explicitly warns against it on every components JavaDoc. What you may be seeing is just components not updating their visible state when they are changed from a different thread. Have you tried using a debugger to see if a thread is actually not getting spawned, or it is being spawned, but not having the result you want?

Properly implementing a delay in a game

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();

Categories