# PingPong selber schreiben



## H3llGhost (31. Mrz 2008)

Hallo Leute

ich habe vor das Spiel PingPong zu schreiben.
Bis jetzt noch kein Problem ...
Ich habe einen sehr leichten Code und der hilft mir aber nur bei einem waagrecht fliegenden Ball.
Nun möchte ich diesen gerne waagrecht und senkrecht gleichzeitig (schräg) fliegen zu lassen.
Aber ich weiß nicht wie ich das realisieren soll.
Könnt ihr mir helfen?

Danke im Voraus!


----------



## ARadauer (31. Mrz 2008)

nach rechts: x++
nach oben: y++

nach rechts oben: x++; y++

du änderst halt nicht nur die waagrechte position sondern auch die senkrechte.....


----------



## H3llGhost (31. Mrz 2008)

Danke!
Das ist ja die simpelste Methode ... 

Was ist nun aber wenn ich sage er soll gegen eine Wand prallen und je nachdem wie er dagegen prallt, soll er wieder mit einem bestimmten Winkel, der errechnet werden soll, abprallen.
Wie sieht es dann aus?


----------



## Marco13 (31. Mrz 2008)

Da Einfallswinkel==Ausfallswingkel ist, wirst du bei "x++ und y++" soweiso nie einen anderen Winkel als 0, 90 oder 45 grad haben, und das kann man notfalls mit "if" abfragen... Wenn du allgemeinere Bewegungen möglich machen willst, solltest du für die Kugel die Position UND die Geschwindigkeit speichern. Beides besteht aus jeweils 2 ints oder besser floats. Wenn du die Geschwindigkeit dann erstmal als schönen 2D-Vektor hast, kannst du damit die fiesesten Berechnungen machen...


----------



## H3llGhost (31. Mrz 2008)

Hallo Marco,

danke für deine Hilfe.
Das mit dem Einfallswinkel==Ausfallswinkel habe ich mir so auch schon gedacht und das habe ich auch nun geschafft, dass es läuft ... 

Aber wie würdest du überhaupt mit einem 2D Vector anfangen?
Eine Funktion move zum Beispiel schreiben die als Parameter die Position und Geschwindigkeit entgegennimmt?


----------



## Marco13 (31. Mrz 2008)

Höm. Die Parameter braucht sie nichtmal unbedingt. Es würde sich evtl. anbieten, eine Klasse zu machen, die Position und Geschwindigkeit zusammenfasst. Die braucht dann nicht viel mehr als eine "move()"-Methode - vorausgesetzt, man will die Geschwindigkeit nicht von außen beeinflussen. Über Kollisionserkennung und -antwort muß man sich dann halt noch Gedanken machen....


----------



## 0x7F800000 (31. Mrz 2008)

ne. move(long millis) nimmt höchstens die vergangene zeit entgegen. Position und Geschwindigkeit sind doch eigenschaften des objektes => membervariablen, warum soll man die von außen angeben können?

[edit] *bezieht sich auf den vorletzten beitrag, an Marco13's beitrag hab ich nichts auszusetzen  *


----------



## H3llGhost (1. Apr 2008)

@Marco13:
Was würdest du denn noch in die Klasse packen?
Also Positions- und Richtungsangaben ...
Und die Funktion move() wird dann von dem Thread ausgeführt oder?


----------



## Quaxli (1. Apr 2008)

Alles was die Klasse braucht  ,das hängt u. a. davon ab, was Du alles realisieren willst. Und ja, die move-Methode würde periodisch vom Thread aufgerufen. 

Für die Bewegungsänderung bei Kollision würde ich nicht unbedingt mit Vektoren anfangen. Im Prinzip ändert sich immer nur eine Bewegungskomponente. Wenn der Ball z. B. an die rechte Wand prallt, muß man nur die x-Bewegung negieren und die y-Bewegung gleich lassen (nach oben - also negativ). Schon hat man eine Umsetzung von Einfall- gleich Ausfallswinkel. Analog dazu ändert sich bei  einer Kollision mit dem oberen Rand auch nur y-Bewegungsrichtung und die x-Bewegung bleibt gleich.


----------



## 0x7F800000 (1. Apr 2008)

vielleicht noch ein paar informationen über das aussehen des balls, etwa radius, farbe, oder die referenz zu einem bild, das an seiner stelle gezeichnet wird. Evtl auch eine eigene methode, die den ball zeichnet...


----------



## H3llGhost (1. Apr 2008)

Also hier vielleicht mal mein Programm zum besseren Einblick:

GUI:

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

public class ppGUI extends JFrame implements ActionListener, MouseMotionListener
{
  private JPanel einPanel;
  private ppDraw ppd;
  public ppGUI()
  {
    setTitle("Ping Pong eXTr3m3");                             //Titel des Rahmen
    setSize(500,500);                                  //Größe des Rahmen in Pixel
    setDefaultCloseOperation(EXIT_ON_CLOSE);           //Schließt den Rahmen
    setResizable(false);

    einPanel = new JPanel();                           //Erzeugen der "Leinwand"
    einPanel.setLayout(null);
    einPanel.setBackground(Color.BLACK);
    
    ppd = new ppDraw();
    ppd.setBounds(50,50,400,300);
    
    addMouseMotionListener(this);

    //am Ende:
    einPanel.add(ppd);
    add(einPanel);
    setVisible(true);                                //Sichtbar machen
  }
  
  public void actionPerformed(ActionEvent evt)
  {

  }

  public void mouseDragged(MouseEvent e)
  {

  }

  public void mouseMoved(MouseEvent e)
  {
    ppd.setYKoordinate(e.getY()-110);
    System.out.println(ppd.getYKoordinate());
    ppd.repaint();
  }

  public static void main(String args[])
  {
    ppGUI pp = new ppGUI();
  }
}
```

Klasse, die für das Zeichnen zuständig ist:

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

class ppDraw extends JPanel implements Runnable
{
  private int YKoordinate = 0;
  private int PositionX = 21, PositionY = 150;
  private int xd, yd;
  private Thread runner;
  public ppDraw()
  {
    setBackground(Color.WHITE);
    xd = 0;
    yd = 0;
    runner = new Thread(this);
    runner.start();
  }
  public void paintComponent(Graphics g)
  {
    super.paintComponent(g);
    g.fillRect(0,YKoordinate,9,80);
    g.setColor(Color.BLACK);
    g.fillOval(PositionX,PositionY,9,10);
    g.setColor(Color.BLACK);
  }
  public void setYKoordinate(int y)
  {
    YKoordinate = y;
    if (y < 0)
       YKoordinate = 0;
    if (y > 330)
       YKoordinate = 330;
  }
  public int getYKoordinate()
  {
    return YKoordinate;
  }
  public void startThread()
  {
    runner.start();
  }
  public void bmove(int xd, int yd)
  {
    if (xd == 0)
    {
      PositionX = PositionX + 3;
      if ((PositionX == 9) && (PositionY < (getYKoordinate()+80)) && ((getYKoordinate()-10) < PositionY))
         xd = 1;
      else if (PositionX > 390)
      {
        xd = 1;
        System.out.println("game over");
      }
    } else if (xd == 1)
    {
      PositionX = PositionX - 3;
      if ((PositionX == 9) && (PositionY < (getYKoordinate()+80)) && ((getYKoordinate()-10) < PositionY))
        xd = 0;
      else if (PositionX < 0)
      {
        xd = 0;
        System.out.println("game over");
      }
    }
    if (yd == 0)
    {
      PositionY = PositionY + 3;
    } else if (yd == 1)
    {
      PositionY = PositionY - 3;
    }
    try
    {
      Thread.sleep(20);
    }
    catch(InterruptedException e)
    {
      System.out.println("Thread Fehler");
    }
    repaint();
  }
  public void run()
  {
    while(true)
    {
     bmove(xd, yd);
    }
  }
}
```

Und so sollte es am Anfang mit der Grafik auch getan sein ... 
Für den Anfang reicht es ja wohl ...


----------



## Quaxli (1. Apr 2008)

Nun ja, Klassennamen schreibt man schon mal groß. Und wenn jemand JPanels als Grafikelemente verwendet, stellen sich mir immer die Fußnägel auf (= persönliche Abneigung, man kann es schon verwenden...) 
Das Ganze ist etwas überfrachtet - meiner Ansicht nach und der Ball fliegt bei mir immer raus. Aber für einen ersten Versuch nicht schlecht.

Ich habe mal qick & dirty ein Gegenbeispiel zusammengefrickelt. Verwende es oder laß es 

Ein paar Anmerkungen dazu.
- Das Ganze ist in eine Klasse gequetscht, aber nicht uwesentlich unübersichtlicher als Dein Code . Üblicherweis würde man für Schläger und Ball wohl eigene Klassen als sinnvoll erachten (die könnten auch von Rectangle erben)
- die Ballbewegung ist einfach auf einen Integer-Wert festgebacken. Üblicherweise würde man die Zeit seit dem letzten Schleifendurchlauf mitberücksichtigen, wodurch die Bewegung flüssiger erscheint.
- es sind nicht alle Fehlerquelle ausgeschaltet - u. U. könnte der Ball auch rausfliegen.
- ein GameOver-Bedingung fehlt.
- und noch ein paar Unschönheiten mehr... . Aber es funzt 



```
package src;

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

public class Example extends JPanel implements Runnable, MouseMotionListener{
	//Klassenanmen schreibt man groß!!!!

	private static final long serialVersionUID = 1L;
	
	JFrame frame;   //das Fenster
	Rectangle bat;  //der Schläger
	Rectangle ball; // der Ball
	
	int dx = 2; //Bewegungsänderung in x
	int dy = 2; //Bewegungsänderung in y
	
	public static void main(String[] args){
		new Example(); //diese Klasse initialisieren
	}
	
	
	public Example(){
		
		//Fenster erzegeugen
		frame = new JFrame("Beispiel");
		frame.setSize(500,500);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.add(this);	//die Instanz dieses JPanels in Fenster packen
		
		bat = new Rectangle(10,250,10,60);  //Schläger initialisieren
		ball = new Rectangle(200,200,10,10); // Ball initialisieren

		setBackground(Color.black); //Panel-Hintergrudn schwarz
		addMouseMotionListener(this); //MouseMotionListener ins JPanel packen
		
		frame.setVisible(true); //alles anzeigen
		
		Thread t = new Thread(this); //GameLoop in eingenem Thread
		t.run();
		
	}
	
	
	
	public void run() {
	
		while(frame.isVisible()){  //Thread so lange ausführen, wie Fenster sichtbar ist.
			
			moveBall();       //Ball bewegen
			checkCollision(); //Schläger-Ball-Kollision
			
			try {
				Thread.sleep(20);  //Päuschen
			} catch (InterruptedException e) {}
			
			repaint();  //Repaint
		}
		
	}
	
	private void checkCollision(){
		
		if(bat.intersects(ball)){  //Standard-Methode von Rectangle intersetcts(Rectangle r)
			dx *= -1;                //dx negieren, damit Ball wegfliegt
			ball.x += dx;            //einmal bewegen, damit Kollisionsabfrage nicht nochmal greift
		}
		
	}
	
	private void moveBall(){
		
		//wenn linke Seite des Balls nicht mehr sichtbar. X-Bewegung ändern
		if(ball.x<0){
			dx = 2;
		}
		
		//das selbe in Grün für die rechte Seite
		if(ball.x+ball.width >getWidth()){
			dx = -2;
		}
		
		//Beide Bedingung für analog für Y
		if(ball.y<0){
			dy = 2;
		}
		
		if(ball.y+ball.height>getHeight()){
			dy = -2;
		}

		//simple Ballbewegung - unabhängig vom GameLoop
		ball.x += dx;
		ball.y += dy;

	}


	@Override
	//Hier wird gemalt
	protected void paintComponent(Graphics g) {
		super.paintComponent(g); //super-Aufruf nicht vergessen.
		
		//Schläger
		g.setColor(Color.red);
		g.fillRect(bat.x, bat.y, bat.width, bat.height);
		
		//Ball
		g.setColor(Color.blue);
		g.fillOval(ball.x, ball.y, ball.width, ball.height);
		
	}


	//MouseMotionListener-Methoden
	public void mouseDragged(MouseEvent e) {
		
	}


	//einfache Übernahme y-Wert auch wenn Schläger nach unten verschwinden kann.
	public void mouseMoved(MouseEvent e) {
		bat.y = (int) e.getPoint().getY();
	}


	
	
	

}
```

<edit>
AAAARGH! Falschen Code kopiert. Ich Depp... . Jetzt paßt's 
</edit>


----------



## H3llGhost (2. Apr 2008)

Hallo,

danke für den Code ... 
Der gefällt mir aber warum Rectangle?
Und was ist das überhaupt?

Desweiteren ruckelt der Ball beim Bewegen des schlägers ...
Woran kann das Liegen?


----------



## Guest (2. Apr 2008)

Quaxli hat gesagt.:
			
		

> Und wenn jemand JPanels als Grafikelemente verwendet, stellen sich mir immer die Fußnägel auf (= persönliche Abneigung, man kann es schon verwenden...)



Begründ das mal bitte. Weil ich hab in meinem Game ne Menge JPanels drin, eventuell machst du mir nen Umstieg schmackhaft.

Danke schomal


----------



## 0x7F800000 (2. Apr 2008)

Naja, wenn man nicht vorhat, irgendwelche weiteren Komponenten auf dem JPanel zu plazieren, dann kann man auch gleich die abstraktere JComponent klasse nehmen, dann verschwendet man keinen speicher für irgendwelche listen für child-komponenten, die man eh nicht braucht. Und dann ist die liste der alternativen bei der automatischen wort-ergänzungs-dingens kürzer, kann man ein bisschen schneller das nötige finden. Also, man braucht keine begründung *gegen* JPanels als reine grafikkomponenten, es gibt einfach keine argumente *dafür*.


----------



## Quaxli (2. Apr 2008)

Du willst doch nur ein Rechteck zeichnen, daß den Schläger darstellt und die Kollision ermitteln? Warum als ein JPanel verwenden, daß dafür viel zu überfrachtet ist. Was bringt Dir die Klasse JPanel, was in Rectangle nicht drin ist? Sogar die Kollisionserkennung kriegst Du schon mitgeliefert - zumindest, wenn Sie auf Basis von Rectangles stattfindet und wenn Du pixelgenaue Kollision willst, ist ein JPanel auch nicht besser geeignet.

Die Bewegung ruckelt so, weil ich einen fixen Wert für die Veränderung der x- und y-Werte angegeben habe. Üblicherweise mißt man die Zeit, die der letzte Schleifendurchlauf benötigt hat und zieht diesen zur Berechnung der Bewegung heran. Nicht jeder GameLoop benötigt ja gleich lange für den Schleifendurchlauf, weil manchmal durch if-Bedingungen Code ausgeführt wird und manchmal nicht. Daher macht es Sinn, die Bewegung an die benötigte Zeit für den Schleifendurchlauf zu koppeln, damit die Bewegung flüssiger wird. (Soweit ich gesehen habe, ruckelt es bei Dir auch, nur verläßt der Ball zu schnell das Spielfeld).

Ich habe mal den Code verändert, so daß die Bewegung jetzt flüssiger läuft:



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

public class Example extends JPanel implements Runnable, MouseMotionListener{
   //Klassenanmen schreibt man groß!!!!

   private static final long serialVersionUID = 1L;
   private static int SPEED = 75;
   
   JFrame frame;   //das Fenster
   Rectangle bat;  //der Schläger
   Rectangle ball; // der Ball
   
   int dx = SPEED; //Bewegungsänderung in x
   int dy = SPEED; //Bewegungsänderung in y
   long time = 0;
   
   public static void main(String[] args){
      new Example(); //diese Klasse initialisieren
   }
   
   
   public Example(){
      
      //Fenster erzegeugen
      frame = new JFrame("Beispiel");
      frame.setSize(500,500);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.add(this);   //die Instanz dieses JPanels in Fenster packen
      
      bat = new Rectangle(10,250,10,60);  //Schläger initialisieren
      ball = new Rectangle(200,200,10,10); // Ball initialisieren

      setBackground(Color.black); //Panel-Hintergrudn schwarz
      addMouseMotionListener(this); //MouseMotionListener ins JPanel packen
      
      frame.setVisible(true); //alles anzeigen
      
      time = System.nanoTime();
      
      Thread t = new Thread(this); //GameLoop in eingenem Thread
      t.run();
      
   }
   
   
   
   public void run() {
   
      while(frame.isVisible()){  //Thread so lange ausführen, wie Fenster sichtbar ist.
         
         moveBall();       //Ball bewegen
         checkCollision(); //Schläger-Ball-Kollision
         
         try {
            Thread.sleep(20);  //Päuschen
         } catch (InterruptedException e) {}
         
         repaint();  //Repaint
      }
      
   }
   
   private void checkCollision(){
      
      if(bat.intersects(ball)){  //Standard-Methode von Rectangle intersetcts(Rectangle r)
         dx *= -1;                //dx negieren, damit Ball wegfliegt
      }
      
   }
   
   private void moveBall(){
      
      //wenn linke Seite des Balls nicht mehr sichtbar. X-Bewegung ändern
      if(ball.x<0){
         dx = SPEED;
      }
      
      //das selbe in Grün für die rechte Seite
      if(ball.x+ball.width >getWidth()){
         dx = -SPEED;
      }
      
      //Beide Bedingung für analog für Y
      if(ball.y<0){
         dy = SPEED;
      }
      
      if(ball.y+ball.height>getHeight()){
         dy = -SPEED;
      }

      

      long delta = System.nanoTime() - time;  //Zeit seit dem letzen Schleifendurchlauf
      time = System.nanoTime();  //für die nächste Runde Zeit merken
  		 
      ball.x += dx*(delta/1e9); //Bewegung in Abhängigkeit der für den Loop benötigten Zeit.
      ball.y += dy*(delta/1e9);


   }


   @Override
   //Hier wird gemalt
   protected void paintComponent(Graphics g) {
      super.paintComponent(g); //super-Aufruf nicht vergessen.
      
      //Schläger
      g.setColor(Color.red);
      g.fillRect(bat.x, bat.y, bat.width, bat.height);
      
      //Ball
      g.setColor(Color.blue);
      g.fillOval(ball.x, ball.y, ball.width, ball.height);
      
   }


   //MouseMotionListener-Methoden
   public void mouseDragged(MouseEvent e) {
      
   }


   //einfache Übernahme y-Wert auch wenn Schläger nach unten verschwinden kann.
   public void mouseMoved(MouseEvent e) {
      bat.y = (int) e.getPoint().getY();
   }


   
   
   

}
```


----------



## Marco13 (2. Apr 2008)

@Andrey: JComponent erbt schon von Container. Tatsächlich ist es so, dass JPanel keine zusätzlichen Fields enthält, ausser denen, die schon von JComponent, Container und Component geerbt werden. 
Aber das sind einige. 
Irgendwann hatte ich das (genau in diesem Zusammenhang) schon mal rein interesshalber aufgedröselt. Darum nochmal die 83 Gründe, warum man NICHT "mal kurz" von JComponent erben sollte, weil man irgendeinen Furz-Sprite zeichnen will:

```
private boolean isAlignmentXSet;
    private float alignmentX;
    private boolean isAlignmentYSet;
    private float alignmentY;
    protected transient ComponentUI ui;
    protected EventListenerList listenerList = new EventListenerList();
    private transient ArrayTable clientProperties;
    private VetoableChangeSupport vetoableChangeSupport;
    private boolean autoscrolls;
    private Border border;
    private int flags;
    private InputVerifier inputVerifier = null;
    private boolean verifyInputWhenFocusTarget = true;
    transient Component         paintingChild;
    private JPopupMenu popupMenu;
    private InputMap focusInputMap;
    private InputMap ancestorInputMap;
    private ComponentInputMap windowInputMap;
    private ActionMap actionMap;
    private boolean aaText;
    int ncomponents;
    Component component[] = new Component[0];
    LayoutManager layoutMgr;
    private LightweightDispatcher dispatcher;
    private transient FocusTraversalPolicy focusTraversalPolicy;
    private boolean focusCycleRoot = false;
    private boolean focusTraversalPolicyProvider;
    private transient Set printingThreads;
    private transient boolean printing = false;
    transient ContainerListener containerListener;
    transient int listeningChildren;
    transient int listeningBoundsChildren;
    transient int descendantsCount;
    transient ComponentPeer peer;
    transient Container parent;
    transient AppContext appContext;
    int x;
    int y;
    int width;
    int height;
    Color       foreground;
    Color       background;
    Font        font;
    Font        peerFont;
    Cursor      cursor;
    Locale      locale;
    transient GraphicsConfiguration graphicsConfig = null;
    transient BufferStrategy bufferStrategy = null;
    boolean ignoreRepaint = false;
    boolean visible = true;
    boolean enabled = true;
    boolean valid = false;
    DropTarget dropTarget;
    Vector popups;
    private String name;
    private boolean nameExplicitlySet = false;
    private boolean focusable = true;
    private int isFocusTraversableOverridden = FOCUS_TRAVERSABLE_UNKNOWN;
    Set[] focusTraversalKeys;
    private boolean focusTraversalKeysEnabled = true;
    Dimension minSize;
    boolean minSizeSet;
    Dimension prefSize;
    boolean prefSizeSet;
    Dimension maxSize;
    boolean maxSizeSet;
    transient ComponentOrientation componentOrientation = ComponentOrientation.UNKNOWN;
    boolean newEventsOnly = false;
    transient ComponentListener componentListener;
    transient FocusListener focusListener;
    transient HierarchyListener hierarchyListener;
    transient HierarchyBoundsListener hierarchyBoundsListener;
    transient KeyListener keyListener;
    transient MouseListener mouseListener;
    transient MouseMotionListener mouseMotionListener;
    transient MouseWheelListener mouseWheelListener;
    transient InputMethodListener inputMethodListener;
    transient RuntimeException windowClosingException = null;
    long eventMask = AWTEvent.INPUT_METHODS_ENABLED_MASK;
    private PropertyChangeSupport changeSupport;
    boolean isPacked = false;
    transient private Object privateKey = new Object();
    private int boundsOp = ComponentPeer.DEFAULT_OPERATION;
```

Ein Schläger oder ein Ball brauchen keine focusTraversalPolicy und keinen vetoableChangeSupport ... eigentlich brauchen sie fast NICHTS, ausser x,y,width,height....


----------



## 0x7F800000 (2. Apr 2008)

OMFG :shock: nun ja, dass es soviel zeugs ist... doch, hab ich mir schon fast gedacht 
Aber wenn ich irgendetwas zeichnen will, dann muss ich das doch auf einer Komponente machen, die wenigstens von JComponent erbt, ansonsten kann ich das nicht mal ins Fenster einfügen.
Ich hab mir die hier geposteten Codes nicht angegugt (zu lang) aber wenn der OP _jeden sprite_ auf einem eigenen JPanel gezeichnet hat, statt alles auf einmal auf _einem einzigen_ JComponent, dann ist das natürlich unsinn  :toll:


----------



## Guest (2. Apr 2008)

Danke für die Begründung


----------



## H3llGhost (2. Apr 2008)

@Quaxli:

Macht es einen Unterschied ob ich jetzt ball.intersects oder bat.intersects nehme?


----------



## Quaxli (3. Apr 2008)

Über die Frage hast Du nicht ernsthaft nachgedacht, als Du sie hingeschrieben hast, oder? 
Bzw. hat es einen Unterschied gemacht, als Du es ausprobiert hast? 

Soviel zum Wink mit dem Zaun(pfahl)...


----------



## H3llGhost (3. Apr 2008)

Doch eigentlich schon ...
Und ich habe es gerade auch ausprobiert was passiert, also ich sehe keinen Unterschied.

Ich habe im Internet gelesen, dass man für das Spiel zwei Threads machen sollte, damit es ruckelfreier läuft.
Für mich ist aber die Frage wie ...


----------



## Quaxli (3. Apr 2008)

Natürlich macht das keinen Unterschied, ob Du prüfst ob sich Ball und Schläger oder Schläger und Ball überschneiden b)

Nur weil es im Internet steht, muß es ja nicht stimmen 
2 wie im Beispiel reichen eigentlich.


----------



## H3llGhost (3. Apr 2008)

Du hast zwei Threads?
Habe ich garnicht gesehen ... 

Also ich habe folgendes nun geschrieben, aber ich verstehe nicht warum der Ball nicht weiterläuft ...

EDIT:
Habe meinen Fehler gefunden ...
Ich habe vergessen eine While-Schleife einzubinden ... 
Werde mich nun um die Kollisionserkennung kümmern ...

Leider ruckelt es bei mir immer noch und die Kollsionserkennung ist auch nicht sauber ... 
Woran kann das liegen?
Die Berechnung der Zeit und die Kollisionserkennung kommen von dir ... 
Die sind noch im Original Zustand ... 

So hier nochmal der Code.
Mir ist aufgefallen, dass der Ball nicht immer mit gleicher Geschwindigkeit fliegt ...  :shock: 
Und ich habe keine Ahnung warum der so extreme Unterschiede hat ...

Hauptklasse:

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

public class ppGUI extends JFrame implements MouseMotionListener
{
  private JPanel einPanel;
  private ppDraw ppd;
  public ppGUI()
  {
    setTitle("Ping Pong eXTr3m3");                             //Titel des Rahmen
    setSize(500,500);                                  //Größe des Rahmen in Pixel
    setDefaultCloseOperation(EXIT_ON_CLOSE);           //Schließt den Rahmen
    setResizable(false);

    einPanel = new JPanel();                           //Erzeugen der "Leinwand"
    einPanel.setLayout(null);
    einPanel.setBackground(Color.BLACK);
    
    ppd = new ppDraw();
    ppd.setBounds(50,50,400,300);
    
    addMouseMotionListener(this);

    //am Ende:
    einPanel.add(ppd);
    add(einPanel);
    setVisible(true);                                //Sichtbar machen
  }

  public void mouseDragged(MouseEvent e)
  {

  }

  public void mouseMoved(MouseEvent e)
  {
    ppd.bat.y=(e.getY()-100);
  }

  public static void main(String args[])
  {
    ppGUI pp = new ppGUI();
  }
}
```

Drawklasse:

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

class ppDraw extends JPanel implements Runnable
{
  Thread runner;
  Thread runner2;
  Rectangle bat, ball;
  ppBall ppb;

  public ppDraw()
  {
    setBackground(Color.WHITE);

    bat = new Rectangle(0,250,10,60);  //Schläger initialisieren
    ball = new Rectangle(200,200,10,10); // Ball initialisieren

    ppb = new ppBall(ball, this);

    runner = new Thread(this);
    runner.start();
  }
  
  @Override protected void paintComponent(Graphics g)
  {
    super.paintComponent(g);
      if (bat.y < 0) bat.y = 0;
      //Schläger
      g.setColor(Color.red);
      g.fillRect(bat.x, bat.y, bat.width, bat.height);

      //Ball
      g.setColor(Color.blue);
      g.fillOval(ball.x, ball.y, ball.width, ball.height);
  }
  
   private void checkCollision(){

      if(bat.intersects(ball)){  //Standard-Methode von Rectangle intersetcts(Rectangle r)
         ppb.dxnega();                //dx negieren, damit Ball wegfliegt
      }

   }

  public void run()
  {
    while(true)
    {
     checkCollision();
     try {
     Thread.sleep(20);  //Päuschen
     } catch (InterruptedException e)
     {
      System.out.println(e);
     }
     repaint();
    }
  }
}
```

Ballklasse:

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

public class ppBall implements Runnable
{

  private static int SPEED = 100;

  int dx = SPEED; //Bewegungsänderung in x
  int dy = SPEED; //Bewegungsänderung in y
  long time = 0;
  Rectangle ball;
  ppDraw p;
  Thread runner;

  public ppBall(Rectangle b, ppDraw pp)
  {
    ball = b;
    p = pp;
    time = System.nanoTime();
    runner = new Thread(this);
    runner.start();
  }

  public void dxnega()
  {
    dx *= -1;
  }

  public void run()
  {
      while (p.isVisible())
      {
      //wenn linke Seite des Balls nicht mehr sichtbar. X-Bewegung ändern
      if(ball.x<0){
         dx = SPEED;
      }

      //das selbe in Grün für die rechte Seite
      if(ball.x+ball.width>p.getWidth()){
         dx = -SPEED;
      }

      //Beide Bedingung für analog für Y
      if(ball.y<0){
         dy = SPEED;
      }

      if(ball.y+ball.height>p.getHeight()){
         dy = -SPEED;
      }

      long delta = System.nanoTime() - time;  //Zeit seit dem letzen Schleifendurchlauf
      time = System.nanoTime();  //für die nächste Runde Zeit merken

      System.out.println(delta/1e9);

      ball.x += dx*(delta/1e9); //Bewegung in Abhängigkeit der für den Loop benötigten Zeit.
      ball.y += dy*(delta/1e9);
     try {
     Thread.sleep(15);  //Päuschen
     } catch (InterruptedException e)
     {
      System.out.println(e);
     }
      }
  }
}
```


----------



## Quaxli (4. Apr 2008)

Natürlich habe ich zwei Threads. Was macht wohl dieser Code:


```
runner = new Thread(this);
    runner.start();
```



Ich gucke mal bei dem anderen Problem. Übrigens Klassennamen werden immer noch GROSS geschrieben.


----------



## Quaxli (4. Apr 2008)

So, hab's gefunden. Typischer Anfängerfehler -> zu viele Threads.  Ich habe mal bißchen was geändert, schau's Dir an:


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

public class PPDraw extends JPanel implements Runnable{
  Thread runner;
  Thread runner2;
  Rectangle bat, ball;
  PPBall ppb;
  long time = 0;
  long delta = 0;

  public PPDraw(){
  	
    setBackground(Color.WHITE);

    bat = new Rectangle(0,250,10,60);  //Schläger initialisieren
    ball = new Rectangle(200,200,10,10); // Ball initialisieren

    ppb = new PPBall(ball, this);

    runner = new Thread(this);
    runner.start();
    
    time = System.nanoTime();
  }
 
  @Override protected void paintComponent(Graphics g)
  {
    super.paintComponent(g);
      if (bat.y < 0) bat.y = 0;
      //Schläger
      g.setColor(Color.red);
      g.fillRect(bat.x, bat.y, bat.width, bat.height);

      //Ball
      g.setColor(Color.blue);
      g.fillOval(ball.x, ball.y, ball.width, ball.height);
  }
 
   private void checkCollision(){

      if(bat.intersects(ball)){  //Standard-Methode von Rectangle intersetcts(Rectangle r)
         ppb.dxnega();                //dx negieren, damit Ball wegfliegt
      }

   }

  public void run()
  {
    while(true){
    	
     computeDelta();	//delta kann man öfter gebrauche und berechnet man zentral für alle Methoden
     checkCollision();
     ppb.moveBall(delta);
     
     
     try {
     Thread.sleep(20);  //Päuschen
     } catch (InterruptedException e)
     {
      System.out.println(e);
     }
     repaint();
    }
  }
  
  private void computeDelta(){
  	delta = System.nanoTime()-time;
  	time = System.nanoTime();
  }
}
```



```
import java.awt.*;

public class PPBall {

  private static int SPEED = 100;

  int dx = SPEED; //Bewegungsänderung in x
  int dy = SPEED; //Bewegungsänderung in y
  long time = 0;
  Rectangle ball;
  PPDraw p;
  Thread runner;

  public PPBall(Rectangle b, PPDraw pp)  {
    ball = b;
    p = pp;
  }

  public void dxnega()
  {
    dx *= -1;
  }

  public void moveBall(long delta){
  	
      //wenn linke Seite des Balls nicht mehr sichtbar. X-Bewegung ändern
      if(ball.x<0){
         dx = SPEED;
      }

      //das selbe in Grün für die rechte Seite
      if(ball.x+ball.width>p.getWidth()){
         dx = -SPEED;
      }

      //Beide Bedingung für analog für Y
      if(ball.y<0){
         dy = SPEED;
      }

      if(ball.y+ball.height>p.getHeight()){
         dy = -SPEED;
      }


      ball.x += dx*(delta/1e9); //Bewegung in Abhängigkeit der für den Loop benötigten Zeit.
      ball.y += dy*(delta/1e9);


  }
}
```


----------



## H3llGhost (4. Apr 2008)

Hallo,

danke für den Code!
Aber es sind immer noch so extreme Schwankungen in der Geschwindigkeit ...
Und warum funktioniert die Kollsionsabfrage nicht sauber? Ich verstehe das nicht ...


----------



## H3llGhost (6. Apr 2008)

So das Problem mit der Kollsionsabfrage habe ich nun gelöst, in dem ich einfach die checkCollision() in die Klasse PPBall gepackt habe und desweiteren die Funktion dxnega() angepasst.
Nun brauche ich aber nochmals deine Hilfe ...
Wenn der Ball die Wand berührt habe ich ja noch die dx- und dy-Werte mit denen kann ich ja den Winkel ausrechnen es müssten mit den trigonometrischen Funktionen gehen ...
Dann habe ich ja den Winkel und mit Hilfe des Winkels kann ich doch dx und dy neu ausrechnen nur halt mit richtigen Abprallwinkel oder? 
Was meinst du?


----------



## H3llGhost (25. Apr 2008)

Hallo Leute,

ich bins mal wieder ...
ich habe vor das Spiel über Netzwerk laufen zu lassen und dazu habe ich einen eigenen Server geschrieben.
Dieser Server läuft auch eigentlich ganz gut ... 
Aber nun meine Frage:
Sollte man für jeden Client einen eigenen Thread erstellen?
Oder wir würdet ihr das machen?


----------

