I've implemented a game where a player can move a sprite around in a tile-based maze. The player controls the sprite with the arrow keys. What I want is to restrict the speed at which the player can move around, e.g. I don't want them to be able to hold down the arrow key and fly across the screen. I tried fixing this by implementing a sleep:
switch (keyCode) {
case KeyEvent.VK_UP: // Up arrow key
if (running) {
player.Move(1); // Move North
}
paintPlayer(getGraphics());
// So the player can't hold down the arrow key and fly across the screen, force them to wait between inputs
// BUT this leads to problems if you do hold it down, moves end up in a 'queue'...
try {
Thread.sleep(150);
break;
} catch (InterruptedException ex) {
Logger.getLogger(MazeView.class.getName()).log(Level.SEVERE, null, ex);
}
break;
// etc.
But this leads to problems if you hold the arrow key down - the moves seem to end up in some kind of "queue" and you end up constantly crashing into a wall until all the moves from your extended key press are done. Is there a better way of doing this?
Previously I was doing a keyPressed() event, but as Riyafa suggested, I changed it to only fire on keyReleased(), and that's done it. There's no need to include sleeps anymore either.
I advise to investigate Switng Timer. As the name suggests its action is performed in the same thread as your Key event listener and all other GUI stuff, so you can easily painting there and update game state, based on key/button states. You can also use swingutilities.invokelater(new runnable() if you are waiting is some another thread. I did not do Swing for a long time and forgotten everything but I am sure that this is how dynamic games are supposed to be done.
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 want to rotate my object when I PRESS_DOWN button and stop rotating when I PRESS_UP.
I tried to do it with OnTouchListener but seems there is no way to kmow that button is still pressed. For example I touch button 1st time, and there is 1st touch event raised with ACTION_DOWN, but then if I keep my finger not moving nothing will happen (no more events). Or if I will move it very fast there will be different speed of raising on touch event ACTION_MOVE. Different speed that depends on finger's speed moves is unacceptable for my task. So I decided to start timer (that do rotating with fixed speed) when ACTION_DOWN raised and to cancel TimerTask when ACTION_UP is raised, it works good, just like I need. But I think it's not the best solution or even the worst. Please give me an advice of other possible solutions.
Your solution of using a timer is good, and is what you are supposed to you.
If you are running an animation, you cannot rely on UI events, you have to have a separate thread or timer for periodic updates.
Using timer is a good way. To my knowledge, I think using a simple if-else may solve your problem more efficiently. For example,
boolean state = true;
if(state)
{
// do the code for ACTION_DOWN
}
else
{
// do the code for ACTION_UP
state = true;
}
Give a loop to this if statement, as many times you want. But I don't know, whether this solution meets your requirements, or not.
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.
Hi Everyone,
in the game I develop with AndEngine, there are a lot of sprites running around. Now every of this sprites has a TouchArea registered to the scene, because I display some informations about the sprite when it is touched. The Scene itself has a OnSceneTouchListener that I use for moving the camera around and to zoom.
My problem is, that every time the user moves the camera (by touching the display somewhere and moving his finger around) the OnAreaTouched() method of any sprite, that is accidentally under the finger, gets called, when the movement is finished (finger gets lifted). I already limited the triggering events to event.getAction()==UP (before it was a real mess of called touchAreas) but this is not enough. If the user is zooming or moving the camera, the sprites touchAreas should not be activated.
Is there any way I can distinguish between an OnAreaTouched-event and an OnSceneTouched-event? Which one is called first and can I suppress the other?
This is my OnSceneTouched() method (simplified):
public boolean onSceneTouchEvent(Scene scene, final TouchEvent event) {
boolean isZooming = event.getMotionEvent().getPointerCount() >= 2;
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// REMEMBER FIRST TOUCHPOINT, TO KNOW IN WHICH DIRECTION TO MOVE
this.touchPoint = new Point(event.getMotionEvent().getX(), event.getMotionEvent().getY());
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
if (isZooming) {
// DO SOME ZOOM STUFF
} else {
// DO SOME MOVEMENT STUFF
}
return true;
}
OK, actually this is not very interesting - but as you can see I always return true to signal that the touch event was handled. Still the OnAreaTouched() gets called
This is a typical OnAreaTouched() Method of a sprite:
public boolean onAreaTouched(final TouchEvent touchEvent, float touchAreaLocalX, float touchAreaLocalY) {
if (touchEvent.getAction() == TouchEvent.ACTION_UP) {
// DISPLAY INFORMATION ABOUT THE SPRITE
return true;
}
return false;
}
You see, there is nothing special to it. So I hope someone can help me out here to find a solution how to suppress the OnAreaTouch-event when the OnSceneTouch-event should be used. Maybe I can somehow catch the event.getAction()==UP in the OnSceneTouched()-Method??
I hope I could explain the problem good enough for you to understand (sorry, it's not that easy for me : ). Any help is much appreciated, and thank you for you time!
regards
Christoph
edit:
After experimenting with MahdeTo's suggestion to tag the event somehow I found out the following:
the TouchEvent that triggers the OnSceneTouchEvent() Method is not the same as the one triggering the OnAreaTouched() Method.
OnAreaTouched() gets called 20 ms later than OnSceneTouchEvent()
the event calling OnAreaTouched() starts actually when the user puts his finger down on the display (than he moves it around and the OnSceneTouchEvent() gets called multiple times), when he then lifts his finger the first event stops and gets handled. (I tried it out by measuring the time)
So I came up with the solution to measure how long a touch event lasted. If the event is longer than 200 ms, I guess the user wanted not to simply click but move or zoom (because these actions usually take longer). So now the OnAreaTouched() method gets only called when someone really meant to click and not accidentally swiped over the area.
but it's still not a good solution and I would really appreciate if anyone knows more about controlling such events.
thank you
I have recently written a fairly complicated application using touch controls - pressing buttons on the screen, pinch zooming, rotating and moving objects with two fingers and more. What I ended up doing was iteratively improving the application and adding rules controlling how to respond to different combinations of touch events. Whenever something was wrong, I recorded the sequence of TouchEvents and added rules so that the misbehaving action was handled.
You can also create boolean switches that prevent the onAreaTouched to execute, just add a condition that, for example if you want to touch an object, checks whether doNotTouchObject is true or false.
Another thing I found useful were touch mode switchers. I wrote several methods that completely change the behavior when user touches the screen. I believe you can switch them on the go, so it should be possible to call scene.setOnAreaTouchListener(touchListener) while you are touching the screen.
Since un/registering touch areas is blazing fast, simply unregistering certain touch areas while you perform some action is a possibility. After the action is completed, reregister them again.
I used a combination of all of the above and it works rather well, but it is essential to keep the code clean, otherwise debugging or implementing new features will be a pain.
maybe you can use the TouchDetector.
use the clickDetector instead of process TouchUp event, then only the detected click event is processed by the sprites;
use the scroll detector for the Scene Scrolling too;
enable the SceneTouchListenerBinding and TouchAreaBinding will also help you to by pass all the unintended in-progress events.
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.