# Tastatureingaben parallel zum Gameloop



## User123 (6. Okt 2011)

Hallo Leute,

ferzeit befasse ich mich mit 2D-Programmierung und komme soweit bei meinem Pong-Spiel gut vorran. Überfordert bin ich allerdings wenn es um die Steuerung geht. Die Eingabe muss parallel zum Gameloop laufen. Ich weiß das ich dies mit runnable bewerkstelligen kann, ich weiß jedoch nicht, wie. Ich poste jetzt hier einfach meinen gesamten Quelltext, dieser ist in einer einzigen Datei, dementsprechend lang ist er. 


```
import java.awt.*;
import java.awt.event.*;
//import java.awt.geom.*;
import java.awt.font.*;
import java.util.*;
public class pingvspong04 extends Frame implements KeyListener {


int fensterx=800, //fenstergroesse
	fenstery=800, //fenstergroesse
	y=fenstery/2, //startpos Ball
	x=fensterx/2, //startpos Ball
	punktea=0,
	punkteb=0,
	schrittweite=18,
	aktualisierungsges=40,
	balkenabstand=150,
	winkel=2, //Veränderung des Winkels wenn Ball außen getroffen wird
	timesek=0;


Boolean spur=false, //Spur hinter dem Ball an- oder ausschalten!
		kollisionvar=false;
String title = "Ping vs. Pong 0.4";
Random rnd=new Random(); //Zufall Winkel
Random rnd2=new Random(); //Zufall links oder rechts
Random rnd3=new Random(); //Zufall oben oder unten
Color titlecolor = new Color (85,85,85);

static pingvspong04 a;
public pingvspong04() {


setSize(fensterx, fenstery);
setLocation(10, 10);
setBackground(new Color(0,0,0));
addKeyListener(this);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);

}
});
setVisible(true);
}



public void keyPressed(KeyEvent event)
   {


   }
public void keyReleased(KeyEvent event)
   {


   }

	public void keyTyped(KeyEvent event)
	{
		char key = event.getKeyChar();
  		if (key == KeyEvent.VK_BACK_SPACE)
  		{
	  		x=fensterx/2;
	  		y=fenstery/2;
	  		repaint();
		}
	}


public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;

int z=0,
	o=0,
	b=1,
	i=1,
	n=0,
	Bx,
	By,
	Ax,
	Ay,
	Ky,
	Kx,
	Kz,
	Ko,
	randoben=24,
	randunten=fenstery-160,
	speed=0,
	randomvarx=0,
	randomvary=0,
	schlegerAx=10,
	schlegerAy=(fenstery-balkenabstand)/2,
	schlegerBx=fensterx-20,
	schlegerBy=(fenstery-balkenabstand)/2,
	laenge=fenstery/10,
	kollisionsstelle=(laenge/2),
	gesspeed=30;



Graphics2D p1 = (Graphics2D)g;
p1.setPaint(Color.white);

Graphics2D p2 = (Graphics2D)g;
p2.setPaint(Color.white);

Graphics2D p3 = (Graphics2D)g;
p3.setPaint(titlecolor);

Graphics2D w = (Graphics2D)g;
Graphics2D w2 = (Graphics2D)g;

Graphics2D t = (Graphics2D)g;

g2.setColor(Color.white);


g2.fillRect(0,fenstery-balkenabstand,fensterx,10); //Balken


while(z+o!=30 || z==0 || o<=5 || o>=18)
{
	z=rnd.nextInt(31);
	o=rnd.nextInt(31);

}

randomvarx=rnd2.nextInt(2);
randomvary=rnd3.nextInt(2);

if (randomvarx==0)
{z=z*(-1);}

if (randomvary==0)
{o=o*(-1);}



/* FOR SCHLEIFE FÜR DEN SPIELABLAUF

||||||||||||||||||||||||||||||||||
||||||||||||||||||||||||||||||||||
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

*/


for(i=0;i<=100;i=0)
{

g2.fillRect(0,fenstery-balkenabstand,fensterx,10); //Balken

//--------------- TEXT
String points = punktea+" - "+punkteb;
String time = ""+timesek;

t.setColor(Color.white);//Punkte weiss
Font myFont=new Font("Verdana", Font.BOLD|Font.PLAIN, 25);
t.setFont( myFont ); //Schriftart setzen

t.clearRect((fensterx/5),(fenstery-120) ,100 , 100);
t.drawString(time,(fensterx/4),(fenstery-75));



g2.setColor(Color.white);//Punkte weiss
Font myFont2=new Font("Verdana", Font.BOLD|Font.PLAIN, 25);
g2.setFont( myFont2 ); //Schriftart setzen
g2.drawString(points,(fensterx/2)-30,(fenstery-75));

p3.setColor(titlecolor);//Spielname grau
Font myFont3=new Font("Impact", Font.PLAIN, 15);
p3.setFont( myFont3 ); //Schriftart setzen
p3.drawString(title,30,60);

g2.setColor(Color.white); //Color überschreiben und den Rest weiss

//--------------- TEXT

w.clearRect(0,0,10,fenstery-balkenabstand); //Balken
w2.clearRect(schlegerBx+10,0,10,fenstery-balkenabstand); //Balken

try {
	Thread.sleep(aktualisierungsges);
	}
	catch(Exception e) {
		}
y=y+(z);
x=x+(o);
g2.fillRect(x,y,10,10);
b=1;

if (spur==false)
{g2.clearRect((x-o),(y-z),10,10);}


p2.clearRect(schlegerBx, schlegerBy ,10 , laenge);



//Vorberechnung der Flugbahn

if(x>(fensterx/3)&& o>0)
	{	Kx=x;
		Ky=y;
		Kz=z;
		Ko=o;
		while(Kx<=schlegerBx-10)
		{
		Kx=Kx+(Ko);
		Ky=Ky+(Kz);

		if(Ky>=randunten)
		{
		Kz=Kz*(-1);
		}

		if(Ky<=randoben)
		{
		Kz=Kz*(-1);
		}
	}
//-------------- Schläger stoppt bei oberem Rand
				if(schlegerBy>=randoben)
				{
					if(Ky<=schlegerBy+10+(kollisionsstelle))
						{
							schlegerBy=schlegerBy-schrittweite;
						}
				}
//----------------------------------------- Schläger stoppt bei unterem Rand

				if(schlegerBy+laenge<=fenstery-balkenabstand)
				{
					if(Ky>=schlegerBy+10+(kollisionsstelle))
						{
							schlegerBy=schlegerBy+schrittweite;
						}
				}

		}





p2.fillRect(schlegerBx, schlegerBy ,10 , laenge);

p1.clearRect(schlegerAx, schlegerAy,10 , laenge);
if(x<(fensterx/3)&& o<0)
	{	Kx=x;
		Ky=y;
		Kz=z;
		Ko=o;
		while(Kx>=schlegerAx+10)
		{
		Kx=Kx+(Ko);
		Ky=Ky+(Kz);
		if(Ky>=randunten)
		{
		Kz=Kz*(-1);
		}
		if(Ky<=randoben)
		{
		Kz=Kz*(-1);
		}
	}
//-------------- Schläger stoppt bei oberem Rand
				if(schlegerAy>=randoben)
				{
						if(Ky<=schlegerAy+10+(kollisionsstelle))
						{
							schlegerAy=schlegerAy-schrittweite;
						}
					}
//----------------------------------------- Schläger stoppt bei unterem Rand

					if(schlegerAy+laenge<=fenstery-balkenabstand)
					{
						if(Ky>=schlegerAy+10+(kollisionsstelle))
						{
							schlegerAy=schlegerAy+schrittweite;
						}
					}

				}

p1.fillRect(schlegerAx, schlegerAy,10 , laenge);

kollisionvar=false;//kollisionsvariable false setzen!


//---------------KOLLISION-SCHLÄGER


if(x>=schlegerBx-10)
{
	By=schlegerBy;
	for(n=0;n<=laenge+10;n++)
	{


		if(x>=schlegerBx-10 && y==By-10)
		{
			o=o*(-1);

			if (o<(winkel+3)) //Ball fliegt nicht ins Aus oder wird zu steil
			{
//Oben
				if (n<=25)
				{

					if(z==0)
					{
						z=z-winkel;

						if(z<0)
						{o=-gesspeed-z;}

						if(z>0)
						{o=-gesspeed+z;}

						//o=o+winkel;
					}

					if (z<0)
					{
						z=z-winkel;
						o=o+winkel;
					}

					if (z>0)
					{
						z=z+winkel;
						o=o+winkel;
					}
				}
//Unten
				if (n>=65)
				{

					if(z==0)
					{
						z=z+winkel;

						if(z<0)
						{o=-gesspeed-z;}

						if(z>0)
						{o=-gesspeed+z;}

						//o=o+winkel;

					}

					if (z<0)
					{
						z=z-winkel;
						o=o+winkel;
					}

					if (z>0)
					{
						z=z+winkel;
						o=o+winkel;
					}
				}

//Mitte
				if (n>=40 && n<=50)
				{
					o=(-gesspeed);
					z=0;
					kollisionsstelle=0;
				}

			}//Winkeländereung

		kollisionvar = true;

		}if (kollisionvar == true)
		{break;}
		By=By+1;

	}
}



if(x<=schlegerAx)
{
	Ay=schlegerAy;
	for(n=0;n<=laenge+10;n++)
	{

		if(x<=schlegerAx && y==Ay-10)
		{
			o=o*(-1);

			if (o>(winkel+3)) //Ball fliegt nicht ins Aus oder wird zu steil
			{
//Oben
				if (n<=25)
				{
					if(z==0)
					{
						z=z-winkel;

						if(z<0)
						{o=gesspeed+z;}

						if(z>0)
						{o=gesspeed-z;}

						//o=o+winkel;
					}



					if (z<0)
					{
						z=z-winkel;
						o=o-winkel;
					}

					if (z>0)
					{
						z=z+winkel;
						o=o-winkel;
					}
				}
//Unten
				if (n>=65)
				{

					if(z==0)
					{
						z=z+winkel;

						if(z<0)
						{o=gesspeed+z;}

						if(z>0)
						{o=gesspeed-z;}

						//o=o+winkel;
					}

					if (z<0)
					{
						z=z-winkel;
						o=o+winkel;
					}

					if (z>0)
					{
						z=z+winkel;
						o=o+winkel;
					}
				}
//Mitte
				if (n>=40 && n<=50)
				{
					z=0;
				 	o=(gesspeed);
				 	kollisionsstelle=1;
				}

			}//Winkeländerung

		kollisionvar = true;

		}if (kollisionvar == true)
		{break;}
		Ay=Ay+1;

	}


}
//---------------KOLLISION-SCHLÄGER

if(y>=randunten || y<=randoben) //Kollision mit Ober- Unterkante
{
z=z*(-1);
}

	//Geschwindkeit des Balles erhöhen!

	speed=speed+1;

	if (speed%250==0) //--> Erhöhung der Ges. nach 10 sek
	{
		gesspeed=gesspeed+2;

		if (z>0)
		{z=z+1;}

		if (z<0)
		{z=z-1;}

		if (o>0)
		{o=o+1;}

		if (o<0)
		{o=o-1;}
	}

	//Geschwindkeit des Balles erhöhen!

	if(speed%25==0)
	{timesek=timesek+1;}

	if(x<(-10))
	{
	punkteb=punkteb+1;
	break;}

	if(x>(fensterx+10))
	{
	punktea=punktea+1;
	break;}
}
//----------------------------------FOR ENDE!
}
//----------------------------------GRAPHICS ENDE!


public static void main(String[] args) {
new pingvspong04();


}


}
```

Die Tastatureingabe für die Schläger müsste also ein eigener thread sein und parallel zum gameloop laufen.

Ich hoffe ihr könnt mir helfen. Selbst für Denkanstöße wäre ich dankbar.
Vielen Dank im Vorraus!


----------



## yyannekk (6. Okt 2011)

Also die Lösung deines Problems ist, die main-loop in einen Thread zu packen. Beispielsweise erstellst du eine Klasse die die Klasse Thread erbt oder sie implementiert. Von außen musst du dann den Thread starten. Der Thread läuft dann quasie parallel zu deinem anderen code.

Empfehlen würde ich dir mal ein Tutorial für Spieleprogrammierung (es gibt etliche) Schritt für Schritt durchzuarbeiten, da lernst du ne Menge.

Zu deinem restlichen Code: 
Du solltest dich mit Java Konventionen beschäftigen, damit dein Code lesbarer (auch für dich selber ) wird. ZB schreibt man Klasse groß.
Und man baut keine Gottklassen, d.h. splitte dein Programm in mehrere Klassen, zB eine Klasse für die main-loop, eine für die Grafik sachen, eine für den Keylistener...
Dafür wurde schließlich OOP gemacht.
Dann solltest du diese ganzen verschachtelten if statements vermeiden

Beispiel zum Thread:

```
public class BeispielThread extends Thread
{
    public void run()
    {
         while(true)
         { //..... mainloop .... // }
    }
}

public class StartUp
{
    public static void main(String ...args)
    {
        BeispielThread bt = new BeispielThread();
        bt.start();  //hier wird auch die Methode run() in dem Thread gestartet. Außerdem wird ab hier 
//dein Code parallel ausgeführt. D.h. die mainloop läuft (endlos), println() und alles was dannach kommt wird trotzdem ausgeführt
        System.out.println("parallel");

    }
}
```


----------



## Quaxli (6. Okt 2011)

Hilfe, meine  Augen bluten.... 
(Nur Spaß - nicht böse sein )

Konzeptionell solltest Du etliches umstellen. Das wichtigste hat yyanekk ja schon gesagt: Mehrere Klassen und ein 2. Thread für den GameLoop.

Hier noch ein paar weitere Anmerkungen von mir:

- Klassennamen schreibt man mit Großbuchstaben am Anfang

- in der paint-Methode sollte wirklich nur gezeichnet werden. Der GameLoop hat da nix verloren

- man benötigt nur 1 Graphics-Object
  Das kann alles weg:

```
Graphics2D p1 = (Graphics2D) g;
		p1.setPaint(Color.white);

		Graphics2D p2 = (Graphics2D) g;
		p2.setPaint(Color.white);

		Graphics2D p3 = (Graphics2D) g;
		p3.setPaint(titlecolor);

		Graphics2D w = (Graphics2D) g;
		Graphics2D w2 = (Graphics2D) g;

		Graphics2D t = (Graphics2D) g;
```
  Einfach überall die w's, w2's, etc. durch g2 ersetzen und gut ist's

- Die Verwendung von Variablen wie randunten oder randoben ist kein guter Programmierstil. Verwende ein geeignetes Grafik-Layout, wie z. B. BorderLayout und jeweils ein (J)Panel für das Spiel und eines für die Anzeige, dann kannst Du die Grenzen des Spielfeldes über die vorhandenen Methoden des Panels abfragen. ("oben" ist 0, "unten" kriegst Du über getHeight())

- Verwende Swing. Da sind die Komponenten von Haus aus doppelt gepuffert

- Du verwendest recht umständliche Logik für die Kollissions-Ermittlung. Verwende eigene Klassen, wie yyanekk schon gesagt hat und laß diese von Rectangle oder Rectangle2D.Double erben. Dann kannst Du die vorhanden Methoden intersects(..) (vgl. API) verwenden.

- Im KeyListener solltest Du keine repaint()-Aufrufe einbauen. Setze dort maximal boolean-Werte und arbeite diese im noch zu erstellenden GameLoop ab.

Soweit daß, was mir beim schnellen überfliegen aufgefallen ist.


----------



## Quaxli (6. Okt 2011)

Ich konnt's jetzt doch nicht lassen und hab' mal auf die Schnelle ein Beispiel gebastelt, so wie ich's gemacht hätte. Das sah mir doch zu wild aus, da oben.... 

Hier also mal was zum Abgucken. 
Es gibt 3 Klassen:
Pong (das GamePanel)
Ball (der Ball)
Player (für die beiden Schläger)

Tastaturabfrage ist rudimentär auch eingebaut.
Zähler und ähnliches nicht - Du sollst ja auch noch was zu tun haben 


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

public class Pong extends JPanel implements Runnable, KeyListener{

	private static final long	serialVersionUID	= 1L;
	JFrame frame;
	
	//Spielobjekte
	Ball ball;
	Player player1;
	Player player2;
	
	//Tastaturabfragen
	boolean up;
	boolean down;

	public static void main (String[] args){
		new Pong();
	}
	
	//Konstruktor
	public Pong(){
		setPreferredSize(new Dimension(600,600)); //Größe des GamePanels
		
		frame = new JFrame("Pong");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.add(this);
		frame.addKeyListener(this);
		frame.pack();
		frame.setVisible(true);
		
		//Spielobjekte erzeugen
		ball = new Ball(this);
		player1 = new Player(50,50,this);
		player2 = new Player(getWidth()-60,getHeight()-50,this);
		
		Thread th = new Thread(this);
		th.start();
	}
	

	//GameLoop
	@Override
	public void run() {

		while(frame.isVisible()){
			
			checkKeys();
			
			ball.move();
			
			//könnte auch noch in eine Logic-Methode ausgelagert werden.
			ball.doLogic();
			player1.doLogic();
			player2.doLogic();
			
			checkCollision();
			
			repaint();
			
			//Pause
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
			
		}
		
	}
	
	//Nur mal so - ohne konkrete Logik
	private void checkKeys(){
		
		if(up){
			System.out.println("'Pfeil oben' gedrückt");
		}
		
		if(down){
			System.out.println("'Pfeil unten' gedrückt");
		}
	}
	
	
	private void checkCollision(){
		
		if(ball.intersects(player1)){
			ball.collided();
		}
		
		if(ball.intersects(player2)){
			ball.collided();
		}
	}
	
	
	//bei Swing immer paintComponent überschreiben!!!
	@Override
	public void paintComponent(Graphics g){
		super.paintComponent(g); //super-Aufruf nicht vergessen
		
		g.setColor(Color.BLACK);
		g.fillRect(0, 0, getWidth(), getHeight());
			
		ball.paintBall(g);
		player1.paintPlayer(g);
		player2.paintPlayer(g);
	}

	public Ball getBall(){
		return ball;
	}
	
	@Override
	public void keyPressed(KeyEvent e) {
		
		if(e.getKeyCode()==KeyEvent.VK_UP){
			up = true;
		}
		
		if(e.getKeyCode()==KeyEvent.VK_DOWN){
			down = true;
		}
	}

	@Override
	public void keyReleased(KeyEvent e) {
		
		if(e.getKeyCode()==KeyEvent.VK_UP){
			up = false;
		}
		
		if(e.getKeyCode()==KeyEvent.VK_DOWN){
			down = false;
		}
		
		//Spielende
		if(e.getKeyCode()==KeyEvent.VK_ESCAPE){
			frame.setVisible(false);
		}
		
	}

	@Override
	public void keyTyped(KeyEvent e) {

	}
	
}


//Die Klasse für den Ball
class Ball extends Rectangle {

	private static final long	serialVersionUID	= 1L;
	//Bewegungsvariablen
	int dx = 0;
	int dy = 0;
	
	//Referenz zum GamePanel
	Pong gamepanel;

	public Ball(Pong p){
		super(300,300,10,10);
		gamepanel = p;
		dx = 2;
		dy = 1;
	}
	
	//hier wird gezeichnet
	public void paintBall(Graphics g){
		Graphics2D g2 = (Graphics2D) g;
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		g2.setColor(Color.RED);
		g2.fillOval(x, y, width, height);
	}
	
	public void move(){
		x = x+dx;
		y = y+dy;
	}
	
	public void doLogic(){
		
		//Abbrallen oben oder unten
		if(y<=0){
			dy *= -1;
		}
		
		if(y+height>=gamepanel.getHeight()){
			dy *= -1;
		}
		
		//Abbrallen links oder rechts
		if(x<=0){
			//Einfache Lösung auf die Schnelle
			System.out.println("Punkt für den Gegner");
			dx *= -1;
		}
		
		if(x+width>=gamepanel.getWidth()){
			//Einfache Lösung auf die Schnelle
			System.out.println("Punkt für den Gegner");
			dx *= -1;
		}
		
	}
	
	//mit irgendwas kollidiert
	public void collided(){
		dx *= -1;
	}
	
}

//Die Klasse für die Schläger
class Player extends Rectangle {

	private static final long	serialVersionUID	= 1L;
	Pong gamepanel;
	
	public Player(int x, int y, Pong p){
		super(x,y,10,50);
		gamepanel = p;
	}
	
	public void paintPlayer(Graphics g){
		g.setColor(Color.WHITE);
		g.fillRect(x, y, width, height);
	}
	
	public void doLogic(){
		
		//Bewegung nur, wenn der Ball auf den Schläger zufliegt - sieht professioneller aus ;-)
		if(gamepanel.getBall().x>x && gamepanel.getBall().dx>0){
			return;
		}
		
		if(gamepanel.getBall().x<x && gamepanel.getBall().dx<0){
			return;
		}

		//Anpassung der Höhe
		if(y+height/2>gamepanel.getBall().y){
			y-=2;
		}
		
		if(y+height/2<gamepanel.getBall().y){
			y+=2;
		}
		
		//Nicht über's Gamepanel hinaus
		if(y<0){
			y=0;
		}
		
		if(y+height>gamepanel.getHeight()){
			y = gamepanel.getHeight() - height;
		}
		
	}
	
}
```


----------

