I want to have a game where the view will move around as the mouse reaches the outer edge of the window (similar to many RTS games). I have read that there is significant overhead when using the MouseMotionListener.
Is there perhaps a way of having a second transparent component within the game window (JPanel) that does not affect game-play, but will register when the mouse leaves the inner component via MouseAdapter.mouseEntered()/mouseExited()?
boolean mouseOnScreen;
boolean mouseWithinInnerComponent; //is (10 <= mouse.x <= screenWidth - 10) && (10 <= mouse.y <= screenHeight)
if(mouseOnScreen && !mouseWithinInnerComponent)
{
//do something...
}
I am at a loss for how to determine which screen boundary was crossed without having to have four of the above mentioned components overlapping at the corners to form a border around the screen which can detect if the mouse is at any edge or corner. This I imagine to be fairly expensive (having to check each component while running the game)...
boolean mouseOnScreen;
boolean mouseWithinTopComponent; //is (0 <= mouse.y <= 10)
boolean mouseWithinBottomComponent; //is (screenHeight - 10 <= mouse.y <= screenHeight)
boolean mouseWithinLeftComponent; //is (0 <= mouse.x <= 10)
boolean mouseWithinRightComponent; //is (screenWidth - 10 <= mouse.x <= screenWidth)
if(mouseOnScreen)
{
if(!mouseWithinBottomComponent)
{
//move view up
}
if(!mouseWithinTopComponent)
{
//move view down
}
if(!mouseWithinLeftComponent)
{
//move view right
}
if(!mouseWithinRightComponent)
{
//move view left
}
}
Exactly how much overhead exists with MouseMotionListener? Would this or a similar method perhaps be more efficient if the detections only need to be made along the borders of a game window?
NOTE: This will be used in windowed mode as well as possible full-screen application.
I have implemented the same thing you need using a MouseMotionListener. I'm not really sure why you think it will add overhead... If you add one and simply ask each of its methods to print to the console (which is slow) and move your mouse around, you will see it is pretty snappy from the user's point of view.
My implementation consists of 4 main pieces: a scroll pane, rectangular regions, a timer, and a MouseMotionListener.
First, I created a panel, called AutoScrollPane, that extends JScollPane. Even though it is a JScrollPane, you can hide the scrollbars. This allows me to leverage functionality for moving a viewport around on a map or similar like in an RTS game as you say.
Second, for the scroll regions, I actually have 8: n, ne, e, se, s, sw, w and nw (i.e. "north", north-east", etc.), with the diagonals allowing diagonal scrolling. I implement them simply as Rectangles. They aren't drawn on the screen or anything - I just instantiate 8 rectangles in my class sized appropriately and with coordinates that match the regions of the window. I actually allow my window to be resized, so I resize the rectangles if necessary.
Third, I have a timer that can be turned on and off. When it is turned on, every "tick" generates a Runnable. This Runnable's job is to scroll the panel's viewport in the appropriate direction a certain distance. Each Runnable is handed to the Swing event queue.
Finally, I have a MouseMotionListener. It's job is to intercept mouse enter, exit, and move events. Each time it receives an event, it checks the current mouse location and whether it intersects one of the rectangles. Based on which rectangle, it chooses a direction to scroll. It keeps track of whether the mouse was in a scroll region on the previous event or not. Based on this information it knows whether it should start scrolling, stop scrolling, or just let whatever is happening continue. I wanted my scrolling to stop if the mouse goes outside the pane, thus the use of the exit/enter events. Anyway, to start scrolling, the listener saves off the direction and distance to scroll and tells the timer to start. When it is time to stop scrolling (such as when the mouse exits a scroll region), it tells the timer to stop.
It took a while to pick the right timer granularity and scroll distance for smooth scrolling, but it does work pretty well. I hope this outline provides some help.
I think it was Martin Fowler that espoused that premature optimisation is the root of all evil in software development. Why not try the MouseMotionListener and only think about optimising if you find that it affects the performance of the game.
There is nothing wrong with a MouseMotionListener When you read about the overhead it was probably one specific exmaple
Anything you can do in any programming language can be done bad or wrong.
If you pay attention to what you do in your listener all should be fine
Related
I'm looking for a way in Java to read in raw mouse input and not just the event listeners. I'll tell you what I'm doing. Essentially, there is a game where if you move the mouse to the right, I want Java to send key signals of the 'd' key being pushed down, and when the mouse is moved to the left, the 'a' key being held down. But, when you move the mouse, the game quickly corrects the mouse position and sends it right back into the middle of the screen. So, if you use mouse listener, you get one event of the mouse being moved to the right, then another quickly following of the mouse being move back to the left. I want to know if I can get data from the mouse without those corrections in the position. Thanks!
A little code might make the situation clearer, but here's what you can do.
While there are a number of APIs (LWJGL's Mouse interface, as one example) that allow you to directly poll the mouse position, it sounds like overprogramming in your case. First, keep a private field with a reference to that last mouse position. We'll call it x here.
Use a MouseMotionListener, and have it call the same method from mouseMoved and mouseDragged. That method should look something like this.
void controlMethod(MouseEvent event) {
int newX = event.getXOnScreen();
int dx = this.x - newX;
if(dx > 0) **D Key Event**
else if(dx < 0) ***A Key Event**
x = newX;
}
That should do the job for you. The only thing you might want to look out for is the mouse straying off of the MouseMotionListener's area; but there are always ways around things like that, and it seems a bit tangential.
As a final note, if you end up with wild swings in mouse control at the beginning of your game loop, consider setting the class field x to an Optional, and use Optional.ifPresent(...) for the dx logic. This will also protect you from data nullification, such as from a focus loss, and I recommend making it a practice.
Best of luck!
How do I get the mouse position. I have tried:
int mouseX = MouseInfo.getPointerInfo().getLocation().x;
int mouseY = MouseInfo.getPointerInfo().getLocation().y;
But that does it for the whole screen. Anyway to do it relative to the JPanel/JFrame
If I'm only using Graphics JFrame and JPanel that is being repainted every millisecond, should I have buffers? Or will it be fine?
How do I add a mouseAcionEvent only to the frame so it gets X() and Y() of mouse but only in frame?
Use a MouseListener instead of MouseInfo. MouseListener will trigger events which are contextual to the component which raised them, which means you won't need to translate the events into the component space as the event will already be converted to within the component context.
See How to write a mouse listener for more details
How should I update my game rePaint() every millisecond or another way?
Use a javax.swing.Timer...
See How to use Swing Timers for more details...
Should I use buffers?
That will depend. Swing components are already double buffered, but if you use a more complex timing mechanism (AKA game loop), you might find it useful, even to roll your own.
I, personally, would start simple
How can I improve the way I thought out my code in the first place? Is it right having 10 loops or only all in 1 to reduce lag ect.
There are probably lots of things, but start with broader idea...
Breakdown entities to their own responsibilities, for example, the player should know where it is and how it should be painted. It could even know how it's suppose to move based on the current state of the game. This way you could create any number of entities, all with there own set of rules which are isolated and easily updated.
Devise a controller mechanism which is responsible for taking in keyboard and mouse events and simply updating the current state of the game model. That is, rather than going "the user pressed the 'left' key, move player to the left", it would simply raise a flag in the game model that the "left" state has been triggered (or untriggered) and the engine would, on the next update loop, ensure that each entity knew about the change
Don't use magic or hard coded numbers, instead provide some kind of ability to scale the scene. For example, you could decide what is shown on the screen based on the size of the window...
I have a mouse event handler and under mouseMoved I have this:
public void mouseMoved(MouseEvent e)
{
if((e.getX()>=0 && e.getX()<=100) && (e.getY()>=0 && e.getY()<=100))
{
robot.mouseMove(mainOutput.getX()+200,mainOutput.getY()+200);
}
}
What this does is that if the user tries to move towards the first 100x100 pixels of the frame, the pointer will translated. What I want to do however is recreate an "impassable wall".
Basically when the user attempts to go in the region it cannot pass the end points of the region. What I want to know is how would I go about doing this?
Unfortunately, this is a bit more difficult than it seems. Let me first illustrate the problems with a simple move-to-outside-of-boundary approach.
As you can see, in this case the boundary approach will detect the mouse inside the boundary, and move it to the blue point in the corner. Let me emphasize this, it detects the location of the mouse. What we want is to capture the movement of the cursor, and have it end at the red point. There are also other problems with this method that may not be immediately apparent.
So how do we capture the movement of the mouse? We need to capture the mouse displacement (black arrow) as a vector by keeping track of the previous location as well. I assume you can do this. So how do we calculate the new location? Well, we can perform line intersection of the displacement vector with the lines that make up the edges of the box. As you're only dealing with horizontal and vertical lines, it is greatly simplified and can be done with just a bit of thinking. If you're lazy, copy a generalized line intersection algorithm.
You may think that this approach is too rigorous, but it is the most robust way. I can already think of two additional issues with the simpler approach. Also, you're actually doing 2D hitbox detection. This is quite a useful thing to know.
Assuming the impassable area is a JPanel, you can add a mouse listener to it that will respond whenever the mouse enters the area, and then do the relocation stuff that you already have.
JPanel pnlArea = new JPanel();
pnlArea .addMouseListener(new MouseAdapter() {
#Override public void mouseEntered(MouseEvent arg0) {
//execute some codes if the mouse pointer has enterd the area.
}
});
I think you just need to clarify to yourself what the behaviour of a mouse hitting a wall would be, in terms of coordinates.
Hopefully this terrible image helps;
Whenever the mouse moves, you want to check if it's in the forbidden region (the region beyond the wall; the no man's land). To do this, just check if the x coordinate (assuming a horizontal wall in this example) of the mouse is beyond its limit (the x coordinate of the wall).
If it is, move the mouse back to the wall, preserving its y value (set its x coordinate to that of the wall)
FOR A CAGE:
The case of having a surrounding, square wall is a little bit more complicated, in terms of where to place the mouse when breaching the wall.
Observe that there are 8 different regions to consider where the mouse could enter the 'forbidden zone', and each boundary should translate the mouse differently.
The coordinate of the boundary corners are in red (and consist of 4 values; xL, xR, yT, yB for left, right, top, bottom respectively)).
The green text in each region describes the conditions that must all be true for the mouse to lie in that region (they're how you detect the mouse must be in that region) where x and y are the mouse coordinates.
You can see that the four 'side' regions involve a simple translation; just altering one of the coordinates of the mouse (the x coordinate for a vertical wall, y for a horizontal wall) to match that of the wall.
The four 'corner' regions can entirely change the mouse coordinate (to their corner coordinate!)
I am developing a small Game in Java, and I'm rewriting the Player Movement system to not be Grid-Based. It is a 2D side-scroller, and what I'm trying to achieve is basic Player Movement, so that when the user presses, and holds, the right Key the Player moves right, and the same for the Left Key. The problem I am having is that the Paint Component in another Class draws the image of the Player on the screen, with positions X and Y stored in a Settings Class. Then a KeyListener Class gets when the user is pressing Right, and adds to the X value (And the same for Left, but minuses 1 every time). This creates a slowly moving Character on the screen, and what I want to do is have him move faster without adding more than 1px each time as it would seem like he was skipping pixels (I've already tried).
I was wondering if there was a better way to go about this, as the code I'm using is bare-minimum, and my outcome would be a smoothly moving Player.
KeyListener Snippet:
public void keyPressed(KeyEvent arg0) {
int key = arg0.getKeyCode();
if(key == 39) { // Right Key
Settings.player_pos_x++;
}else if(key == 37) { // Left Key
Settings.player_pos_x--;
}
main.Game.redo();
}
Drawing User on-screen:
g.drawImage(player_image, Settings.player_pos_x, Settings.player_pos_y, this);
Any help is appreciated, if you need any more information or code please feel free to ask.
Let's try again :)
Double buffering
Quote: http://msdn.microsoft.com/en-us/library/b367a457.aspx
Flicker is a common problem when programming graphics. Graphics operations that require
multiple complex painting operations can cause the rendered images to appear to flicker
or have an otherwise unacceptable appearance.
When double buffering is enabled, all paint operations are first rendered to a memory
buffer instead of the drawing surface on the screen. After all paint operations are
completed, the memory buffer is copied directly to the drawing surface associated with
it. Because only one graphics operation is performed on the screen, the image
flickering associated with complex painting operations is eliminated.
Quote: http://docs.oracle.com/javase/tutorial/extra/fullscreen/doublebuf.html
Suppose you had to draw an entire picture on the screen, pixel by pixel or line by
line. If you were to draw such a thing directly to the screen (using, say,
Graphics.drawLine), you would probably notice with much disappointment that it takes
a bit of time. You will probably even notice visible artifacts of how your picture is
drawn. Rather than watching things being drawn in this fashion and at this pace, most
programmers use a technique called double-buffering.
Perhaps you could write the app to iterate multiple redraws to the right, each only 1 or 2 pixels per keyboard input received. This way, you're kind of artificially setting how fast you want it to move. Otherwise, you'd be limited to how the user has their keyboard iteration speed set up.
Also, java's jPanel is not exactly the place to look for video game efficiency. Just saying; you might want to look to something like openGL for that.
At best, you could optimize it to have a transition buffer outside of the jpanel drawing logic, with the same draw functionality as a jPanel, this way you write to the buffer, then copy the whole buffer during a writeImage call... I don't know if you're already doing that, but I think it avoids flicker or something... read about it a long time ago, it's called double buffering.
While double buffering will probably help you should first address how you are calculating the distance you move each time.
Change in distance (dx) can be defined as the velocity(v) times the change in time(dt), dx = v * dt. From what I can see you are omitting dt (change in time). You should calculate the time difference from the last time the character was moved and multiply that by your velocity. This way if your distance processing code is executed 10 time or 100 times in 10 seconds the character will always move the same distance.
The code will look something like this:
int currentTime = System.nanoTime();
int deltaTime = currentTime - previousTime;
Settings.player_pos_x += velX * deltaTime;
previousTime = currentTime;
With this code you will probably need to significantly increase velX. Typically I would have a constant that I would multiply by the velocity so they are smaller numbers.
Additionally if you want better movement and physics look into JBox2D although it's probably over kill for you right now. Their documentation might also help to understand basic physics.
In order to answer your question about the speed, most games i've seen store a global int for the velocity in each plane (x and y planes), then apply the velocity to the current position rather than simply incrementing it as you have done above.
public int velX = 2;
public int velY = 2;
public void keyPressed(KeyEvent arg0) {
int key = arg0.getKeyCode();
if(key == 39) { // Right Key
Settings.player_pos_x += velX;
}else if(key == 37) { // Left Key
Settings.player_pos_x -= velX;
}
main.Game.redo();
}
Once you have that set up, try adding operations which may increase the velocity (i.e. holding the key down for a long time, or something else);
Also, try to implement a jump operation, where the players Y position goes up, and try to add a gravity field to make it come back down.
I'm working on a Java Bomberman clone, using a grid system, and I'm not really satisfied with the movement right now. When the player presses a movement key, the character starts to move (with 0.25 speed). The player loses the control, and the character keeps moving until it has moved a full tile. The player only regains control when the character gets to the center of the next tile.
This makes it's too "laggy". If I want to change direction now, I can't.
Maybe I could make the base of the character smaller than the size of the sprite, meaning I would have to check ahead of the corners to check if it was a valid move. Any thoughts?
Thanks!
I just played Bomberman a few minutes ago :)
You can move pretty smooth, there no grid calculations.
I haven't used Java that much. I use Flash a lot. Is the Grid for checking collisions ?
What is it for exactly ?
Would it make sense to have something like(I'll try to sketch it):
float vx,vy = 0;//velocity on x and y
Character bomberman
void keyDownHandler(KeyboardEventSomething event){
if(key is Left && ! Right ) vx -= .5;
else if(key is Right && !Left ) vx += .5;
//idem for Y axis
}
void keyUpHandler(KeyboardEventSomething event){
vx = vy = 0;
}
void updateLoop(){
bomberman.x += vx;
bomberman.y += vy;
}
I might be a bit off, because I'm not sure how much you want to clone Bomberman or not. What you're describing with the grid movement seems closer to hopmon
Don't disable user input. Ever. Constantly poll for it.
Have a Tick() or OnFrameEnter() function that does the following:
Poll the keyboard/joystick/whatever, and cache the last direction entered by the user.
Check the position of the character.
If the character is idle in the center of a grid square, set it's velocity in the proper direction and upate the movement one step, unless there's a wall/collision object in the way. Also clear out the last key press to some invalid value.
Else if the character is already moving, update the position of the character, checking for collision with a wall, or proximity to a direction changing node (intersection). If it collides with a wall, stop the character. If it's close to a direction changing node and can travel in the cached direction, change the direction and clear the cached direction. Else if the input direction is opposite the current direction (it's in a hallway and the player wants to reverse), reverse the direction and then clear the key press. This all assumes that the character should continue moving, always, until it hits a wall and stops. If you want the character to stop anywhere, a reverse direction key should simply stop it in place.
Caching the key press allows the player to preemptively change directions as the character approaches an intersection. This way the character doesn't have to wait to stop before moving on, and the player doesn't feel the game is unresponsive. Polling constantly lets the player reverse directions at any time, and makes direction changes at intersections seem faster.
You should be able to get rid of current sprite and load the new one exactly when you need it - i.e. if the character moved half way to the next tile and the player presses a key to return, cancel the sprite and load the new one starting at 0.5.
I guess it depends on how you want it to behave.
Say you are moving up, and you click right.. do you want it to stop moving up and go right?
I would store the previous grid location when you make a movement. that way if you click to move elsewhere you can hop backwards and change the movement direction..
Or if you are moving up and you click down... you could set the movement speed to be the opposite of what it is...
It is hard to answer with out knowing what you want it to behave like.
It sounds like you update the players position (start of the animation) by changing the grid cell in which its contained.
To be able to achieve the desired effect, the sprites render position should be independent of where the entity represented by that sprite is located on the grid. And allow the user to move inside that grid within a range of the cell's center without changing cells. If this range is not chosen correctly you may en up with the "doh! im in this (visually) cell but it says im in the future cell" problem, or the "my avatar is overlapping a wall".
Dont change the players cell until he is within a range (lets say 1/3 of the bounding sphere of the sprite) from the limits/borders of the cell that contains him. You would also have to modify the animation process for it to be interruptible.
This is independent of the degrees of liberty you give the player (like, it could only move up, down, left, right, and diagonals vs 360 degrees)
Another thing you should watch out for is, if the sprite is larger than the cell size. For instance, a Fat monster, or a Snake boss which spans across X cells but not necessarily in rectangular form.