# Grundlagen der Spieleentwicklung



## Thoringe92 (21. Jun 2017)

Heyho,

Kurz zu mir: Fachinformatiker für Systemintegration im 1. Lehrjahr. Voraussichtlich werde ich wohl zum 2. Lehrjahr in die Anwendungsentwicklung springen.

Zur Zeit arbeite ich etwas mit Java, um mich in der Programmierung - neben der Schule - weiterzubilden.

Dazu habe ich bisher einige Tutorials durchgelesen und möchte nach und nach ein Spiel entwickeln. Dazu habe ich mir im Voraus keine Gedanken gemacht, was das Ziel ist - wohl ein großer Fehler! -, sondern möchte einfach mal gucken wie weit ich komme.

*Klasse 1*

```
package gamePackage;

public class Game
{
    public static void main(String[] args)
    {
        new GameWindow();
    }
}
```
*Klasse 2*

```
package gamePackage;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFrame;
import javax.swing.JMenuBar;
import javax.swing.JMenu;
import javax.swing.JMenuItem;

@SuppressWarnings("serial")
public class GameWindow extends JFrame
{
    GamePanel gamePanel;
   
    public GameWindow()
    {
        this.gamePanel = new GamePanel();
       
        createMenu();
       
        add(gamePanel);
        pack();
       
        setTitle("Nam3");
        setResizable(false);
        setLocationRelativeTo(null);
       
        setVisible(true);
    }
   
    void createMenu()
    {
        JMenuBar menuBar = new JMenuBar();
        this.setJMenuBar(menuBar);
       
        JMenu gameMenu = new JMenu("Spiel");
        JMenu configMenu = new JMenu("Einstellungen");
       
        menuBar.add(gameMenu);
        menuBar.add(configMenu);
       
        addGameMenuItems(gameMenu);
        addConfigMenuItems(configMenu);
    }
   
    private void addGameMenuItems(JMenu gameMenu)
    {
        JMenuItem quitItem = new JMenuItem("Spiel verlassen");
        gameMenu.add(quitItem);
        quitItem.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                System.exit(0);
            }
        });   
    }
   
    private void addConfigMenuItems(JMenu configMenu)
    {
       
    }
}
```

*Klasse 3*

```
package gamePackage;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;

import java.net.URL;

import javax.swing.ImageIcon;
import javax.swing.JPanel;

@SuppressWarnings("serial")
public class GamePanel extends JPanel
{
    GameFigure gameFigure;
   
    private final int width = 800, height = 600;
    private final Dimension windowSize = new Dimension(width, height);

    public static final String IMAGE_DIR = "imagePackage/";
    private ImageIcon backgroundImage;
    private final String[] backgroundImages = new String[] {"image0.jpg"};
   
    public GamePanel()
    {
        this.gameFigure = new GameFigure();
       
        setPreferredSize(windowSize);
       
        initGame();
        startGame();
    }

    void initGame()
    {
        setBackgroundImage(0);
    }
   
    void startGame()
    {
        repaint();
    }
   
    public void setBackgroundImage(int imageNumber)
    {
        String imagePath = IMAGE_DIR + backgroundImages[imageNumber];
        URL imageURL = getClass().getResource(imagePath);
        backgroundImage = new ImageIcon(imageURL);
    }
   
    @Override
    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
       
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,  RenderingHints.VALUE_ANTIALIAS_ON);
       
        backgroundImage.paintIcon(null, g, -300, 0);
       
        g.setColor(Color.BLUE);
        g.fillRect(gameFigure.x, gameFigure.y, gameFigure.width, gameFigure.height);
    }
}
```

*Klasse 4*

```
package gamePackage;

import java.awt.Rectangle;

@SuppressWarnings("serial")
public class GameFigure extends Rectangle
{
    public Rectangle gameFigure;
   
    public GameFigure()
    {
        gameFigure = new Rectangle(400, 400, 100, 100);
    }
}
```

Bis jetzt öffne ich ein Fenster mit einem Hintergrundbild. Außerdem gibt es eine Menüleiste, der ich noch nach und nach diverse Einstellungen hinzufügen möchte. Also habe ich noch gar nichts erreicht. Als "Spielfigur" wollte ich einfach mal ein Rectangle als Test einfügen, jedoch passiert beim ausführen - im Bezug auf das Rectangle - nichts. Es gibt keine Fehlermeldung. Kann mir jemand sagen, wo der Fehler liegt? Werden die Variablen des Rectangles nicht übernommen? Würde mich über Antwort freuen, aber auch Denkanstöße oder generelle Tipps zu einem saubereren (das Wort gibt es nicht, ich weiß) Programmierstil.

Gruß

Thor


----------



## Tobse (21. Jun 2017)

Du hast schon die richtige Vermutung: Das Rectangle, dass du in der Klasse GameFigure erzeugst, wird nie angesprochen. Aber der Reihe nach:


Dein "Programmierstil" im Sinne von Ästhetik sieht gut aus; die Einrückung stimmt und die Vertikalen Abstände sind auch gut.
Überschreibe die Methode paint(), nicht paintComponent(). Das ist der offizielle Weg.
extends Rectangle ist _schlechter _Stil. Hier gibt es mehrere Probleme:
Du verwendest im GamePanel eine Instanz der Klasse _GameFigure_. In dieser Klasse erzeugst du das Rectangle aber in einer Member-Variablen. Die hat *nichts *damit zu tun, was die Instanz von GameFigure nach aussen hin ist. Du müsstest im GameFigure() konstruktor super mit den Parametern aufrufen; die gameFigure Member-Variable kannst du dann löschen.
Noch besser ist es, wenn du das _extends Rectangle_ weglässt. Hier geht es um Kapselung. Siehe http://beginnersbook.com/2013/03/oops-in-java-encapsulation-inheritance-polymorphism-abstraction/
Das Problem beginnt damit, dass du von _Rectangle _zu _Shape _abstrahieren musst. Beispiel unten.



```
class GameFigure {
  private Rectangle visibleShape;
  public GameFigure() {
    this.visibleShape = new Rectangle(400, 400, 100, 100);
  }

  public Shape getVisibleShape() {
    return this.visibleShape;
  }
}

// in paint(Graphics) in GamePanel:
Graphics2D g2d = (Graphics2D) g;
g2d.draw(gameFigure.getVisibleShape());
```

P.S.: Swing ist eine denkbar schlechte Umgebung, um Spieleentwicklung zu lernen. Wirklich.


----------



## JCODA (21. Jun 2017)

Tobse hat gesagt.:


> Überschreibe die Methode paint(), nicht paintComponent(). Das ist der offizielle Weg.


Wie passt das zu dem: https://docs.oracle.com/javase/tutorial/uiswing/painting/closer.html ?
Ansonsten stimme ich dir bei allem zu.


----------



## Thoringe92 (21. Jun 2017)

zu 1.

Danke! 

zu 2.

Ich habe das paintComponent aus einem Tutorial übernommen. Hatte es vorher auch als paint geschrieben. Beides funktioniert. Den Unterschied muss ich mir nocheinmal vor Augen führen. 
Als Shape habe ich ja nun nur den Umfang des Rechtecks . Wenn ich davon ausgehe, dass die Figur ein ausgefülltes Rechteck ist, kann ich dann auch Shape verzichten?

zu 3.

"extends" kam mir einfach bei JFrame und JPanel so vorteilhaft vor, dass ich es auch mal mit Rectangle ausprobieren wollte. ^^

zu P.S.:

Es heißt immer "Guck in dem Tutorial.", "Guck in dem Forum." und viele Programmieren dann kleine Spielchen mit Swing. Deswegen bin ich dann auch damit gestartet. :|

Vielen Dank an dich erstmal. Deine Änderungen zeigen direkten Erfolg in meinem Code und deine Erklärungen sind für mich als Laien auch relativ leicht verständlich.


----------



## Tobse (21. Jun 2017)

JCODA hat gesagt.:


> Wie passt das zu dem: https://docs.oracle.com/javase/tutorial/uiswing/painting/closer.html ?
> Ansonsten stimme ich dir bei allem zu.


Ich habe es öfters mit paint() gelesen und es hat immer funktioniert. Der Vorteil ist ja die Hintergrundfarbe und das zeichnen der Unter-Komponenten. Bei einem Spiel... weiss nicht, braucht man das da?

Du kannst das gefüllte Rechteck zeichnen, wenn du g_2d.fill(gameFigure.getVisibleShape());_ benutzt. Schau einfach mal in den Javadoc zu Graphics2D; da finden sich sicherlich noch andere Methoden, die dich interessieren 

Vererbung (extends) ist ein sehr mächtiges Werkzeug. Das kann sich manchmal zu deinem Vorteil auswirken; aber auch sehr leicht zu deinem Nachteil. Deswegen sollte man es mit Bedacht einsetzen. Es wird immer davon abgeraten, dass bei JFrame und JPanel zu benutzen. Auf mich ist es in diesen beiden Fällen aber noch nicht negativ zurückgefallen. Der Beste weg ist, von javax.swing.Component zu erben.

Bzgl. Swing: Swing ist nicht für Spiele gemacht. Es ist auch oft nicht schnell genug dafür. Java Spieleentwicklung ist prinzipiell eine Sackgasse; das beste, auf das du hoffen kannst, ist etwas wie Minecraft (und das ist bei weitem nicht pures Java).


----------



## Thoringe92 (22. Jun 2017)

Ich danke erstmal für die Hilfe/Erklärungen soweit. Ich würde das Thema offenhalten, da ich stark davon ausgehe, dass sich in nächster Zeit noch Probleme auftun und ich dann keinen extra-Thread dafür öffnen möchte. Für dieses Projekt werde ich ersteinmal bei Swing bleiben und werde dann mal nach Alternativen sehen. Was wäre denn geeigneter als Swing? Wie gesagt, bin ich oft auf Swing gestoßen (einfach mal Java und Snake/Flappy Bird/... in der google-Suche eingegeben).


----------



## Thoringe92 (22. Jun 2017)

Ich habe nun mal geguckt, ob ich das extend entfernen kann. 
Bei JFrame und Rectangle funktioniert das Programm weiterhin. Wenn ich "extends JPanel" aus der Klasse 3 nehme aber in Klasse 2 den Verweis auf das Objekt weiterhin habe "jFrame.add(gamePanel)" dann erkennt er dies nicht (, da der Verweis "extends JPanel" ja nicht mehr mitgegeben wird!?), daher muss es ja eine Möglichkeit geben, das Objekt GamePanel an sich dem jFrame hinzuzufügen oder!? Ich hoffe das ist verständlich!?


----------



## Tobse (22. Jun 2017)

Zum einen: doch, mache bitte separate Themen. Dann ist es für andere Leute einfacher, über die Foren-Suche oder die Google-Suche eine Antwort zu finden, wenn sie das selbe Problem haben. Außerdem hilft das, die Diskussion fikussiert zu halten - wenn du hier 4 Themenblöcke anfängst wirds sehr schnell chaotisch.

Die Vererbung da rauszumachen ist einiges mehr Arbeit als nur das "extends _Klasse_" aus dem Quellcode rauszunehmen. Lass es erstmal drin. Wenn du dann mit den ganzen OOP Verhältnissen und Begrifflichkeiten gut klarkommst, weisst du auch, was du da tun musst, um die Vererbung entfernen zu können.


----------



## JuKu (23. Jun 2017)

Tobse hat gesagt.:


> P.S.: Swing ist eine denkbar schlechte Umgebung, um Spieleentwicklung zu lernen. Wirklich.



Dem ist nichts mehr hinzuzufügen.
Swing ist wie AWT, SWT, JavaFX eine UI Library und UI Libraries sind für Spiele einfach eher ungeeignet.
Ich denke, dass kein halbwegs professionelles Spiel je solch eine Library verwenden würde.
JavaFX ist zwar auch noch halbwegs akzeptabel, aber erleichtert die Spiele Entwicklung auch nicht gerade.


----------

