I'm a little bit new to Key Bindings, as I have been using KeyListeners for the majority up until recently when KeyListeners proved to be my biggest obstacle. I'm wondering how you would program a keyReleased-like event with KeyBindings. KeyListeners provided the easy three methods: keyPressed, keyTyped, and keyReleased, but I'm a little bit confused as for how to make this happen with Key Bindings.
Basically, when the user presses UP, I want an object to move upwards. But when the user releases UP, the object should move downwards automatically to simulate basic gravity. Here's a little bit of my code showing the UpAction class.
class UpAction extends AbstractAction
{
public void actionPerformed(ActionEvent tf)
{
north = true;
helitimer.start();
helitimer.start();
helitimer2.start();
repaint();
}
}
The three helitimers are Timer objects that start a series of Timers to increment the y position of the object continuously and smoothly. When the action upAction is invoked, the class UpAction is called and the three timers start in order to move the object.
Is there anyway I could make it so when the user releases UP, the action is no longer invoked and the timers stop?
Thanks a lot!
I'm wondering how you would program a keyReleased-like event with KeyBindings
Exactly the same way you do for a keyPressed event. The difference is in the KeyStroke. Read the KeyStroke API, is shows how to create a KeyStroke for a keyReleased event. There are a couple of different ways depending on how you want to create the KeyStroke.
The KeyStroke methods assume keyPressed, so you will need to add an additional parameter or additional keyword for the keyReleased event. I don't know which method you are using to create the KeyStroke so I can't tell you the exact change. Check the API for the details.
The three helitimers are Timer objects that start a series of Timers to increment the y position of the object continuously and smoothly
You should not need 3 Timer for this. One Timer should work satisfactorily.
Is there anyway I could make it so when the user releases UP, the action is no longer invoked and the timers stop?
If you can "start" a Timer in your keyPress Action, then you can "stop" the Timer in the keyReleasedAction. All you need is a reference to the Timer. Based on the code you posted you already defined the Timer as a class variable so this should not be an issue.
But when the user releases UP, the object should move downwards automatically to simulate basic gravity
Sounds to me like you will need another Timer to do this.
Another option is to always have a Timer running. Then when the UP key is pressed you make the Y increment a negative value. When the UP key is released you make the Y increment a positive value.
Related
Right now, the object moves a certain increment each time I manually click a button, but its actually suppose to move on its own across the screen on its own once the button is clicked. I tried calling timer.start(); various times in my code. As well as, setting up an 'if' statement in the actionPerformed method that checks for a button being pressed and then calls timer.start() as a result. But, it didn't get the object to move on its own.
Can anyone lead me in the right direction? Am I not writing the code right? Or is does this problem have something to do with java swing timer.
PS. I am new to java,
And this is part of my code :
public void actionPerformed(ActionEvent e){
if (e.getSource() == rightBtn) {
objXpos += objMoveIncrement;
direction.equals("Right");
}
if (e.getSource() == leftBtn) {
direction.equals("Left");
objXpos -= objMoveIncrement;
}
repaint();
}
}
**edit
the timer is suppose to start once a button is clicked, and the timer is what allows the object to move across the screen
this problem have something to do with java swing timer.
No.
Am I not writing the code right?
That would be the problem.
the timer is suppose to start once a button is clicked
And how does the Timer stop? What happens if you click "right" and then "down"?
Without knowing the exact requirements it is hard to give an exact solution.
So I would suggest that one solution is to just start the Timer when your program starts.
Then in the ActionListener for each button, you change the direction.
Then when the ActionListner for the Timer is invoked, you simply move the object based on the current direction and then repaint the object.
Generally you would use Key Bindings for something like this. So when you press a key you start the Timer and when you release the key you stop the Timer. Check out the Motion With Key Bindings example from Motion Using the Keyboard for a working example of this approach.
I am coding a program whose functions include one that checks whether the user input is one of the arrow keys and moves an on-screen sprite accordingly.
I am using the KeyListener interface for an inner class I call ArrowListener. Currently, I have code in the keyPressed() that moves the sprite on the screen.
I am wondering how often my ArrowListener class checks for keyboard input, because I have another method in the larger component class that calls repaint() every 100 milliseconds. If the KeyListener class checks user input more or less often than this, I will change the repaint frequency as well.
EDIT:
I realized that KeyListener doesn't check/poll the keyboard for inputs regularly, but processes interrupts from the keyboard. Still, if I were to hold down a key on the keyboard for, say, 5 seconds, how many interrupts would KeyListener process?
It looks like I was mistaken; what you're looking for are KeyBindings; these allow you to execute an ActionListener only once when a key is pressed.
I'd like to know what object a swing MouseMotionEvent (or MouseReleased) ends in. The problem is that both the MousePressed and MouseReleased events go to the object that was under the "press", not the release.
Here's a contrived example that may explain better:
The user sees a screen with some balls and some baskets and is told to drag a ball to a basket. Each ball represents some entity in the application space and each basket represents some action to take in the application space. From the Swing point of view, the balls and baskets are implemented separately as highly-overridden JButtons. On mousePressed the ball stores its identity in a known place. I'd like mouseReleased to be caught by a MouseListener in the basket which checks up the balls identity in the known place and then goes off into program logic and do the task represented by that basket.
But as I understand Swing (actually the AWT) the mouseReleased event goes to the component that contained the mousePressed event (ie the ball). Other than looking at X and Y (which seems an atrocious kludge) how do I figure out which basket the mouseReleased happened in? (If the mouseRelease happened outside of any basket, I'll need to take some sort of default reset action. Than can be done by a mouseEvent handler in the underlying JPanel).
(Please don't tell me this is a poor interface. The example I've given is not real. It abstracts out the problem I have in a way that I think is easy to visualize and understand.)
If the mouseRelease happened outside of any basket, I'll need to take some sort of default reset action -
Use the Drag and Drop API and then you will only be able to drop on components that support your drop.
Other than looking at X and Y (which seems an atrocious kludge)
Why? The event doesn't have the information so you need to get it somehow. So if you don't want to use the DnD API then you need to do this yourself.
There are methods in the API to help you do this:
Window window = SwingUtilities.windowForComponent( e.getComponent() );
Point dropPoint = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), window);
Component dropComponent = SwingUtilities.getDeepestComponentAt(window, dropPoint.x, dropPoint.y);
I'm probably doing this wrong, so please be nice.
I'm developing a Java game, and I'm at the stage of testing character movement / animation.
The "person" can move up down left and right on a grid.
The class the grid is drawn in is the gamePanel class.
The buttons are in the gameControlPanel class.
I have a button which spawns a person on the grid.
I then have a button to move the person up down left and right.
When the move up button is pressed, it calls the move up method from the person class.
(At the moment, I'm only testing one "person" at a time.)
In that method is the following code...
int move = 10;
while(move!=0)
{
setTopLeftPoint(new Point((int)getTopLeftPoint().getX(),
(int)getTopLeftPoint().getY() - 3));
try
{
Thread.sleep(300);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
move-=1;
}
The problem is that I can't seem to call the repaint method for the gamePanel class from within the Person class.
To get around this, I created a timer in the gamePanel class which repaints every 20ms.
When I press the up button after the person is spawned, the button remains pressed down until the cycles of the while loop have been completed, and then the circle representation of the person is displayed in the grid square above.
I will try to answer any questions regarding this.
repaint() does not immediately repaint the GUI. Rather, it posts a message to the AWT thread telling it to paint at the next convenient opportunity. When it gets the chance, it will repaint your app. However, if you do this in an event handler, then the AWT thread is already busy and you need to exit the handler to give control back to the AWT handler.
As a general rule of thumb, you don't want to do any long-running calculations on the AWT thread (including in event handlers) since these will stop the app from responding to other events until your calculations are done. This will often appear to the user as stuck buttons like you described. To get around this, use a SwingWorker which can do the calculations on a separate thread.
Finally, something to be aware (but not necessarily to change) is that timers and sleeps do not guarantee when they will awaken. Rather, they guarantee that they will not waken before the amount of time elapses, but can theoretically sleep indefinitely. Also, not all machines have 1 ms timer resolution. In particular, on many windows machines the timers only have 55 ms resolution, so a 20 ms timer may give weird results.
If you want to repaint at a certain interval, javax.swing.Timer is probably the class for you. In the specific case of repaint you can call it from a non-EDT thread, but you may get yourself into difficulty as you are now dealing with multiple threads.
I don't have much experience making games but having a loop to control all animation is a fundamental aspect of game programming. Most simple 2d games only have 1 loop to render most of its animation.
In my experience a good way to render a whole bunch of stuff is to have a collection of all the entities in your game in one place and just loop over this collection passing the Graphics object to each entity.
This will allow each entity to draw itself onto the graphics object. Although this is just one way to do it.
synchronized ( entities ) {
for ( Entity e : entities ) {
e.draw( g );
e.doAction();
}
}
Is the button press handled by the event-dispatch thread of your GUI?
If this is a case, then the repaint method on the GUI will not fire until the event dispatch thread ends (i.e. when the button is released and it breaks out of the loop). I had this problem recently, and the best solution I can suggest is to make the class with the movement algorithm threadable and fire off a thread when the keypress is detected. This allows the event-dispatch thread to finish and therefore allows the gui to repaint.
For more information on threading see Starting a thread.
I'm experiencing some strange behaviour when using a stylus with swing.
I am interpreting pressing the button on the side of the stylus(RIGHT) and pressing the stylus down(LEFT) as a "Grab" event, but occasionally (more often than 0), events are just being dropped.
The JavaDocs for MouseEvent are pretty explicit about how multibutton presses are handled if executed one at a time (left down, right down, right up, left up) but say nothing about simultaneous button presses.
I'm left to wonder, would they be emitted as two mousePressed events, or as one with the button mask set for both buttons, or something else entirely?
Thanks.
I'd interpret the API doc as simultaneous button presses being simply not possible:
When multiple mouse buttons are pressed, each press, release, and click results in a separate event.
So there should be separate events. The problems you observe could be due to errors in your code, the stylus' driver, the hardware, or Swing (this is in decreasing order of likelihood as I see it :)
I'd try to diagnose the problem by logging events at different levels, if possible.
Simultaneous button presses are processed as two separate mousePressed events. Run the Mouse Events Demo to see them processed separately.
As I recall, there's no way to handle simultaneous button presses. What I used to do to ensure that multiple buttons being pressed at once were treated as such was I would have a boolean variable for each button and when it was pressed, I would set it to true and when it was released, I would set the boolean to false. Then when it came time to perform an action, I would check for the boolean variables (sometimes I would have the actionlistener redirect to the method call for determining what action was to happen next after setting the booleans). This doesn't work if the only thing you want to do is them being pressed at the exact same time, but if you're just trying to get combinations to work, then that's how I did it. This was about 4 years ago, before Java 5, so I may be wrong about that.