# Ping Pong langsam



## 123fa241 (23. Jan 2011)

Hi!

ich will ein Ping-Pong-Spiel machen, jedoch ist es sehr langsam (AMD Phenom II 2x2.8Ghz / ATI Radeon HD 5850):
(Code soll noch keinen spielerischen Zweck erfüllen, soll einfach nur einen pendeldenen Punkt darstellen)


```
public class GamePanel extends JPanel implements Runnable {
    private ArrayList<FieldObject> objects;

    private Barrier topBarrier;
    private Barrier rightBarrier;
    private Barrier bottomBarrier;
    private Barrier leftBarrier;

    private Kugel kugel;
    private boolean gameRunning = true;

    private Thread gameThread;

    public GamePanel(Dimension dim) {
        objects = new ArrayList<FieldObject>();
        setPreferredSize(dim);

        topBarrier = new Barrier(new Dimension(500, 10), new Point(0, 0));
        rightBarrier = new Barrier(new Dimension(10, 480), new Point(490, 10));
        bottomBarrier = new Barrier(new Dimension(500, 10), new Point(0, 490));
        leftBarrier = new Barrier(new Dimension(10, 480), new Point(0, 10));

        kugel = new Kugel(new Dimension(20, 20), new Point(250, 250));

        objects.add(topBarrier);
        objects.add(rightBarrier);
        objects.add(bottomBarrier);
        objects.add(leftBarrier);


        gameThread = new Thread(this);
        gameThread.start();
    }

    public FieldObject checkCollision(FieldObject obj1) {
        for (FieldObject obj2 : objects) {
            if (obj2.isPointInside(obj1.getLocation()) && !obj2.equals(obj1)) return obj2;
        }
        return null;
    }

    public void run() {

        final int LEFT = 0;
        final int RIGHT = 1;

        int direction = LEFT;

        while (gameRunning) {

            if (direction == LEFT) {
                kugel.setLocation(new Point(--kugel.getLocation().x, kugel.getLocation().y));
                if (checkCollision(kugel) != null) direction = RIGHT;
            }
            else {
                kugel.setLocation(new Point(++kugel.getLocation().x, kugel.getLocation().y));

                if (checkCollision(kugel) != null) direction = LEFT;
            }

            try {
                gameThread.sleep(5);
            }
            catch (Exception exc) {}

            repaint();
        }
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        
        g.drawPolygon(topBarrier.getPolygon());
        g.fillPolygon(topBarrier.getPolygon());
        g.drawPolygon(rightBarrier.getPolygon());
        g.fillPolygon(rightBarrier.getPolygon());
        g.drawPolygon(bottomBarrier.getPolygon());
        g.fillPolygon(bottomBarrier.getPolygon());
        g.drawPolygon(leftBarrier.getPolygon());
        g.fillPolygon(leftBarrier.getPolygon());
        
        g.setColor(Color.BLUE);
        g.fillPolygon(kugel.getPolygon());
    }
}
```


----------



## Antoras (23. Jan 2011)

Was heißt sehr langsam? Niedrige FPS oder bewegt sich der Punkt schlicht zu langsam? Bei ersterem müsstest du mal ein KSKB bauen - in deinem geposteten Code kann ich nichts erkennen was unnötig viel Leistung verbraucht. Den Punkt kannst du schneller machen indem du seine Bewegungsschritte pro Durchlauf erhöhst (also von momentan 1 auf z.B. 10).


----------



## dhe325 (23. Jan 2011)

Er bewegt sich am Bildschirm ungleichmäßig und ruckelt. FPS habe ich aber scheinbar 194/195.

Klassen:


```
public class Main {
    public static void main(String[] args) {
        new MainFrame().setVisible(true);
    }
}
```


```
public class MainFrame extends JFrame {
    private JPanel gamePanel;
    private Dimension fieldSize;

    public MainFrame() {
        super();

        fieldSize = new Dimension(500, 500);
        gamePanel = new GamePanel(fieldSize);
        gamePanel.setBackground(Color.WHITE);
        add(gamePanel);
        pack();
    }
}
```


```
public class GamePanel extends JPanel implements Runnable {
    private ArrayList<FieldObject> objects;

    private Barrier topBarrier;
    private Barrier rightBarrier;
    private Barrier bottomBarrier;
    private Barrier leftBarrier;

    private Kugel kugel;
    private boolean gameRunning = true;

    private Thread gameThread;



        long firstFrame;
        int frames;
        long currentFrame;
        int fps;


    public GamePanel(Dimension dim) {
        objects = new ArrayList<FieldObject>();
        setPreferredSize(dim);

        topBarrier = new Barrier(new Dimension(500, 10), new Point(0, 0));
        rightBarrier = new Barrier(new Dimension(10, 480), new Point(490, 10));
        bottomBarrier = new Barrier(new Dimension(500, 10), new Point(0, 490));
        leftBarrier = new Barrier(new Dimension(10, 480), new Point(0, 10));

        kugel = new Kugel(new Dimension(20, 20), new Point(250, 250));

        objects.add(topBarrier);
        objects.add(rightBarrier);
        objects.add(bottomBarrier);
        objects.add(leftBarrier);


        gameThread = new Thread(this);
        gameThread.start();
    }

    public FieldObject checkCollision(FieldObject obj1) {
        for (FieldObject obj2 : objects) {
            if (obj2.isPointInside(obj1.getLocation()) && !obj2.equals(obj1)) return obj2;
        }
        return null;
    }

    public void run() {

        final int LEFT = 0;
        final int RIGHT = 1;

        int direction = LEFT;

        while (gameRunning) {

            if (direction == LEFT) {
                kugel.setLocation(new Point(--kugel.getLocation().x, kugel.getLocation().y));
                if (checkCollision(kugel) != null) direction = RIGHT;
            }
            else {
                kugel.setLocation(new Point(++kugel.getLocation().x, kugel.getLocation().y));

                if (checkCollision(kugel) != null) direction = LEFT;
            }

            try {
                gameThread.sleep(5);
            }
            catch (Exception exc) {}

            repaint();
        }
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        // fps
        frames++;
        currentFrame = System.currentTimeMillis();

        if (currentFrame > firstFrame + 1000) {
            firstFrame = currentFrame;
            fps = frames;
            frames = 0;
        }

        g.fillPolygon(topBarrier.getPolygon());
        g.fillPolygon(rightBarrier.getPolygon());
        g.fillPolygon(bottomBarrier.getPolygon());
        g.fillPolygon(leftBarrier.getPolygon());
        
        g.setColor(Color.BLUE);
        g.fillPolygon(kugel.getPolygon());

        g.setColor(Color.RED);
        g.drawString("FPS: " + fps, 10, 20);
    }
}
```


```
public abstract class FieldObject {
    protected Point location;
    protected Dimension size;

    public FieldObject(Dimension size, Point location) {
        this.location = location;
        this.size = size;
    }

    public boolean isPointInside(Point p) {

        if (p.x >= location.x
            && p.y >= location.y
            && p.x <= location.x + size.width
            && p.y <= location.y + size.height)
            
            return true;
        else
            return false;
    }



    /**
     * @return the location
     */
    public Point getLocation() {
        return location;
    }

    /**
     * @param location the location to set
     */
    public void setLocation(Point location) {
        this.location = location;
    }

    /**
     * @return the size
     */
    public Dimension getSize() {
        return size;
    }

    /**
     * @param size the size to set
     */
    public void setSize(Dimension size) {
        this.size = size;
    }

    public Polygon getPolygon() {
        int[] xints = {location.x, location.x + size.width, location.x + size.width, location.x};
        int[] yints = {location.y, location.y, location.y + size.height, location.y + size.height};

        Polygon poly = new Polygon(xints, yints, 4);
        return poly;
    }

}
```


```
public class Barrier extends FieldObject {

    private Point point1;
    private Point point2;
    private Point point3;
    private Point point4;


    public Barrier(Dimension size, Point location) {
        super(size, location);
        this.size = size;

        point1 = location;
        point2 = new Point((int) location.getX() + size.width, (int) location.getY());
        point3 = new Point((int) location.getX() + size.width, (int) location.getY() + size.height);
        point4 = new Point((int) location.getX(), (int) location.getY() + size.height);
        
    }

    @Override
    public Polygon getPolygon() {
        int[] xints = {point1.x, point2.x, point3.x, point4.x};
        int[] yints = {point1.y, point2.y, point3.y, point4.y};

        Polygon poly = new Polygon(xints, yints, 4);
        return poly;
    }

    /**
     * @return the point1
     */
    public Point getPoint1() {
        return point1;
    }

    /**
     * @param point1 the point1 to set
     */
    public void setPoint1(Point point1) {
        this.point1 = point1;
    }

    /**
     * @return the point2
     */
    public Point getPoint2() {
        return point2;
    }

    /**
     * @param point2 the point2 to set
     */
    public void setPoint2(Point point2) {
        this.point2 = point2;
    }

    /**
     * @return the point3
     */
    public Point getPoint3() {
        return point3;
    }

    /**
     * @param point3 the point3 to set
     */
    public void setPoint3(Point point3) {
        this.point3 = point3;
    }

    /**
     * @return the point4
     */
    public Point getPoint4() {
        return point4;
    }

    /**
     * @param point4 the point4 to set
     */
    public void setPoint4(Point point4) {
        this.point4 = point4;
    }
}
```


```
public class Kugel extends FieldObject {
    private int speed;
    private int direction;

    public Kugel(Dimension size, Point location) {
        super(size, location);
    }

    /**
     * @return the speed
     */
    public int getSpeed() {
        return speed;
    }

    /**
     * @param speed the speed to set
     */
    public void setSpeed(int speed) {
        this.speed = speed;
    }

    /**
     * @return the direction
     */
    public int getDirection() {
        return direction;
    }

    /**
     * @param direction the direction to set
     */
    public void setDirection(int direction) {
        this.direction = direction;
    }

    
}
```


----------



## Antoras (23. Jan 2011)

Ich hab den Code mal ausgeführt, bei mir läuft er aber ruckelfrei. Ich hab zwar keine Ahnung warum das bei dir ruckelt, aber poste mal noch dein OS, JDK version usw.

Vllt. kann dir mit diesen Informationen sonst noch jemand weiterhelfen.

Ändere mal noch die Stelle an der du den Thread schlafen legst in:

```
try {
	Thread.sleep(5);
} catch (final Exception exc) {
	exc.printStackTrace();
}
```
Das ändert zwar nichts an der Ausführung deines Codes, da du mit 
	
	
	
	





```
sleep
```
 aber immer nur auf den gerade aktiven Thread zugreifen kannst solltest du die Methode über den statischen Weg aufrufen. Und niemals eine Exception verschlucken.

Edit: Bringt es vllt. eine Verbesserung, wenn du dein GUI auf der EventQueue startest?

```
public static void main(final String[] args) {
	EventQueue.invokeLater(new Runnable() {
		@Override
		public void run() {
			new MainFrame().setVisible(true);
		}
	});
}
```


----------



## 2343d2 (23. Jan 2011)

Hi!

Ich verwende Ubuntu 10.10 x64 mit den aktuellsten 3D-Treibern.

VM:

```
java version "1.6.0_20"
OpenJDK Runtime Environment (IcedTea6 1.9.2) (6b20-1.9.2-0ubuntu2)
OpenJDK 64-Bit Server VM (build 19.0-b09, mixed mode)
```

Probiere es gleich mal noch in Windows.


----------



## 14asd14 (23. Jan 2011)

Unter Windows 7 x64 läufts nun auch perfekt und ruckelfrei^^ 
Dann liegts wohl an meinem Ubuntu... aber wo genau?


----------



## Antoras (23. Jan 2011)

Könnte auch am OpenJDK liegen oder verwendest du das unter Widows auch? Wenn du keinen triftigen Grund hast das zu verwenden könntest du mal schauen ob es was bringt wenn du mal das JDK von Oracle installierst.


----------



## Empire Phoenix (24. Jan 2011)

try {
                gameThread.sleep(5);
            }
            catch (Exception exc) {}

Hm also jeder tick dauert bearbeitungszeit + 5 ms
das sollteste mal ändern das die tickrate fest ist
Also threa.sleep(10-bearbeitungszeit)
bearbeitungszeit kannste mit System.currenttimeinmillis einfach berehcen aus differenz vorher nachher


----------



## axscas (30. Jan 2011)

Hi!

Danke für den Tipp mit der gleichen Tickrate, jedoch hat das keine Besserung gebracht.
Selbiges Problem besteht mit der Nutzung des OpenJDKs und des offiziellen Oracle-JDKs.


----------



## Empire Phoenix (31. Jan 2011)

Gibt es für die anderen JDK's profiler ? dann kannst du direkt nachgucken wo die zeit für verwendet wird.


----------



## Ivan Dolvich (31. Jan 2011)

Ich glaube kaum, dass es an der JDK liegt. Ich laufe auch mit OpenJDK und das ruckeln ist nur minimal und messbar:
Wenn man in der [c]GamePanel.paintComponent(Graphics g)[/c] Sich folgendes am anfang der Methode ausgeben lässt:

```
System.out.println(System.currentTimeMillis() - currentFrame);
```

Dann bekomme ich eine schwankung von 2 - 18ms. Deshalb würde ich das ganze auf Swing schieben, welches ja eher nicht für High-End-Grafik ( ;D )ausgelegt ist.

Ich würde mir überlegen, da bisschen mehr arbeit rein zu stecken und sich ins Thema Double Buffering mit Java beschäftigen. Bei Google gibt es dafür wirklich viel gutes Zeug.


----------



## Quaxli (1. Feb 2011)

Ich habe mir das Programm mal angesehen. Auf meiner 4 Jahre alten Büro-Möhre (die demnächst endlich getauscht wird) läuft es nichtsdestotrotz flüssig.
Allerdings fand ich es etwas umständlich programmiert. Mir ist z. B. in der jetzigen Version nicht klar, warum da noch Polygone erzeugt werden und auch warum die Objekte die Klassen Punkt und Dimension als Instanzvariable beinhalten statt von Rectanlge zu erben (oder von Rectangle2D falls man genauer rechnen will/muß, ist mir nicht ganz klar. Außerdem hat die Kollision mit der rechten Barriere nicht wirklich funktioniert.

Ich habe daher mal dran rum gefrickelt.  Unter anderem habe ich die Klasse FieldObject rausgeworfen, die für diese Version nicht wirklich gebraucht wird (später könnte die durchaus Sinn machen). Außerdem habe ich die (Instanz-)Variablen ziemlich ausgedünnt, sowie einige vorhanden Methoden genutzt, wie z. B. intersects(..) von Rectangle oder fillRectangle(..) aus Graphics.
Damit wird das Ganz nach meinem Dafürhalten etwas übersichtlicher.

Du kannst ja mal drüber gucken und das, was Dir gefällt, übernehmen.  Ob es Dein eigentliches Problem löst, weiß ich aber nicht, vielleicht bringt es aber ein bißchen was, da einige Rechenschritte fehlen. :bahnhof:

1. Die Hauptklasse:


```
import javax.swing.*;
import java.awt.*;

public class MainFrame extends JFrame {

	private static final long	serialVersionUID	= 1L;
	  
  //Main-Methode kann auch hier rein
	public static void main(String[] args) {
		new MainFrame();
	}

	public MainFrame() {
		super();
		GamePanel gamePanel = new GamePanel(new Dimension(500,500));
		gamePanel.setBackground(Color.WHITE);
		add(gamePanel);
		pack();
    setVisible(true); //gehört meines Erachtens hierher
	}
	
}
```

2. GamePanel


```
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.*;
import java.util.ArrayList;

import javax.swing.JPanel;

public class GamePanel extends JPanel implements Runnable {

	private static final long	serialVersionUID	= 1L;
	
	private ArrayList<Rectangle> 	objects;
	private Kugel										kugel;
	private boolean									gameRunning	= true;
	long														firstFrame;
	int															frames;
	long														currentFrame;
	int															fps;

	
	public GamePanel(Dimension dim) {
		objects = new ArrayList<Rectangle>();
		setPreferredSize(dim);

		Barrier topBarrier = new Barrier(0,0,500,10);
		Barrier rightBarrier = new Barrier(490,10,10,480);
		Barrier bottomBarrier = new Barrier(0,490,500,10);
		Barrier leftBarrier = new Barrier(0,10,10,480);
		kugel = new Kugel(250,250,20,20);

		objects.add(topBarrier);
		objects.add(rightBarrier);
		objects.add(bottomBarrier);
		objects.add(leftBarrier);

		Thread th = new Thread(this);
		th.start();
	}



	public void run() {

		while (gameRunning) {

      kugel.moveObject();
      
      //Kollisionen mal hier gehören aber in extra Methode
      //Die Logik hier paßt nicht mehr, sobald sich die Kugel auch in y-Richtung bewegt!!!
      for(Rectangle r:objects){
      	if(kugel.intersects(r)){
      		kugel.setSpeed(-kugel.getSpeed());
      	}
      }
      
      
			try {
				Thread.sleep(5);
			} catch (Exception exc) {
			}

			repaint();
		}
	}

	@Override
	public void paintComponent(Graphics g) {
		super.paintComponent(g);

		// fps
		frames++;
		currentFrame = System.currentTimeMillis();

		if (currentFrame > firstFrame + 1000) {
			firstFrame = currentFrame;
			fps = frames;
			frames = 0;
		}

		for(Rectangle r:objects){
			Barrier b = (Barrier)r;
			b.drawBarrier(g);
		}

		g.setColor(Color.BLUE);
    kugel.drawKugel(g);

		g.setColor(Color.RED);
		g.drawString("FPS: " + fps, 10, 20);
	}
}
```

3. Barrier


```
import java.awt.*;

class Barrier extends Rectangle {


	public Barrier(int x, int y, int w, int h) {
		super(x,y,w,h);
	}

	public void drawBarrier(Graphics g){
		g.fillRect(x, y, width, height);
	}
	
}
```

4. Kugel


```
import java.awt.*;

class Kugel extends Rectangle {

	private static final long	serialVersionUID	= 1L;
	private int	speed = 1;

	public Kugel(int x,int y,int w,int h) {
		super(x,y,w,h);
	}

	public int getSpeed() {
		return speed;
	}

	public void setSpeed(int speed) {
		this.speed = speed;
	}

	public void moveObject(){
		x+=speed;
	}
	
	public void drawKugel(Graphics g){
		g.fillRect(x, y, width, height);
	}


}
```


----------



## Grillmeister (7. Feb 2011)

Hi,

versuch mal die Geschwindigkeit der Kugel mit der vergangenen Zeit zu multiplizieren:

(vergangene Zeit zur letzten Bewegung)*Geschwindigkeit

Bei dir ist die Geschwindigkeit des Balls nämlich von den FPS abhängig und kann deshalb variieren.


----------



## Quaxli (7. Feb 2011)

Grillmeister hat gesagt.:


> Hi,
> 
> versuch mal die Geschwindigkeit der Kugel mit der vergangenen Zeit zu multiplizieren:
> 
> ...



Da käme was raus.... :autsch:
Wenn, sollte man das schon korrekt angeben:

durchschnittliche Bewegung  = Geschwindigkeit * Zeit für den Schleifendurchlauf / 1e9

Die Geschwindigkeit sind dabei Pixel/s
Die Division erfolgt hier durch 1e9, da ich davon ausgegangen bin, daß man idealerweise die Zeit in Nanosekunden mißt.


----------

