# 2D-Spiele ruckeln auf JPanel



## Leifa (26. Okt 2010)

Moin!

Ich möchte anfangen, 2D-Spiele zu programmieren. Dafür habe ich mich ein bisschen mit dem Tutorial von Quaxli beschäftigt. Das Tutorial gefällt mir sehr gut, die Gedankengänge sind sehr gut nachvollziehbar. Was mich allerdings stört, ist das Ergebnis: Die Sprites ruckeln (ein bisschen).

Ich habe daraufhin die Klasse GamePanel mit Minimalfunktionen nachprogrammiert und wollte ein Rechteck über den Bildschirm gleiten lassen, bei jedem repaint einen Pixel weiter. Aber es ruckelt, egal ob 10 FPS oder 140 oder irgendwas dazwischen. Ich frage mich jetzt, ob das Ruckeln nur auf meine Rechnerleistung zurückzuführen ist, oder ob sich bewegende Objekte auf einem JPanel der Grund sind. Bei ApoMario habe ich z.B. 140 FPS und so gut wie kein Ruckeln.

Meine Vermutung ist daher, dass ein JPanel gar nicht so gut geeignet ist, um Spiele mit Sprites zu programmieren. Was kann man stattdessen benutzen? Ich möchte auch keine fertige Engine nutzen, denn es geht mir ja gerade darum zu lernen und mir selbst die Klassen zu schreiben, die ich für ein 2D-Spiel brauche.

Hier noch die relevanten Methoden meines Programms, vielleicht mache ich ja irgendwas furchtbar falsch:


```
@Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        
        
        g.setColor(Color.RED);
        g.fillRect(x, x, 120, 40);
        
        if (showFps) {
            g.setColor(Color.GREEN);
            g.fillRect(5, 5, 120, 40);
            g.setColor(Color.RED);
            g.drawString("FPS: " + df.format(fps), 10, 20);
            g.drawString("DELTA: " + df.format((double) delta / 1000000) + " ms", 10, 35);
        }        
    }
    
    
    private void computeDelta() {
        delta = System.nanoTime() - last;
        last = System.nanoTime();
        fps = (1000000000.0) / delta;
    }
    
    
    @Override
    public void run() {
        while (running) {
            computeDelta();
            x++;
            repaint();            
            try {
                Thread.sleep(sleepTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }        
    }
```


----------



## Leifa (26. Okt 2010)

Ich bin gerade zu der Erkenntnis gekommen, dass das Nutzen eines JPanels nicht der Grund sein kann.

Denn dieses umfangreiche Beispiel hier verwendet ebenfalls JPanel und bekommt dabei mal eben 1000-mal aufwändigere Animationen angezeigt. Und es ruckelt bei mir kein bisschen.

Ein JPanel sollte daher für meinen Bedarf vollkommen ausreichen. Doch worin liegt der Grund für das Ruckeln?


----------



## Volvagia (27. Okt 2010)

Ich glaube, dass ein Pixel einfach zu wenig Veränderung ist. Wenn du die Distanz veränderst, wird die Bewegung eventuell flüssiger wirken.


----------



## Leifa (27. Okt 2010)

Volvagia hat gesagt.:


> Ich glaube, dass ein Pixel einfach zu wenig Veränderung ist. Wenn du die Distanz veränderst, wird die Bewegung eventuell flüssiger wirken.


Das bringt leider nichts. Das Bild ruckelt, egal mit welcher Veränderung, als würde das Bild nicht schnell genug gezeichnet werden.

Zu dem Beispiel von Java 2D gibt es übrigens hier den source code. Ich konnte ihn ohne größere Probleme kompilieren und sämtliche Effekte laufen flüssig ab. Allerdings möchte ich keine 15000 Zeilen durchsuchen, um den elementaren Unterschied in der Implementierung herauszufinden.


----------



## Volvagia (27. Okt 2010)

Also das läuft bei mir schon mit ohne, dass es ruckelig wirkt:


```
import java.awt.Color;
import java.awt.Graphics;
import java.util.concurrent.TimeUnit;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class TestPanel extends JPanel implements Runnable
{
	private static final long serialVersionUID = 1L;

	private int x = 0;
	private int y = 0;
	
	private boolean showFps = true;
	private boolean running = true;
	
	private long delta;
	private long last;
	private double fps;
	
	private long sleepTime = 10L;
	
	public void paintComponent(Graphics g) {
		super.paintComponent(g);

		g.setColor(Color.RED);
		g.fillRect(x, y, 120, 40);
		
		if (showFps) {
			g.setColor(Color.GREEN);
			g.fillRect(5, 5, 120, 40);
			g.setColor(Color.RED);
			g.drawString("FPS: " + fps, 10, 20);
			g.drawString("DELTA: " + (double) delta / 1000000 + " ms", 10, 35);
		}        
	}
	private void computeDelta() {
		delta = System.nanoTime() - last;
		last = System.nanoTime();
		fps = (1000000000.0) / (double)delta;
	}
	public void run() {
		while (running) {
			computeDelta();
			x+= 10;
			repaint();            

			try
			{
				TimeUnit.MILLISECONDS.sleep(sleepTime);
			}
			catch (InterruptedException e)
			{
				e.printStackTrace();
			}
		}
	}
	public static void main(String[] args) {
		JFrame frame = new JFrame();
		frame.setSize(600, 600);
		frame.setVisible(true);

		TestPanel panel = new TestPanel();
		frame.add(panel);

		new Thread(panel).start();
	}
}
```


----------



## Leifa (27. Okt 2010)

Seltsam, auch das ruckelt bei mir (sehr stark sogar).


----------



## krazun (27. Okt 2010)

Also das Beispiel von Volvagia läuft bei mir auch ohne Ruckeln. 



> Was kann man stattdessen benutzen?



Eine andere Möglichkeit währe z.B. Active Rendering:  GameDev.net -- Java Games: Active Rendering


```
import java.awt.*;
import java.util.Random;
import javax.swing.JFrame;

/*
 * This is an example of a simple windowed render loop
 */
public class SimpleWindowedGame {

  public static void main( String[] args ) {
		
    // Create game window...
    JFrame app = new JFrame();
    app.setIgnoreRepaint( true );
    app.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		
    // Create canvas for painting...
    Canvas canvas = new Canvas();
    canvas.setIgnoreRepaint( true );
    canvas.setSize( 640, 480 );
		
    // Add canvas to game window...
    app.add( canvas );
    app.pack();
    app.setVisible( true );
		
    // Create BackBuffer...
    canvas.createBufferStrategy( 2 );
    BufferStrategy buffer = canvas.getBufferStrategy();

    // Get graphics configuration...
    GraphicsEnvironment ge = 
        GraphicsEnvironment.getLocalGraphicsEnvironment();
    GraphicsDevice gd = ge.getDefaultScreenDevice();
    GraphicsConfiguration gc = gd.getDefaultConfiguration();

    // Create off-screen drawing surface
    BufferedImage bi = gc.createCompatibleImage( 640, 480 );

    // Objects needed for rendering...
    Graphics graphics = null;
    Graphics2D g2d = null;
    Color background = Color.BLACK;
    Random rand = new Random();
		
    // Variables for counting frames per seconds
    int fps = 0;
    int frames = 0;
    long totalTime = 0;
    long curTime = System.currentTimeMillis();
    long lastTime = curTime;
		
    while( true ) {
      try {
        // count Frames per second...
        lastTime = curTime;
        curTime = System.currentTimeMillis();
        totalTime += curTime - lastTime;
        if( totalTime > 1000 ) {
          totalTime -= 1000;
          fps = frames;
          frames = 0;
        } 
        ++frames;

        // clear back buffer...
        g2d = bi.createGraphics();
        g2d.setColor( background );
        g2d.fillRect( 0, 0, 639, 479 );
				
        // draw some rectangles...
        for( int i = 0; i < 20; ++i ) {
          int r = rand.nextInt(256);
          int g = rand.nextInt(256);
          int b = rand.nextInt(256);
          g2d.setColor( new Color(r,g,b) );
          int x = rand.nextInt( 640/2 );
          int y = rand.nextInt( 480/2 );
          int w = rand.nextInt( 640/2 );
          int h = rand.nextInt( 480/2 );
          g2d.fillRect( x, y, w, h );
        }
				
        // display frames per second...
        g2d.setFont( new Font( "Courier New", Font.PLAIN, 12 ) );
        g2d.setColor( Color.GREEN );
        g2d.drawString( String.format( "FPS: %s", fps ), 20, 20 );
				
        // Blit image and flip...
        graphics = buffer.getDrawGraphics();
        graphics.drawImage( bi, 0, 0, null );
        if( !buffer.contentsLost() )
          buffer.show();
				
        // Let the OS have a little time...
        Thread.yield();
      } finally {
        // release resources
        if( graphics != null ) 
          graphics.dispose();
        if( g2d != null ) 
          g2d.dispose();
      }
    }
  }
}
```

mfg,
krazun


----------



## Leifa (27. Okt 2010)

Mit krazuns Beispiel läuft alles sehr flüssig, auch bewegte Objekte.
Vielen Dank!

Falls jemand noch eine Idee hat, was vorher das Ruckeln ausgelöst haben könnte: Ich bin immer noch interessiert!


----------

