2

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.

Ozzy
  • 8,244
  • 7
  • 55
  • 95
oyed
  • 572
  • 2
  • 14
  • 34
  • Double-buffering? http://www.realapplets.com/tutorial/DoubleBuffering.html – Ozzy Apr 20 '12 at 20:11
  • @Ozzy I'm not developing an Applet, would this still work? – oyed Apr 20 '12 at 20:13
  • 2
    Principle is the same - double buffer! Look at this related question: http://stackoverflow.com/questions/4430356/java-how-to-do-double-buffering-in-swing – Ozzy Apr 20 '12 at 20:13
  • @Ozzy I will give it a try. So the KeyListener is fine, and I should continue using it? – oyed Apr 20 '12 at 20:15
  • If you read the applet tutorial link I posted (above), you'll understand why you need double buffering in animation, and the Listeners have nothing to do with it. A Listener just listens to some user input. – Ozzy Apr 20 '12 at 20:17
  • @Ozzy I'm reading it now, but I still don't understand why the KeyListener isn't the issue, as it is increasing the value of the X that is drawn, not directly affecting the image position in the paintComponent, the paintComponent just takes the value, so surely the slow movement is the KeyListener not incrementing and calling the repaint fast enough? – oyed Apr 20 '12 at 20:19

4 Answers4

3

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.

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
Ozzy
  • 8,244
  • 7
  • 55
  • 95
  • The problem is not flickering, I don't have that at all. – oyed Apr 20 '12 at 20:24
  • `and my outcome would be a smoothly moving Player.` If you want smoothly moving sprites, you need double-buffering. Didn't you notice the difference between the 3 applets in the double buffering tutorial link I posted for you? – Ozzy Apr 20 '12 at 20:27
  • `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` You can't move him 1px at a time and expect him to move at a given velocity, you have to move him at that velocity and use double-buffering! The `skipping-pixels` you mentioned is the flickering we're talking about. – Ozzy Apr 20 '12 at 20:29
  • I noticed flickering in the demo's, yes, and I'll look in to double buffering, but the larger issue I'm facing is the lack of speed in the Player, which is most likely the KeyListener. – oyed Apr 20 '12 at 20:29
  • At the normal rate of 29 FPS (like your TV set), the fastest anything in your current program can move is 29 pixels per second, because you only want to animate 1px at a time. In most games, a velocity is calculated from the user input (whether it be a mouse listener or a key listener or whatever) and the sprite is moved at that velocity (more than 1px per second, for sure). – Ozzy Apr 20 '12 at 20:31
  • Again, I'll look in to double-buffering but please explain why this isn't an issue with the KeyListener, because even if I use double-buffering wouldn't the KeyListener still be increasing the position at the same speed? And the "skipping pixels" is when I increase the position by more than 1 each time the player moves. Sorry for the confusion. – oyed Apr 20 '12 at 20:33
  • The speed is another issue you are having, because you need to allow your program to calculate a speed. Change the +1 px to +2 px and see what happens - it moves twice as fast right? – Ozzy Apr 20 '12 at 20:35
  • Yes, but it doesn't look as smooth. Let me put this conversation on hold and I'll try putting some double-buffering in, and see the difference. – oyed Apr 20 '12 at 20:36
  • @ThomasMosey It will be smooth with double-buffering - thats the whole point of double buffering. Btw i've added another answer regarding the speed issue you have. – Ozzy Apr 20 '12 at 20:39
  • 1
    Double Buffering has no effect on what Tom is trying to accomplish. He's trying to move 1 pixel at a time faster without skipping pixels by changing the increment to anything greater than 1 pixel. I'm having a similar problem, let me know if you come up with a solution. – Rice_Crisp Apr 20 '12 at 20:40
  • @Rice_Crisp not another one, lol. You have to use double buffering and move more than 1px at a time - that is the answer. There is a limit to how many repaints your JVM can make in one second. Do you seriously want to limit the speed of light in your program to 30-60 pixels a second? – Ozzy Apr 20 '12 at 20:44
  • I suppose Double Buffering sort of relates to the problem. A better way to put it would be refresh rate, as Double Buffering itself doesn't mean this. But I'm assuming you're aware of this and are trying to move the image faster without redrawing the entire screen a lot. – Rice_Crisp Apr 20 '12 at 20:58
  • If I move an image 2 pixels to the right, it is skipping the first pixel to the right. I think this is what Tom is complaining about. Double Buffering is just drawing an off-screen image. It's still skipping that first pixel. – Rice_Crisp Apr 20 '12 at 21:02
  • The artifacts which occur in the picture without double-buffering are what causes the animation to not appear "smooth" or to appear as if it has "skipped" frames. With double-buffering, a sprite should look smooth even flying across your screen at 100px/second or whatever. – Ozzy Apr 20 '12 at 21:07
  • Please don't use code formatting for quotes that should simply start with `>`. :) – Andrew Thompson Apr 21 '12 at 07:21
1

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.

Adam Miller
  • 1,756
  • 1
  • 25
  • 44
  • I'll look at the looping, thanks. Also, I realize OpenGL is a better option, but this is for me to learn more about the language on a wider range. I'll be starting OpenGL soon, thanks. – oyed Apr 20 '12 at 20:25
1

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.

mholzmann
  • 1,289
  • 1
  • 8
  • 4
0

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.

Ozzy
  • 8,244
  • 7
  • 55
  • 95
  • Thanks for this, I have been looking for a way to make the Movement more realistic when I rewrite it, and I'll implement this. I haven't worked with Timers before, have you got any examples of how to increase the Velocity over time, like you said? Thanks. – oyed Apr 20 '12 at 20:41
  • It totally depends on the game, since you are the designer, you know when the best time to increase the speed would be. – Ozzy Apr 20 '12 at 20:42
  • Oh yes, completely, I'm just asking if you have an example which would be good to look at in my scenario? – oyed Apr 20 '12 at 20:44
  • @ThomasMosey 2D side-scroller? Maybe you pick up a power up (Collision-detection) and then your velocity increases for 5 seconds, then it goes back to the default? Use your imagination :P If you need inspiration - play a side-scroller and see what they do! – Ozzy Apr 20 '12 at 20:46
  • I understand what you're getting at, I'm referring to your post, "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);", the "holding the key down" part. Thanks. – oyed Apr 20 '12 at 20:51
  • I don't have any example code for that, if thats what you're asking for :P. I was suggesting you try and develop those features yourself, as a challenge. Of course, if you get stuck at any part you could just make a new post on SO for help. – Ozzy Apr 20 '12 at 21:02