2

I used my recent days to program a little 2D game in Java. I wanted to use nothing but the normal Java SDK. So far so good. I now finshed it, there's just one little problem: I used the Thread.sleep method to create something like a gameloop/tick. Since I'm not too familiar with object oriented programming, I'm not quite sure whether I use the right expressions, so please ignore wrong ones.

The strange thing is, that if I switch my waiting-time (I called it "Takt") to 100 or above, everything works perfectly fine. I want to change it to 50, but as soon as I get it lower than 100, the game starts lagging extremly (the bouncing red ball which this easy ping-pong-game is about starts flickering heavily.

Here's a piece of code of my Display class, it includes the paint(); method. If you need other code parts, just let me know! And please pardon my variable names, I'm from germany. So here's the code:

import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

@SuppressWarnings("serial")

public class Output extends JFrame implements Runnable{

public static int W = 400;
public static int H = 700;

public static int Playerx;
public static int Playery = H-30;
public static int Punkte;

public static int Trash;

public static long Takt = 100;

public static boolean DrawGameOver;
public static boolean finished;

public static void main(String[] args) throws InterruptedException {
    JOptionPane.showMessageDialog(null, "Das Spiel startet, sobald Sie auf OK drücken. Das Ziel ist es, möglichst viele Punkte zu erzielen.", "Ping-Pong-Solo", JOptionPane.NO_OPTION);
    JOptionPane.showMessageDialog(null, "Mindstanforderungen: 1. Dual-Core Prozessor 2. Bildschirmauflösung mit mindestens 400*700p", "Systemanforderungen", JOptionPane.NO_OPTION);

    Output Fenster = new Output();
    Fenster.setSize(W, H);
    Fenster.setResizable(false);
    Fenster.setLocationRelativeTo(null);
    Fenster.setVisible(true);
    Fenster.setTitle("Ping Pong Release 1.0");
    Fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    Fenster.addKeyListener(new MyKeyListener());
    new Physics();
    new Thread(new Output()).start();
    while(true){
        if(DrawGameOver == false && finished){
            Fenster.repaint();
        }else{
            GameOver();
        }
        try {
            Thread.sleep(Takt);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if(!DrawGameOver){
            Physics.Bewegung();
        }
    }
}       

public static void GameOver(){
    JOptionPane.showMessageDialog(null, "Sie erreichten "+Punkte+" Punkte. ", "Spiel beendet.", JOptionPane.NO_OPTION);
    JOptionPane.showMessageDialog(null, "(c) Joel Krimmel", "Der Boss", JOptionPane.NO_OPTION);
    System.exit(0);
}
    public void paint(Graphics g){
        finished = false;

        g.setColor(Color.BLACK);
        g.fillRect(0, 0, W, H);
        g.setColor(Color.BLUE);
        g.fillRect(Playerx, Playery, 100, 20);
        g.setColor(Color.RED);
        g.fillRoundRect(Physics.x, Physics.y, Physics.W, Physics.H, 500, 500);
        g.setColor(Color.YELLOW);
        g.drawString("Punkte: "+Punkte, W-100, 50);

        finished = true;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        while(true){
            MyKeyListener.Verarbeitung();
        }
    }
} 
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
Joey
  • 207
  • 4
  • 11
  • 2
    It's difficult to answer a question like this. The Stack overflow rules state that you have to have at least a minimal understanding of the problem. If you can find why it lags, we can answer this. – CaffeineToCode Feb 27 '15 at 19:16
  • The code is disjointed and out of context, your paint method could be doing something horrible wrong or not, it's difficult to know. Consider providing runnable example which demonstrates your problem. You could also consider using a Swing Timer instead of your "while-lop", but until we see more, that's just a guess – MadProgrammer Feb 27 '15 at 21:03
  • Just to prove the point, you can check out [this example](http://stackoverflow.com/questions/13022754/java-bouncing-ball/13022788#13022788), which is a simple bouncy ball example, [this example](http://stackoverflow.com/questions/14886232/swing-animation-running-extremely-slow/14902184#14902184) and [this example](http://stackoverflow.com/questions/23417786/rotating-multiple-images-causing-flickering-java-graphics2d/23419824#23419824) which are about rendering copious amount of objects (100's if not 1000's)... – MadProgrammer Feb 27 '15 at 21:17
  • As a side not, you will want to avoid `KeyListener` in favour for the the key bindings API, which will fix the focus related issues with `KeyListener`. See [How to Use Key Bindings](http://docs.oracle.com/javase/tutorial/uiswing/misc/keybinding.html) for more details – MadProgrammer Feb 27 '15 at 21:19

2 Answers2

2

So there could be multiple reasons why your game is flickering. I think the most likely one is that you are using no buffering. That means that you are drawing the screen while it's beeing displayed. So stange things like half drawn pictures will be displayed sometimes. Instead you can write the content to an image first and then draw the whole image at once.
In the code it would look something like this:

public void paint(Graphics g){
    finished = false;
    // the buffer image
    BufferedImage bufferImage = new BufferedImage(getWidth(),getHeight(),BufferedImage.TYPE_INT_RGB);
    // I used Graphics2D instead of Graphics here, because its more flexible and lets you do more things.
    Graphics2D g2 = (Graphics2D)bufferImage.getGraphics();
    g2.setColor(Color.BLACK);
    g2.fillRect(0, 0, W, H);
    // ...
    g2.drawString("Punkte: "+Punkte, W-100, 50);
    // now draw the buffer image on the screen.
    g.drawImage(bufferImage, 0, 0, null);
    finished = true;
}

(That code is not ideal, but it's a quick way to do it. If you want to know how to do it better look up "double buffering".)

Another (unlikely) reason could be that the rendering takes different amounts of time each rendering process. You could fix that by measuring the time the rendering takes with System.currentTimeMillis() and adjust the sleeping time.

And also: Don't name variables in german. I'm german too (Hallo aus Bremen) and I don't do it either. I's bad. :-)
But more importantly don't start variable name with an upper-case letter, they can easily be confused with clases otherwise.
You should take a look at the java naming conventions.

Josh Crozier
  • 233,099
  • 56
  • 391
  • 304
Quijx
  • 366
  • 1
  • 10
  • Or, if the OP is using a `JComponent` based class, simply take advantage of the double buffering support already provided...but there is no context to make such determinations... – MadProgrammer Feb 27 '15 at 21:18
  • Hmmm this is quite hard to understand for me... Why exactly is this drawing a black rectangle from 0,0 to the last point on screen? Using this just gives me a blackscreen O.o – Joey Feb 28 '15 at 10:49
2

I've noticed that you're using JFrame, which means that each time you repaint, you're repainting the entire window. The solution is to use a JPanel, which is much faster, and to put that into your JFrame as to not have any more flickering. Also, thread.sleep essentially freezes your entire program, so please use the swing timer.

Eddy
  • 21
  • 1