# Anfänger Spieletest



## michi2 (29. Apr 2006)

Da ich in sachen Spiele Programmierung ein *absoluter Anfänger* bin und kein ordenliches Tutorial zur Programmierung von Applikationsspielen gefungen hab hab ich mich einfach mit der API hingesetzt und mal darauf losgeschrieben, und das ist das (bescheidene) Resultat (es ist nur ein Test zur Steuerung):

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

public class Spieltest extends JFrame
   {
   Spielflaeche gamePanel;
   MoveFig mv;
  
   class CMeinWindowLauscher extends WindowAdapter
      {
      public void windowClosing(WindowEvent e)
         {
         System.exit(0);
         }
      }
   
   class MoveFig extends Thread
      {
      int newx;
      int newy;
      int relx;
      int rely;
      MoveFig(int x, int y)
         {
         relx = x - gamePanel.figx;
         rely = y - gamePanel.figy;
         newx = x;
         newy = y;
         }
      MoveFig(int x, int y, boolean absolut)
         {
         if(absolut)
            {
            relx = x - gamePanel.figx;
            rely = y - gamePanel.figy;
            newx = x;
            newy = y;
            }
         if(!absolut)
            {
            relx = x;
            rely = y;
            newx = gamePanel.figx + x;
            newy = gamePanel.figy + y;
            }
         }
      
      public void run()
         {
         System.out.println(relx);
         System.out.println(rely);
         int xadd = 0;
         int yadd = 0;
         int betragx = relx;
         int betragy = rely;
         if(relx<0)
            betragx = -1 * relx;
         if(rely<0)
            betragy = -1 * rely;
         if(betragy>betragx && betragx!=0)
            {
            yadd= rely/relx;
            if(yadd<0)
               yadd = -1*yadd;
            }
         if(betragx>betragy && betragx!=0)
            {
            xadd = rely/relx;		//### Ineffizient!!!
            if(xadd<0)
               xadd = -1*xadd;
            }
         int j = gamePanel.figy;
         int i = gamePanel.figx;
         while(i!=newx || j!=newy)
            {
            System.out.println("relx: "+relx+"rely: "+rely);
            System.out.println("xadd: " + xadd);
            System.out.println("yadd: " + yadd);
            
            if(i<newx){
               if(i+ xadd +1>newx)
                  i= i+ 1;
               else
               i= i+ xadd +1;
               System.out.println("i+xadd: " +i);}
            
            if(j<newy){
               if(j + yadd +1>newy)
                  j = j + 1;
               else
               j = j + yadd +1;
               System.out.println("j+yadd: " +j);}
            
            if(i>newx){
               if(i - xadd -1<newx)
                  i= i  -1;
               else
               i= i - xadd -1;
               System.out.println("i-xadd: " +i);}
            
            if(j>newy){
               if(j - yadd -1<newy)
                  j = j  -1;
               else
               j = j - yadd -1;
               System.out.println("j-yadd: " +j);}            
            System.out.println("2: i: "+i+"j: "+j);            
            gamePanel.paintback();
            gamePanel.paintfigur((int) i, j);
            gamePanel.repaint();
            try
               {
               Thread.sleep(15);
               }
            catch(java.lang.InterruptedException IEx){System.out.println("Fehler!");}
            }
         }
      }
   
   class MeinMouseLauscher implements MouseListener
      {
      public void mouseClicked(MouseEvent e)
         {
         // einmal piepen
         //java.awt.Toolkit.getDefaultToolkit().beep();
         mv = new MoveFig(e.getX()-20,e.getY()-20);
         mv.start();
         }
      public void mouseEntered(MouseEvent e) {
         }
      public void mouseExited(MouseEvent e) {
         }
      public void mousePressed(MouseEvent e) {
         }
      public void mouseReleased(MouseEvent e) {
         }
      }

   
   class MyKeyLauscher implements KeyListener
      {
      public void keyTyped(KeyEvent k)
         {
         System.out.println("key:");
         System.out.println(k);
         int relx = 0;
         int rely = 0;
         switch(k.getKeyChar())
            {
            case 'w':
            rely = -10;
            break;
            case 's':
            rely = 10;
            break;
            case 'a':
            relx = -10;
            break;
            case 'd':
            relx = 10;
            break;
            
            case 'W':
            rely = -10;
            break;
            case 'S':
            rely = 10;
            break;
            case 'A':
            relx = -10;
            break;
            case 'D':
            relx = 10;
            break;
            default:
            break;
            }
         mv = new MoveFig(relx,rely,false);
         mv.start();
         }
      public void keyPressed(KeyEvent k)
         {
         //System.out.println("key:");
         //System.out.println(k);
         }
      public void keyReleased(KeyEvent k)
         {
         //System.out.println("key:");
         //System.out.println(k);
         }
      }//Ende KeyLauscher
   
  
   Spieltest(String titel)
      {
      super(titel);
      addWindowListener(new CMeinWindowLauscher());
      getContentPane().setLayout(new BorderLayout());
      gamePanel = new Spielflaeche();
      getContentPane().add(gamePanel);
      gamePanel.paintback();
      gamePanel.paintfigur(0,0);
      gamePanel.repaint();
      gamePanel.setFocusable(true);
      gamePanel.addKeyListener(new MyKeyLauscher());
      gamePanel.addMouseListener(new MeinMouseLauscher());
      }
   
   public static void main(String[] args)
      {
      Spieltest Fenster = new Spieltest("Spieltest");
      
      Fenster.setSize(300,300);
      Fenster.pack();
      Fenster.show();
      }   
   }//Ende Spieltest

class Spielflaeche extends JPanel
   {
   private Image image = new BufferedImage(450, 350, BufferedImage.TYPE_INT_RGB);
   int figx = 0;
   int figy =0;
   public void update(Graphics g)
      {
      System.out.println("update!");
      paint(g);
      }
   Spielflaeche()
      {
      setBackground(Color.black);
      repaint();
      }
   
   public void paintback()	//hier wird das Hintergrund gezeichnet!
      {
      Graphics g = image.getGraphics();
      g.drawImage(loadImage("back.jpg"),0,0,this);
      }
   public void paintfigur(int x, int y)	//hier wird eine Figur gezeichnet!
      {
      Graphics g = image.getGraphics();
      figx= x;
      figy =y;
      g.drawImage(loadImage("fig.gif"), x, y, this);
      }
   public void paint(Graphics g)  // Die wichtigste Methode: hier wird gezeichnet!
      {
      g.drawImage(image,0,0,this);
      }

   
   public Image loadImage(String filename)
      {
      Toolkit toolkit = Toolkit.getDefaultToolkit();
      MediaTracker ladekontrolle = new MediaTracker(this);
      // Bild laden und beim MediaTracker registrieren
      Image Bild = toolkit.getImage(filename);
      ladekontrolle.addImage(Bild,0);
      // Solange warten, bis das Bild ganz geladen ist
      try
         {
         ladekontrolle.waitForID(0);
         }
      catch(InterruptedException e)
         {
         // Das Laden ist fehlgeschlagen
         System.out.println("Fehler!");
         return null;
         }
      return Bild;
      }
   
   // Diese Methode liefert die minimale Größe der Canvas
   public Dimension getMinimumSize()
      {
      return new Dimension(400,300);
      }
   // Die Lieblingsgröße setzen wir auf die Minimalgröße
   public Dimension getPreferredSize()
      {
      return getMinimumSize();
      }

   }
```
Welche Ratschläge, Tipps, Links,... könnt ihr mir geben???


----------



## Redfrettchen (30. Apr 2006)

Hi,
(nachfolgendes stellt nur meine Meinung dar und sollte nicht als Weltformel angesehen werden)
1. Die Klammereinrückung ist etwas gewöhnungsbedürftig. Wenn schon eher im Allman-Stil, dann doch lieber die Klammer auf der Höhe der Anweisung. Und Kontrollstrukturen ruhig ein Leerzeichen mehr gönnen.

```
public static void main(String[] args)
{
    for (int i=0; i<args.length; i++)
    {
        System.out.println(args[i]);
    }
}
```
Vor allem Achtung beim Weglassen von Klammern!

```
if(i<newx){ 
               if(i+ xadd +1>newx) 
                  i= i+ 1; 
               else 
               i= i+ xadd +1; 
               System.out.println("i+xadd: " +i);} // es sieht zwar so aus, als würde der Befehl nur im else-Zweig ausgeführt, aber der Befehl wird unabhängig vom inneren if-Konstrukt ausgeführt
```
2. Für einen Test ist die Struktur akzeptabel, aber sobald es konkret wird, sollte man größeren Klassen eine eigene Datei spendieren. Auch anonyme innere Klassen sind nützlich, falls ein Listener nur wenig Code enthält, z.B.:

```
addWindowListener(
    new WindowAdapter()
    {
        public void windowClosing(WindowEvent e)
        {
            System.exit(0);
        }
    }
);
```
3. Du solltest den Zugriff auf Membervariablen und Methoden mit Modifiern einschränken. Momentan sind viele default-sichtbar, aber warum sollte jemand außerhalb auf x und y-Koordinaten schreibend zugreifen dürfen (siehe hierzu auch Warum Instanzvariablen private deklarieren).
4. Statt von Thread zu erben kannst du auch Runnable implementieren. Abgesehen davon versteh ich nicht, warum du immer wieder einen Thread erzeugst um (vermutlich) irgendetwas zu bewegen. Die ganze Struktur ist irgendwie komisch.
Also so wie ich das verstanden habe, kann man mit wasd ein Bild bewegen. Aber dieser Umweg über ein Thread ist mir nicht verständlich. Wie wärs mit einer Klasse, von der ein Objekt gehalten wird, das beim Tastendrücken seine Variablenwerte verändert?
5. Betragsfunktion wurde schon implementier: Math.abs(double). Damit sparst du dir die ganze Betragsvariablen.
6. Ich persönlich bevorzuge die Benutzung von getKeyCode() anstatt getKeyChar() und der damit verbundene switch Anweisung mit den KeyEven.VK-Konstanten.
7. Bei Swing sollte man statt paint(Graphics) lieger paintComponent(Graphics) überschreiben.


----------



## michi2 (30. Apr 2006)

1.Weiß nicht was der Allman still ist, das andere stimmt, mach ich normalerweiße nicht!
3.Werd ich machen, bringt das auch Performance?
4.Macht das nen Unterschied
4 1/2. Die bewegung soll durch WASD und/oder durch kliken mit derMaus gesteuert werden, dafür braucht man meines wissens den thread?!
5.Danke für den Tipp, hatte ich in der API nicht gefunden
6.werds mir überlegen
7.kann ich machen...


----------



## Redfrettchen (30. Apr 2006)

Mit Allman-Stil ist gemeint, dass du die öffnende Klammer auf die nächste Zeile und nicht wie SUN es vorschlägt am Ende der letzten Codezeile setzt.

```
public void methode()
{
    // ...
}
```
Das wäre eben Allman-Stil.

Man sollte nicht immer nur nach Performance gieren, es geht dabei eher um den objektorientierten Aspekt des Geheimnisprinzips und so weiter. Ob es jetzt Performance bringt kann ich nicht sagen, aber ich glaube nicht, dass es dadurch langsamer wird.

Du brauchst dazu eigentlich keinen Thread. Warum machst du es nicht so, wie ich vorgeschlagen habe:

```
public class Bild {
    // Koordinaten
    private int x, y;

    private final Image image;

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public int getImage() {
        return image;
    }

    public Bild(String filename) {
        this(filename,0,0);
    }

    public Bild(String filename, int x, int y) {
        Toolkit toolkit = Toolkit.getDefaultToolkit(); 
        MediaTracker ladekontrolle = new MediaTracker(this); 
        // Bild laden und beim MediaTracker registrieren 
        image = toolkit.getImage(filename); 
        ladekontrolle.addImage(Bild,0); 
        // Solange warten, bis das Bild ganz geladen ist 
        try { 
           ladekontrolle.waitForID(0); 
        } catch (InterruptedException e) { 
           // Das Laden ist fehlgeschlagen 
           System.out.println("Fehler!"); 
           return null; 
        } 

        this.x = x;
        thix.y = y;
    }

    public void move(int x, int y) {
        this.x += x;
        this.y += y;
    }
}

// in einer anderen Datei...

public class BildZeiger extends JPanel {
    private Bild image;

    public BildZeiger(String filename) {
        this.image = new Bild(filename);

        addKeyListener(new MyKeyListener());
    }

    public void paintComponent(Graphics g) {
         g.drawImage(image.getImage(),image.getX(),image.getY(),this);
    }

    class MyKeyListener extends KeyAdapter {
        public void keyTyped(KeyEvent k) {
            switch (k.getKeyCode()) {
            case KeyEvent.VK_A: {
                image.move(-10,0);
                break;
            }
            // usw.
            }
        }
    }
}
```


----------



## michi2 (30. Apr 2006)

Und ws ist mit dem bewegen per Maus, und was wenn mann 2 Objekte unabhänig voneinander bewegen will, dann brauch ich aber threads?!?


----------



## Redfrettchen (30. Apr 2006)

Naja, MouseListener ist eigentlich das gleiche. Bei jedem Klick müssen die Koordinaten des Mauszeigers gespeichert werden (in der umschließenden Klasse) und im MouseMotionListener bei mouseDragged(MouseEvent) muss das Bild eben um die Differenz der gespeicherten Daten und der aktuellen Koordinaten bewegt werden und die letzteren gespeichert werden. Auch hier sind keine Threads nötig.
Wenn du mit "2 Objekte unabhänig voneinander bewegen" meinst, eins mit der Maus, das andere mit der Tastatur zu bewegen musst du bloß die Referenzen umschreiben. Die Events konkurrieren ja nicht wirklich und man braucht auch da keine Threads einsetzen. Der Listener verarbeitet die Eingabe indem er das Bild-Objekt (oder ein anderen Objekt) verändert. Dann wird kommt ein repaint (oder Puffer zeichnen oder sowas), der nichts von der Änderung des Objekts weiß und es einfach nur nach seinen Koordinaten fragt, die ja durch die Listener geändert wurden.


----------

