# Applet startet, aber führt den Thread nicht aus



## bernii (9. Feb 2010)

Hi, ich komme hier einfach nicht weiter. Es geht um ein Tischtennisspiel, und einen Ball, der von einem Schläger abprallen soll. Vorab, es gibt keine Syntaxfehler. Es klappte alles bestens.

Da ich nicht weiß woran es liegt, weiß ich auch nicht genau welche Code-Stellen ich euch geben soll.

Also wenn im folgenden Code, wo der Ball vom "Player"-Schläger (dem rechten) abprallt. Es klappt alles bestens.


```
//Player-Abprall
            if(x_pos+radius > player.x_pos && x_pos < player.x_pos+player.breite && x_speed > 0)
            {
                if(y_pos > player.y_pos && y_pos < player.y_pos + player.hoehe/3)
                {
                  x_speed *= -1;
                  y_speed = -0.5; 
                }
                if(y_pos > player.y_pos+(player.hoehe/3) && y_pos < player.y_pos+(player.hoehe-(player.hoehe/3)))
                {
                    x_speed *= -1;
                    y_speed = 0.0;
                }
                if(y_pos > player.y_pos+(player.hoehe-(player.hoehe/3)) && y_pos < player.y_pos + player.hoehe)
                {
                    x_speed *= -1;
                    y_speed = 0.5;
                }
            }
```

Und wenn ich den nun folgenden Code einfüge und das Applet im Appletviewer starte, werden zwar alle Objekte gezeichnet und sie bewegen sich für eine geschätzte Milisekunde auch, doch dann friert alles ein und nichts bewegt sich mehr.



```
//Cpu-Abprall
             if(x_pos < cpu.x_pos + cpu.breite)
            {
                if(y_pos > cpu.y_pos && y_pos < cpu.y_pos + cpu.hoehe)
                {
                    x_speed = -2;
                }
            }
```

Das Komische ist, dass die Klasse Player und CPU gleich aufgebaut sind und überall die gleichen Refferenzen sind undsoweiter.

Wäre super, wenn mir jemand helfen könnte!
Danke!


----------



## Quaxli (10. Feb 2010)

Da wirst Du nicht umhinkommen, mehr Code zu posten. Die paar if-Bedingunen helfen da nicht weiter.
Poste zumindest mal den Teil, wo die Bewegung ausgeführt wird. Besser noch wäre natürlich ein vollständiges, kompilierbares Beispiel.


----------



## Steev (10. Feb 2010)

Wie zeichnest du dein Applet? Das hört sich nämlich an als ob das Applet nicht wieder neu gezeichnet würde. Hast du eine repaint-Schleifer bzw. Thread oder überschreibst du einfach die nur die paint-Methode?

PS:
Du solltest für den Ball usw. eine eigene Klasse machen. Auserdem ist es sehr unschön, wenn man die Attribute direkt anspricht (-> Geheimnisprinzip) normalerweise macht man da verschiedene Getters und Setters.

Gruß
Steev


----------



## bernii (10. Feb 2010)

Hi, ja ich zeichne das Applet mit repaint() immer neu. Es funktioniert nur nicht, wenn ich in der Ball-Klasse auf Attribute von der CPU-Klasse verweise (z.B.: cpu.x_pos).
Für Den Ball, den CPU, den Player und Main habe ich aber auch eigene Klassen.


```
public void run ()
    {
        while (true)
        {
            bewegen();
            ball.move();
            cpu.bewegen();
            repaint();

            try
            {
                // Stoppen des Threads für 15 Millisekunden
                Thread.sleep (15);
            }
            catch (InterruptedException ex)
            {
                // do nothing
            }
        }
    }
```


----------



## Steev (10. Feb 2010)

Hi bernii,

kannst du vieleicht etwas mehr (oder allen) Code posten.
Ich finde deinen Code etwas unschön, ich würde das ganze etwas anders machen.
1. Für jedes Objekt (in abstratem Sinne) eine eigene Klasse erstellen (-> Ball, Platte, Spieler, Spiel, Spielfläche, Menü, etc. ...)
2. Eine ordentliche Spiellogik: (-> Renderung und Logik trennen, Spielereignisse erstellen etc.)

Zu deinem Code oben. Vieleicht ist das so etwas schöner: (Nur mal als Grobkonzept)

*Game*
[Java]private transient LinkedList<Gameobject> gameobjects = new LinkedList<Gameobject>();

public void run () {
  while (true) {
    doLogic();
    repaint();
    try {
      Thread.sleep(60);
    } catch (Exception e) {
      e.printStackTrace();
    }
}

public synchronized void doLogic() {
  // Iteriere durch alle Spielobjekte und führe die Logik-Methoden aus
  if (gameobjects != null && !gameobjects.isEmpty())
    for (Gameobject obj : gameobjects)
      if (obj != null)
        obj.doLogic();
}

public void paint(Graphics g) {
  // TODO

  // Iteriere durch alle Spielobjekte und rendere sie
  if (gameobjects != null && !gameobjects.isEmpty())
    for (Gameobject obj : gameobjects)
      if (obj != null)
        obj.render(g);
}

public boolean addGameobject(Gameobject obj) {
  return gameobjects.add(obj);
}

public LinkedList<Gameobject> getGameobjects() {
  return gameobjects;
}[/Java]

*Gameobject*
[Java]public abstract class Gameobject {
  public abstract void doLogic(Game game);
  public abstract void handleEvent(Game game /* , ... */); // Irgendwelche Event-Parameter.
  public abstract void render(Game game, Graphics g);
}[/Java]

*Ball*
[Java]public class Ball extends Gameobject {
  private double x, y, z;
  private double x_speed, y_speed, z_speed;
  private double raddius;

  public Ball(double x, double y, double z, double radius) {
    // TODO 
  }

  public synchronized void doLogic(Game game) {
    // TODO Gravitation

    // Ballbewegung
    x += x_speed;
    y += y_speed;
    z += z_speed;

    testCollission(Game game);
  }

  protected synchronized void testCollission(Game game) {
    Plate plate = game.getPlate();
    if (plate == null) return;

    // Aufspringen
    if (z <= plate.getZ() && plate.contains(this)) {
      z_speed = -z_speed; // TODO Weniger Geschwindigkeit durch das Aufspringen
      // Wenn der Ball auf der Platte aufspringt, so teile dies der Platte mit...
      game.handlePlateEvent(plate, this, plate.getContainsSide(this));
    }
  }

  public void handleEvent(Game game) {
    // TODO mit Leben füllen...
  }

  public abstract void render(Game game, Graphics g) {
    int shy = (int) (y - z); // ;-)
    double zradius = radius + z / 10.;

    // Schatten
    g.setColor(Color.BLACK);
    g.fillOval((int) (x - radius / 2.), (int) (shy - radius / 2.), (int) radius, (int) radius);

    // Ball
    g.setColor(Color.WHITE);
    g.fillOval((int) (x - zradius / 2.), (int) (y - zradius / 2.), (int) zradius, (int) zradius);
  }

  // TODO Getters und Setters usw.
}[/Java]

PS: Threads muss man auch starten und erstellen, die Implementierung von Runnable alleine ist nur die halbe Miete. (-> new Thread(yourRunnable).start() )

So, jetzt muss ich aber mal Frühstücken ;-)

Gruß
Steev


----------



## bernii (10. Feb 2010)

Hi, vielen Dank schonmal für deine Mühe, ich muss aber gestehen, dass ich einiges an deinem Konzept nicht verstehe (also von den "Vokabeln" her ), ich werde es mal durcharbeiten.

Ich poste mal meinen gesamten Code:

Main:


```
import java.awt.*;
import java.util.*;
import java.applet.*;
import java.net.*;
import java.awt.event.*;

public class Main extends Applet implements Runnable
{
    // Deklarationen der Variablen
    public Player player;           
    public Ball ball;       
    public CPU cpu;
    public Platte platte;
    
    // Thread
    Thread th;                      // Thread in dem das Spiel läuft

    // Init - Methode
    public void init ()
    {
        // Neue Hintergrundfarbe
        Color superblue = new Color (0, 0, 255);

        setBackground (Color.black);

        // Initialisierung der Spielobjekte
        player = new Player (420, 175, ball);
        ball = new Ball (10, 190, 250, 3, -1, Color.red, player, cpu);
        cpu = new CPU (70, 200, ball);
        platte = new Platte();
    }


    // Start - Methode, hier beginnt das Applet zu laufen
    public void start ()
    {
        // Schaffen eines neuen Threads, in dem das Spiel läuft
        th = new Thread (this);
        th.start ();
    }

    public void stop ()
    {
    }

    // Implementierung der Runmethode
    public void run ()
    {
        while (true)
        {
            bewegen();
            ball.move();
            cpu.bewegen();
            repaint();

            try
            {
                // Stoppen des Threads für 15 Millisekunden
                Thread.sleep (15);
            }
            catch (InterruptedException ex)
            {
                // do nothing
            }
        }
    }
    
    public void bewegen() //Dies gehört eigendlich in die Player-Klasse, aber dort funktioniert es nicht
    {
        addMouseMotionListener(new MouseMotionAdapter()
         {
             public void mouseMoved(MouseEvent em)
             {
                 player.y_pos = em.getY();
                 player.x_pos = em.getX();
             } 
         });
    }

    // Paint - Methode
    public void paint (Graphics g)
    {
            g.setColor (Color.yellow);
            player.paint(g);
            ball.DrawBall(g);
            cpu.paint(g);
            platte.zeichnePlatte(g);
}
}
```

Ball:

```
import java.applet.*;
import java.awt.*;
import java.util.*;
import java.net.*;

public class Ball
{
    // Deklaration der Variablen
     double x_pos;              // Variable für die X - Position des Balles
     double y_pos;              // Variable für die Y - Position des Balles
     double z_pos;
     
     double y_speed = 0.1; 
     int x_speed = 2; 
     double z_speed = -0.1;
    
     double radius;                // Radius des Balles

     int first_x;           // Start x - Position
     int first_y;           // Start y - Position

    // Farbe des Balles
    Color color;

    CPU cpu;
    Player player;

    // Konstruktor
    public Ball (int radius, int x, int y, int vx, int vy, Color color, Player player, CPU cpu)
    {
        // Initialisierung der Variablen
        this.radius = radius;

        x_pos = x;
        y_pos = y;

        first_x = x;
        first_y = y;

        x_speed = vx;
        y_speed = vy;

        this.color = color;
        this.cpu = cpu;
        this.player = player;

    }

    // Move - Methode, berechnet die Bewegung des Balls
    public void move ()
    {
        x_pos += x_speed;
        y_pos += y_speed;
        radius += z_speed;
        
        //Player-Abprall
            if(x_pos+radius > player.x_pos && x_pos < player.x_pos+player.breite && x_speed > 0)
            {
                if(y_pos > player.y_pos && y_pos < player.y_pos + player.hoehe/3)
                {
                  x_speed *= -1;
                  y_speed = -0.5; 
                }
                if(y_pos > player.y_pos+(player.hoehe/3) && y_pos < player.y_pos+(player.hoehe-(player.hoehe/3)))
                {
                    x_speed *= -1;
                    y_speed = 0.0;
                }
                if(y_pos > player.y_pos+(player.hoehe-(player.hoehe/3)) && y_pos < player.y_pos + player.hoehe)
                {
                    x_speed *= -1;
                    y_speed = 0.5;
                }
            }
            
            //Cpu-Abprall
             if(x_pos < cpu.get_x_pos() + cpu.breite)
            {
                if(y_pos > cpu.get_y_pos() && y_pos < cpu.get_y_pos() + cpu.hoehe)
                {
                    x_speed = -2;
                }
            }
            
           if(x_pos < 100)
           {
               x_speed *= -1;
            }
            //Springen
            if(radius < 5)
            {
                z_speed *= -1;
            }
            if(radius > 11)
            {
                z_speed *= -1;
            }
     }
     
    double get_y_pos()
    {
        return y_pos;
    }
    
    double get_x_pos()
    {
        return x_pos;
    }
    
    int get_x_speed()
    {
        return x_speed;
    }
    
    // Diese Methode zeichnet den Ball in das Spielfeld
    public void DrawBall (Graphics g)
    {
        g.setColor (color);
       // g.fillOval((int) x_pos, (int) y_pos, (int) radius, (int) radius);
       g.fillOval((int) (x_pos), (int) (y_pos), (int) radius, (int) radius);
    }

}
```

CPU:

```
import java.awt.*;

public class CPU 
{
    int x_pos_first; 
    int y_pos_first;
    int  x_pos;
    int  y_pos;
    int hoehe = 50; 
    int breite = 5;
    int x_speed;
    int y_speed;
    int y_pos_mitte = y_pos + (hoehe/2);

    
    Ball ball;
    
    public CPU(int x, int y, Ball ball)
    {
        x_pos = x;
        y_pos = y;
        
        x_pos_first = x;
        y_pos_first = y;
        
        this.ball = ball;
    }
    
   public void bewegen()
    {   
        if(ball.get_x_speed() < 0)
        {  
            if(y_pos > ball.get_y_pos())
            {
                y_speed = -2;
            }
            if(y_pos < ball.get_y_pos())
            {
                y_speed = 2;
            }
            if(y_pos == ball.get_y_pos())
            {
                y_speed = 0;
            }
            
            
            if(x_pos < ball.get_x_pos())
            {
                x_speed = 2;
            }
            if(x_pos > ball.get_x_pos())
            {
                x_speed = -2;
            }
            if(x_pos == ball.get_x_pos())
            {
                x_speed = 0;
            }
        }
        
        if(ball.x_speed > 0)
        { 
            if(y_pos > 200)
            {
                y_speed = -2;
            }
            if(y_pos < 200)
            {
                y_speed = 2;
            }
            
            if(x_pos > 70)
            {
                x_speed = -2;
            }
            if(x_pos < 70)
            {
                x_speed = 2;
            }
        }
        
        y_pos += y_speed;
        x_pos += x_speed;
    }
    
    int get_x_pos()
    {
        return x_pos;
    }
    
     int get_y_pos()
    {
        return y_pos;
    }
    
    
    public void paint(Graphics g)
    {
        g.setColor(Color.green);
        g.fillRect(x_pos, y_pos, breite, hoehe);
    }
}
```

Player:

```
import java.awt.*;

public class Player 
{

    int x_pos; 
    int y_pos; 
    int hoehe = 50; 
    int breite = 5;
    int x_pos_mitte = x_pos+breite/2;
    int y_pos_mitte = y_pos+hoehe/2;
    int x_pos_first;
    int y_pos_first;
    Ball ball;
    CPU cpu;
    
    public Player(int x, int y, Ball ball)
    {
        x_pos = x;
        y_pos = y;
        
        x_pos_first = x;
        y_pos_first = y;
        
        this.ball = ball;
    }
  
           
   public void paint(Graphics g)
    {
        g.setColor(Color.yellow);
        g.fillRect(x_pos, y_pos, breite, hoehe);
    }
}
```

Und dann noch die Platte:

```
import java.awt.*;

public class Platte
{
    public Platte()
    {
        
    }
    
    
    public void zeichnePlatte(Graphics g)
    {
            g.setColor(Color.white);
            g.drawRect(100,100,400,200);
            g.drawLine(300,100,300,300);
            g.drawLine(100,200,500,200);
    }
}
```

Ich nehm alle Vorschläge, Ideen zu Herzen


----------



## Quaxli (10. Feb 2010)

Ich hab' mal dran rumgefrickelt und ein bißchen was schlanker gemacht. Und das Bild flackert jetzt nicht mehr so.  
Es ist aber noch vieles zu tun und teilweise auch nicht schön - u. a. der Zugriff auf Instanz-Variablen.
Dazu der Bequemlichkeit halber alles in einer Datei. 
Das Springen des Balls hab' ich jetzt auch nicht mehr angepaßt.

Aber Du erkennst hoffentlich trotzdem ein paar grundlegegen Konzepte.

Schau's Dir halt mal an:


```
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Rectangle2D;
import javax.swing.JApplet;
import javax.swing.JPanel;


//JApplet, damit wir ein JPanel reinpacken können
public class Main extends JApplet{

	private static final long	serialVersionUID	= 1L;
	
	public void init(){
		setSize(800,600);
		add(new GamePanel());
	}
	
}

//JPanel - doppelt gepufferte Komponente als Basis = kein Flackern
//Hier auch MouseMotionListener dran hängen
class GamePanel extends JPanel implements Runnable, MouseMotionListener {
	
	private static final long	serialVersionUID	= 1L;
	
	// Deklarationen der Variablen
	public Player	player;
	public Ball		ball;
	public CPU		cpu;
	
	//Variablen zur Errechnung der Zeit pro Schleifendurchlauf
	long delta = 0;
	long last = 0;


	// Init - Methode
	public GamePanel() {
		
		setBackground(Color.BLACK);
    setSize(new Dimension(800,600));
    addMouseMotionListener(this);
    
		player = new Player(520, 175, this);
		ball = new Ball(10, 250, 175, 160, 0, Color.red, this);
		cpu = new CPU(70, 175, this);
		
		last = System.nanoTime();
		
		// Schaffen eines neuen Threads, in dem das Spiel läuft
		Thread th = new Thread(this);
		th.start();
	}



	// Implementierung der Runmethode
	public void run() {
		while (true) {
			computeDelta();  //Zeit des letzten Schleifendurchlaufs
			moveAll(delta);  //alles Bewegen in Abhängigkeit vom letzten Schleifendurchlauf
			doLogic(delta);  //Logiken
			repaint();       //neu pinseln

			try {
				Thread.sleep(10); //Päuschen
			} catch (InterruptedException ex) {}
		}
	}
	
	private void doLogic(long delta){
		ball.doLogic(delta);
		
		//Kollision - Methoden von Rectangle nutzen!
		if(ball.intersects(player)){
			ball.collide(player.x);
		}
		
		if(ball.intersects(cpu)){
			ball.collide(cpu.x+cpu.width);
		}
	}
	
	//NanoTime für ausreichend hohe Auflösung der Zeit
	private void computeDelta(){
		delta = System.nanoTime() - last;
		last = System.nanoTime();
	}

	private void moveAll(long delta){
		ball.move(delta);
		cpu.move(delta);
	}

	
	// Paint - Methode
	public void paint(Graphics g) {
		super.paint(g); //super-Aufruf nicht vergessen
		
		g.setColor(Color.WHITE);
		g.drawRect(100, 100, 400, 200);
		g.drawLine(300, 100, 300, 300);
		g.drawLine(100, 200, 500, 200);
		
		g.setColor(Color.yellow);
		player.paint(g);
		ball.DrawBall(g);
		cpu.paint(g);
	}



	public void mouseDragged(MouseEvent e) {
		// TODO Auto-generated method stub
		
	}



	public void mouseMoved(MouseEvent e) {
		player.y = e.getY();	//Y-Wert des Schlägers anpassen
	}
}

//Alle Klassen erben von Rectangle
class Ball extends Rectangle2D.Double{

	private static final long	serialVersionUID	= 1L;
	double	y_speed	= 0;
	int			x_speed	= 0;
	double	z_speed	= -1;
  int radius;

	// Farbe des Balles
	Color		color;
	GamePanel parent;
	long divisor = (long) 1e9;
	
	// Konstruktor
	public Ball(int radius, int x, int y, int vx, int vy, Color color, GamePanel m) {
    super(x,y,radius*2,radius*2); //super-Aufruf um Rectanlge zu versorgen
    
    parent = m;
		x_speed = vx;
		y_speed = vy;
		
		this.radius = radius;

	}

	//Ballbewegung umkehren - momentan nur x-Werte
	public void collide(double xval) {
		if(x_speed>0){
	    x = xval - width;
		}else{
			x = xval;
		}
    x_speed *= -1;
	}

	// Move - Methode, berechnet die Bewegung des Balls
	// In Abhängigkeit der Zeit des letzten schleifdurchlaufs = gleichförmigere Bewegung
	public void move(long delta) {
		
    if(x_speed!=0){
    	double div = (double)delta/(double)divisor;
    	div *= x_speed;
      x += div;
    }
    
    if(y_speed!=0){
    	double div = (double)delta/(double)divisor;
    	div *= y_speed;
      y += div;
    }
    
		radius += z_speed;
	}
	
	
	//Logikprüfungen des Balls
	public void doLogic(long delta){

		// Springen
		if (radius < 5) {
			z_speed *= -1;
		}
		
		if (radius > 11) {
			z_speed *= -1;
		}
	}

	int get_x_speed() {
		return x_speed;
	}

	// Diese Methode zeichnet den Ball in das Spielfeld
	public void DrawBall(Graphics g) {
		g.setColor(color);
		g.fillOval((int) x, (int) y, (int) radius*2, (int) radius*2);
	}

}


// Das Gleiche hier. Ein bißchen Logik fehlt noch
class CPU extends Rectangle2D.Double{

	private static final long	serialVersionUID	= 1L;
	long divisor = (long) 1e9;
  GamePanel parent;	
  double x_speed = 0;
  double y_speed = 0;
	
	public CPU(int x, int y, GamePanel m) {
		super(x,y,5,50);
		parent = m;
	}

	public void move(long delta) {
		
    if(x_speed!=0){
    	double div = (double)delta/(double)divisor;
    	div *= x_speed;
      x += div;
    }
    
    if(y_speed!=0){
    	double div = (double)delta/(double)divisor;
    	div *= y_speed;
      y += div;
    }
	}

	public void paint(Graphics g) {
		g.setColor(Color.green);
		g.fillRect((int)x, (int)y, (int)width,(int) height);
	}
}

class Player extends Rectangle{


	private static final long	serialVersionUID	= 1L;
	
	GamePanel parent;
	
	public Player(int x, int y, GamePanel main) {
		super(x,y,5,50);
		parent = main;
	}

	public void paint(Graphics g) {
		g.setColor(Color.yellow);
		g.fillRect((int)x,(int)y,(int)width,(int)height);
	}
}
```

Viel Spaß damit


----------



## Quaxli (11. Feb 2010)

Ich habe noch ein blöden Fehler gefunden, den ich eingebaut habe ;( : Nachdem das Beispiel oben ein JPanel als doppelt gepufferte Komponente verwendet, darf natürlich nicht paint überschrieben werden, sondern paintComponent, wie bei Swing üblich. Ich hatte das beim Ändern übersehen. 
Das entsprechende Stück Code sollte also so aussehen:


```
public void paintComponent(Graphics g) {
		super.paintComponent(g); //super-Aufruf nicht vergessen
		
		g.setColor(Color.WHITE);
		g.drawRect(100, 100, 400, 200);
		g.drawLine(300, 100, 300, 300);
		g.drawLine(100, 200, 500, 200);
		
		g.setColor(Color.YELLOW);
		player.paint(g);
		ball.DrawBall(g);
		cpu.paint(g);
	}
```


----------

