# Probleme mit redraw()



## bob_sheknowdas (2. Dez 2011)

Hallo,

ich versuche mich gerade an einem Spiel ähnlich dem Handygame Bounds.
Es geht darum einen kleinen Ball durch eine Welt zu steuern. Ich bin noch nicht besaonders weit, denn die grafische Dastellung (vor allem die redraw()-Methode) mach mir Probleme. 
Auf einen tastendruck hin soll der Ball springen, aber es passiert optisch einfach nichts.

Hier mal mein Code. Es gibt bis jetzt vier Klassen: 

Die Startklasse Spiel:

```
public class Spiel {
	
	public static void main(String[] args) {
		ball = new Ball(ausdehnung);
		oberflaeche = new GUI(ausdehnung);
	}
	
	public static GUI getGUI(){
		return oberflaeche;
	}
	
	public static Ball getBall(){
		return ball;
	}
	
	private static Ball ball;
	private static GUI oberflaeche;
	private static int ausdehnung = 600;

}
```

Die GUI-Klasse:

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

public class GUI extends JFrame {
	
	protected GUI( int ausdehnung) {
		groesse = ausdehnung;
		setTitle("Test123");
		setSize(ausdehnung, ausdehnung/3*2);
		
		Button start = new Button("Start");
		start.setBackground(Color.ORANGE);
		Button load = new Button("Laden");
		load.setBackground(Color.ORANGE);
		Button save = new Button("Speichern");
		save.setBackground(Color.ORANGE);
		Button timeout = new Button("Pause");
		timeout.setBackground(Color.ORANGE);
		
		Panel menuPanel = new Panel();
		menuPanel.setSize(ausdehnung, 20);
		menuPanel.setLayout(new GridLayout(1, 4));
		menuPanel.setBackground(Color.orange);
		
		gamePanel = new Spielfeld(ausdehnung);
		
		menuPanel.add( start);
		menuPanel.add( timeout);
		menuPanel.add( save);
		menuPanel.add( load);
		
		add( menuPanel);
		add( gamePanel);
		
		setVisible(true);
		
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		}
	
	public int getAusdehnung(){
		return groesse;
	}
	
	public Spielfeld getSpielfeld(){
		return gamePanel;
	}
	
	
	
	
	private int groesse;
	private Spielfeld gamePanel; 
}
```

Das Spielfeld nochmal separat(mit KeyListenern und der paint()-Methode):

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

import javax.swing.*;

public class Spielfeld extends Panel {
	
	public Spielfeld(int ausdehnung){
		setBackground(Color.white);
		setSize(ausdehnung, ausdehnung-20);
		addKeyListener(new KeyListener() {
			@Override
			public void keyTyped(KeyEvent e) {
				// TODO Auto-generated method stub
				
			}
			
			@Override
			public void keyReleased(KeyEvent e) {
				// TODO Auto-generated method stub
				
			}
			
			@Override
			public void keyPressed(KeyEvent e) {
				if (e.getKeyCode() == KeyEvent.VK_UP){
					Spiel.getBall().spring();
				}
			}
		});
			

			
			
	}
	
	public void paint(Graphics g){
		Point position = Spiel.getBall().getPosition();
		g.drawOval(position.x, position.y, 5, 5);
	}
	

}
```

und den Ball:

```
import java.awt.Point;


public class Ball {
	
	public Ball(int ausdehnung){
		position = new Point (10, ausdehnung/3*2-100);
	}
	
	public Point gehLinks(){
		position.x -= 2; 
		return position;
	}
	
	public Point gehRechts(){
		position.x += 2; 
		return position;
	}
	
	public Point spring (){
		if (position.y <= Spiel.getGUI().getAusdehnung()/3*2-100){
			position.y +=1;
			if (position.y <= Spiel.getGUI().getAusdehnung()/2){
				position.y +=1;
				if (position.y <= Spiel.getGUI().getAusdehnung()/1.5){
					position.y +=1;
				}
			}
		}
		Spiel.getGUI().getSpielfeld().repaint();
		return position;
	}
	
	public Point fallen(){
		position.y -= 3;
		return position;
	}
	
	public Point getPosition(){
		return position;
	}
	
	
	private Point position;

}
```


Ich hoffe Ihr könnt mir helfen.
Danke im Vorraus für jeden gut gemeinten Rat und jede Kritik.

Gruß
Bob


----------



## Quaxli (2. Dez 2011)

Mal so auf die Schnelle drüber geschaut: Dein Hauptproblem ist eine falsche Herangehensweise. Die Aktualisierung Deines Spiels findet letzten Endes über einen KeyListener statt. Das ist nicht gut.
Üblicherweise erstellt man einen sog. GameLoop also einen eigenen Thread, der das Spiel alle x Sekunden aktualisiert.
Zudem mischt Du AWT und Swing, das kann, muß aber nicht gut gehen. Entscheide Dich für eins von beiden. Dein Spielfeld erbt von Panel, dieses ist von Haus aus schon mal nicht doppelt gepuffert.
Das heißt, wenn eine Animation zustande käme, wäre diese vermutlich nicht schön anzusehen.

Schau Dir erst mal ein Spieletutorial an, um ein Grundkonzept zu haben und schnitze dann was Eigenes.
Wenn ich die Zeit finde, später mehr.


----------



## Quaxli (2. Dez 2011)

So, ich hab' dran rumgefrickelt. So ganz konnte ich nicht nachvollziehen, was die einzelnen Methoden machen sollten. Vor allem die Klasse Ball kam mir irgendwie spanisch vor.
Ich habe als einfach mal eine Funktionaltiät erfunden. Geht ja erst mal nur um was zum Abschreiben. 

Im Folgenden also mal ein Beispiel wie man es machen könnte. Das ist jetzt nur eine Option und nicht das 11. Gebot der Spieleentwicklung... 
Das Ganze ist natürlich nicht schön, man könnte in die Bewegung des hüpfenden Balls viel mehr Logik reinpacken, um einen weichen Ablauf hin zu bekommen, als das abrupte Gehüpfe, daß ich jetzt auf die Schnelle fabriziert habe. Aber es geht hier ja auch nur ums Grundgerüst.

Das Spiel besteht jetzt nur noch aus 2 Klassen und alle Funktionalität die zur Klasse Ball gehört wurde auch dort gekapselt. Die Klasse Ball ist die Einzige, die einigermaßen erhalten geblieben ist. Der Rest wurde gehörtig geändert. 

1. Die Klasse GUI


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

/*
 * Das Menü habe ich weggelassen. Hier erst mal nur die Basics.
 * Die Klasse erbt von JPanel. Damit haben wir schon eine doppelt gepufferte Komponente zum Zeichnen.
 * Für dieses kleine Beispiel hängen wir auch die Interfaces für den Thread und den KeyListener mit rein. 
 */

public class GUI extends JPanel implements Runnable, KeyListener{

	private static final long	serialVersionUID	= 1L;
	
	private Ball ball;  //dein Ball
	private static int	ausdehnung	= 600;
	JFrame frame;  //Das Fenster, in das wir später diese Panel reinpacken
	
	//booleans für die Steuerung
	boolean left;
	boolean right;

	//main-Methode
	public static void main(String[] args) {
		new GUI(ausdehnung);
	}

	//Konstruktor
	protected GUI(int ausdehnung) {
		//Größe des Panels über setPrefferedSize(Dimension d);
    setPreferredSize(new Dimension(ausdehnung,(int)(ausdehnung/3*2)));
    
    //Hier jetzt das Fenster
    frame = new JFrame("Spiel");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(this); //Instanz von GUI (=JPanel) als Spielfläche reinpacken
    frame.addKeyListener(this); //KeyListener dranschrauben
    frame.pack();
    frame.setVisible(true);
    
    ball = new Ball(this); //Ball instanziieren
    
    //GameLoop als eigenen Thread starten - GameLoop ist alles was unten in der Run-Methode steht
    Thread th = new Thread(this);
    th.start();
	}

	@Override
	public void run() {

		//Solange immer wieder alles ausführen, wie das Fenster sichtbar ist
		while(frame.isVisible()){
			
			//Steuerungsabfrage: links oder rechts gesetzt?
			if(left){
				ball.gehLinks();
			}
			
			if(right){
				ball.gehRechts();
			}
			
			//Hüpf Bällchen hüpf ;-)
			ball.spring(); //hierdrin steht ALLES was mit Bewegung zu tun hat
			
      //Kleines Päuschen, damit auch andere (Prozesse) zu ihrem Recht kommen.
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
			//Zeichen die GUI(JPanel) neu
			repaint();			
		}
		

		
	}

	/*
	 * Hier und nur hier wird ALLES gezeichnet
	 * in Swing IMMER paintComponent überschreiben - niemals paint
	 */
	public void paintComponent(Graphics g){
		super.paintComponent(g); //super-Aufruf ist wichtig. Nicht vergessen.
		ball.malDenBall(g); //Graphics-Objekt an die Zeichen-Methode von Ball übergeben
	}

	
	/*
	 * in die KeyListener keine Logik und keine Methoden-Aufrufe
	 * nur prüfen, ob eine der Pfeiltasten gedrückt wurde und ggf. 
	 * die boolean-Werte ändern
	 * MEHR NICHT!!!
	 */
	@Override
	public void keyPressed(KeyEvent e) {
		if(e.getKeyCode()==KeyEvent.VK_LEFT){
			left = true;
		}
		
		if(e.getKeyCode()==KeyEvent.VK_RIGHT){
			right = true;
		}
	}

	@Override
	public void keyReleased(KeyEvent e) {
		if(e.getKeyCode()==KeyEvent.VK_LEFT){
			left = false;
		}
		
		if(e.getKeyCode()==KeyEvent.VK_RIGHT){
			right = false;
		}
	}

	@Override
	public void keyTyped(KeyEvent e) {
		
	}
	
}
```

2. Die Klasse Ball


```
import java.awt.*;

/*
 * Ball erbt von Rectangle weil's schön bequem ist und
 * uns ein paar Variablen beschert :-)
 * Damit haben wir schon mal x, y, Breite und Höhe
 */
public class Ball extends Rectangle{

	private static final long	serialVersionUID	= 1L;
	GUI parent;  //Referenz auf das JPanel, in dem gezeichnet wird
	boolean faellt = true; //Steuer-Boolean für steigen oder fallen
	int maxHoehe; //kleine Hilfsvariable
	
	//Konstruktor - bekommt nur die Referenz auf die Zeichenkomponente übergeben
	public Ball(GUI gui) {
		super(10, gui.getHeight()-50,10,10); //super-Aufruf, um den Konstruktor ovn Rectangle zu versorgen
		parent = gui; //Referenz setzen
		maxHoehe = parent.getHeight()/2;
	}

	//ohne Rückgabewert!
	public void gehLinks() {
		x -= 2;
	}

	//ohne Rückgabewert
	public void gehRechts() {
		x += 2;
	}

	/*
	 * Methode ebenfalls void gesetzt
	 * Hier wird alles was nicht vom Spieler beeinflußt werden soll gesteuert
	 * Hier zum Beispiel das Springen des Balls
	 */
	public void spring() {
		
		//Je nachdem ob der Ball gerade steigt oder fällt entsprechende Logik verwenden
		if(faellt){
			fallen();
			
			if(y+height>parent.getHeight()){
				y = parent.getHeight() - height;
				faellt = false;
			}
		}else{
			steigen();
			
			if(y+height<maxHoehe){
				y = maxHoehe - height;
				faellt = true;
			}
		}
		
		//nicht nach links raus hüpfen
		if(x<=0){
			x =0;
		}
		
		//nicht nach rechts raus hüpfen
		if(x+width>=parent.getWidth()){
			x = parent.getWidth()-width;
		}
		
	}
	
	//Ball nach oben
	public void steigen(){
		y -= 3;
	}

	//Ball nach unten (war vorher falsch)
	public void fallen() {
		y += 3;
	}

	//Graphics-Objekt vom JPanel verwenden um den Ball zu malen
	public void malDenBall(Graphics g){
		g.setColor(Color.RED);
		g.fillOval(x, y, width, height); //Verwendung der von Rectangle geerbten Koordinaten
	}

}
```

Viel Spaß beim Nachvollziehen.


----------



## bob_sheknowdas (2. Dez 2011)

Nice, dankeschön


----------

