I'm following this [slightly old tutorial for 2d java games here][1].
I have a basic applet that runs in a thread with a mouselistener.
On left button click I can shoot up to 10 claypigeons (balls) from the bottom of the window. On right button click I "shoot" and if I hit a pigeon it is removed from the screen.
I've noticed however that sometimes right clicks are not getting picked up. This is not necessarily when there is a lot going on on the screen, although never at the beginning before it all kicks off. At worst it can take 3 or 4 clicks before one is registered.
I'm guessing that I'm doing something obviously wrong in my code, but I'm not sure what. My first thought was the for loops that loop through every object every frame to recalculate their position or check if they have been "shot"? Could they been "blocking" the mouselistener?!
Any tips on how to debug this are welcome!
*********EDIT***********
Ok I've taken the very good advice given below and reduced the code to the smallest nutshell that reproduces the bug. I think I had it in my head that it was all the for loops and the complexity that were causing the problems which is why I included so much in my first code.
So as it happens I can reproduce this bug with almost no code, but at the same time it the bug is much milder. With the code below I just have the basic applet and the mouselistener and on right click a count increments in console and prints its value to screen. This works fine most of the time, but every now and again, a right click is "lost" and not registered.
With the full class, I could sometimes get sequences of 3 or 4 or more right clicks not being registered, so the bug was much more obvious. Anyway code is below, only one class this time:
Main class code:
package javacooperation;
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.net.*;
public class ClayPigeonGame extends Applet implements Runnable, MouseListener {
//counters and flags for debugging
private boolean flag = true;
private int click_count = 0;
private boolean game_running;
public void init(){
//set boolean for while loop
game_running=true;
addMouseListener(this);
}
public void start() {
//threading this applet.. why?
Thread th = new Thread(this);
//does this call the run method in the class?
th.start();
}
public void stop(){
game_running=false;
}
public void destroy() { }
public void run(){
while(game_running) {
//updatePigeonPosition();
repaint();
try{
//stop thread for 20 milliseconds
Thread.sleep(20);
} catch (InterruptedException ex){ }
}
}
public void paint(Graphics g){ }
public void mouseClicked(MouseEvent e) {
switch (e.getButton()){
case MouseEvent.BUTTON1:
case MouseEvent.BUTTON2:
break;
case MouseEvent.BUTTON3:
click_count ++;
System.out.println("Right click count: " + click_count);
break;
default:
break;
}
}
//all the mouse listening events required because we implemented MouseListener
public void mouseReleased(MouseEvent e) { }
public void mouseEntered(MouseEvent e) { }
public void mousePressed(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
}
It's possible I guess that your code is interfering with the mouse clicked detection timing as a click is actually both a press and a release with the correct bounds etc. Have you tried not using the clicked call back and instead use the pressed (move the code to mousePressed instead) ?
Related
I'm new to JAVA and trying to learn some concurrency concepts.
I have a simple GUI class that pops-up a window with 1 button which I want to use for pause/continue.
Also, I have a class that extends TimerTask, it looks like below and start with the GUI:
public class process extends TimerTask {
public void run() {
while(true) { /*some repetitive macro commands.. */ }
}
}
Real question is, how can I pause the task onClick of the button and also continue onClick of the button if already paused?
I have taken a step to use a boolean to flag the button as it changes from pause to continue on each click.
But then I had to type a lot of while(button); for busy waiting inside the while()...
Do you think I can make like Thread.sleep() or something but from outside the task thread?
OLD ANSWER
Basically, there is no support for pause and resume on TimerTask, you can only cancel, check here
perhaps you might want to read about threading as that's the alternative I know of that has an interrupt and start features and then you can keep track of the progress of what you're doing to resume where it stopped.
So, I will suggest you go through this link, because you need to understand threading not just copy a code to use, there is a sample code there that will definitely solve your problem also.
Note that running an endless while loop will basically cause your program not to respond, unless the system crashes. At a certain point, the data becomes an overload and the program will overflow. This means it will fail.
.
NEW ANSWER
So, response to the new question, I was able to run a tiny little program to demonstrate how you can achieve something that looks like multithreading when working with SWING.
To rephrase your question: You want to run an indefinite task like let say we're playing a song, and then onclick of a button to pause the song, on click again should continue the song?, if so, I think below tiny program might work for you.
public class Test{
static JLabel label;
static int i = 0;
static JButton action;
static boolean x = false; //setting our x to false initialy
public static void main(String[] args) {
JFrame f=new JFrame();//creating instance of JFrame
label = new JLabel("0 Sec"); //initialized with a text
label.setBounds(130,200,100, 40);//x axis, y axis, width, height
action=new JButton("Play");//initialized with a text
action.setBounds(130,100,100, 40);//x axis, y axis, width, height
f.add(action);//adding button in JFrame
f.add(label);
action.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
if(x){
x = false; //Update x here
action.setText("Play");
action.revalidate();
}else{
x = true; //Update x here also
action.setText("Pause");
action.revalidate();
if(x){ //Using x here to determind whether we should start our child thread or not.
(new Thread(new Child())).start();
}
}
}
});
f.setSize(500, 700);//500 width and 700 height
f.setLayout(null);//using no layout managers
f.setVisible(true);//making the frame visible
}
}
class Child implements Runnable{
#Override
public void run() {
while (x) {
//You can put your long task here.
i++;
label.setText(i+" Secs");
label.revalidate();
try {
sleep(1000); //Sleeping time for our baby thread ..lol
} catch (InterruptedException ex) {
Logger.getLogger("No Foo");
}
}
}
}
I'm studying Java and learning how to use mouse listener. However here is a very simple code which doesn't work.
import acm.program.*;
import java.awt.event.*;
public class Test extends GraphicsProgram{
public void run() {
isMouseClicked= false;
addMouseListeners();
while (true) {
if (isMouseClicked) {
println ("OK");
break;
}
}
}
public void mouseClicked(MouseEvent e) {
isMouseClicked= true;
}
private boolean isMouseClicked;
}
The idea is very simple. "isMouseClicked" is false at the beginning, and once mouse is clicked, it turns to true and print "OK" in the screen. The problem I have is that, if I run in a normal mode, no matter how I click mouse, it will not hit and print "OK". However if I run it in the debug-mode. After I clicked mouse, put a breakpoint on
if (isMouseClicked);
Then it turns out that it is true and "OK" is printed. Can anyone tell me why is that? Many thanks in advance.
I've just started using LWJGL and so this is an incredibly basic problem to run into but I've scoured the internet and can think of no solution. Also probably pertinent: I'm on a Mac.
Even when I press the x button, the method Display.isCloseRequested() refuses to return true. Below is the code for my entire "window" class, which is basically a shell for the display object.
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.*;
public class Window {
public static void createWindow(int width, int height, String title){
Display.setTitle(title);
try {
Display.setDisplayMode(new DisplayMode(width,height));
Display.create();
} catch (LWJGLException e) {
e.printStackTrace();
}
}
public static void render(){
Display.update();
}
public static boolean isCloseRequested(){
System.out.println(Display.isCloseRequested());
return Display.isCloseRequested();
}
public static int getWidth(){
return Display.getDisplayMode().getWidth();
}
public static int getHeight(){
return Display.getDisplayMode().getHeight();
}
public static String getTitle(){
return Display.getTitle();
}
}
This class is being referenced by a method in a main class, that looks like this:
private void run(){
isRunning = true;
while(isRunning){
if (Window.isCloseRequested())
stop();
render();
}
}
The stop method is never being run, even when I click the x-button.
Did some simple tutorials like http://ninjacave.com/lwjglbasics1 produce similar faulty behavior?
Important: i think there is also an reset of an intern Event which id caused through press the x Button, if you call the isCloseRequested twice.
So try to save the event and removed the syso-Line
Is your aim to simply stop the render() process when the app window is closed?
If so, just add this to your main JFrame definition:
//replace frame with your main JFrame containing the X close button
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent we) {
isRunning = false;
frame.setVisible(false);
frame.dispose();
}
});
...and change your 2nd code to this:
private void run(){
isRunning = true;
while(isRunning){
render();
}
}
This way when you close your app main window the value of variable isRunning is changed to false and your while() loop will end terminating your render().
In fact, I was dealing today exactly with this problem myself. I hit your post prior the solvement of my problem, but once I did solved it, I return here to post my solution (or at least the way it worked for me).
Surely, you have to make the same isRunning variable be available to both.
In fact I too never found out why that isCloseRequested() never change to false after I closed my app. Nevermind, this is my workaorund - hope it will work for you too.
I want to move a image across the screen by 16 to the right when the arrow key is pressed. I want to move it with a speed(1px/10ms) until reaches the point. The image is created inside a class that is child of JPanel.
I wrote the next code but the image changes the position instatly instead making a movement:
public class Test extends JFrame implements KeyListener {
private int x=0;
private int y=0;
BufferedImage img;
...
...
public void paint(Graphics g){
g.drawImage(img,x,y,null);
}
// Move to a point 16 pixels to right
public void moveRight(){
for(int i=0;i<16;i++){
x++;
repaint();
try {
Thread.sleep(10); // Sleep 10 milliseconds until next position change
}catch (InterruptedException e) {}
}
}
public void keyPressed(KeyEvent e) {
if(e.getKeyCode()==KeyEvent.VK_RIGHT){
moveRight();
}
}
}
The problem is your sleep inside the EDT (Event-Dispatching-Thread). repaint() triggers an event that will be dispatched by the EDT and will in turn perform the actual repainting of your component. Since you are blocking the EDT, the repaint does not perform directly (but after the end of all your code then a single repaint event occurs (because repaint events are grouped whenever possible). You will probably need to use SwingWorker to fix this issue.
What if you call moveRight() in another thread?
try this:
public void keyPressed(KeyEvent e) {
if(e.getKeyCode()==KeyEvent.VK_RIGHT){
new Thread(new Runnable(){
public void run(){
moveRight();
}
}).start();
}
}
I've not tested and I even don't know if this is a good aproach
Im trying to write a program which moves the mouse every 3 minutes (to stop a screen saver coming on) but I want to be able to stop and start it at will. As you can see below I have create the buttons and method but when you click run it steps into the while loop and because its in effect an infinite loop it won't look and see if you have clicked the end button.
I have tried system.exit(0) on click for the end button, having the end button pass in false to the method run() and as you can see from the code ive tried an if statement in the while loop to see if it will take notice of me!
Any help would be greatly appreciated!
code:
import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Robot;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Test
{
boolean loop;
static boolean exit;
public static void main(String[] args) throws AWTException
{
System.out.println("before");
makeButtons();
System.out.println("after");
}
public static void makeButtons()
{
JFrame jfrMain = new JFrame ("Mouse Robot");
JPanel jplMain = new JPanel();
final JButton run = new JButton("Run");
final JButton end = new JButton("End");
run.setEnabled(true);
end.setEnabled(true);
run.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
//run.setEnabled(false);
//end.setEnabled(true);
try {
run(true);
} catch (AWTException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
});
end.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
exit = true;
}
});
jplMain.setLayout(new FlowLayout());
jplMain.add(run);
jplMain.add(end);
jfrMain.getContentPane().add(jplMain, BorderLayout.CENTER);
jfrMain.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jfrMain.pack();
jfrMain.setVisible(true);
}
public static void run(boolean loop) throws AWTException
{
Robot r2d2 = new Robot();
while(loop)
{
System.out.println("1");
Point mousePoint = MouseInfo.getPointerInfo().getLocation();
mousePoint.translate(0, 1);
r2d2.mouseMove(mousePoint.x, mousePoint.y);
r2d2.delay(60000);
//r2d2.delay(60000);
//r2d2.delay(60000);
System.out.println("2");
mousePoint = MouseInfo.getPointerInfo().getLocation();
mousePoint.translate(0, -1);
r2d2.mouseMove(mousePoint.x, mousePoint.y);
r2d2.delay(60000);
//r2d2.delay(60000);
//r2d2.delay(60000);
System.out.println("looping");
if (exit = true)
{
break;
}
}
}
}
Well first correct the condition for exit i.e., make it exit == true as mentioned in the first answer.
Second I don't think even this is going to fix your problem because you are making an infinite loop in the actionPerformed which gets called by EDT (Even Dispatch Thread) and this will halt the event processing altogether. So instead start a new Thread inside the actionPerformed method that moves the mouse. Keep a reference to that thread so that you can stop/interrupt the thread or you can also set the exit condition to stop the thread.
Let me know if you need a code example for this.
Try
if (exit == true)
{
break;
}
if (exit == true)
{
break;
}
I have some doubt about that working out, though I have yet to try it. Wouldn't Java run while without ever listening to mouse, which will end up as infinite loop?
I think it'll have to be some equivalent of javascript function setInterval() to move the mouse and clearInterval() once the button has been clicked.
first of all make it exit == true;
instead of using exit = true; you can use loop = false.
Still it will not stop while loop as soon as you click on end button.
To stop it immediately after button clicked you must use two different threads.
1. Handling your events.
2. another one running the while loop.
In event handling thread you have to maintain a object of while loop thread, by which you can set appropriate value of loop or exit variable to stop the loop.