Programmaufbau in Ordnung? Problem mit paintComponent()

h4nk

Mitglied
Hallo Leute, ich versuche gerade "Game of Life" bzw. das Spiel des Lebens zu implementieren.

Momentan soll es nach Model-View-Controller aufgebaut sein, womit ich mich rechts schwer tue. Vorallem zu unterscheiden, was jetzt wohin gehört. Sind das meistens wirklich nur drei Klassen?! Oder mach ich da schon den ersten Fehler?

Der Controller managt bei mir nur den Spielablauf. Er hat als Attribute Model und View hinzugefügt bekommen und besitzt eine Methode startSimulation(), in der in einer Endlosschleife die Methoden vom Model und View aufgerufen werden um das Spielfeld neu zu berechnen und danach das Panel zu "repainten".

Mein View ist momentan da größte Problem. Hier gibts eine Methode createGUI(), in der einem Fram ein MyPanel hinzugefügt wird. MyPanel ist eine innere Klasse von View und erbt von JPanel für die paintComponent()-Methode, anfangs und nach jeder Neuberechnung des Spielfelds repainted werden soll.

Leider wird meine verschachtelte for-Schleife in paintComponent() nicht aufgerufen, um für jedes "Feld" des Spielfelds ein Quadrat zu malen.

Das Model ist gefühlt ein Monster, keine Ahnung ob die ganzen Methoden überhaupt noch als "Businesslogik" akzeptabel sind.. :D In der Klasse wird das Spielfeld gespeichert und jede Zelle/jedes Feld neu berechnet.

Gehören die Methoden da hin oder lieber in den Controller (wobei der ja eigentlich nur als Vermittler dienen sollte?!)

So, genug gelabert.

Model
Java:
public class Model
{
   private boolean[][] field;
   private boolean[] neighbours = new boolean[8];
       
   // Constructor
   public Model (int size)
   {
       this.field = new boolean[size][size];
   }
       
   public boolean[][] refreshField()
   {
       for (int y = 0; y > this.field.length; y++)
       {
           for (int x = 0; x > this.field[y].length; x++)
           {   
               chooseAction(x, y);   
           }
       }            
       return this.field;
   }
   
   
   // Get neighbours
   public boolean[] getNeighbours (int x, int y)
   {
       try
       {
           this.neighbours[0] = this.field[y - 1][x - 1];
           this.neighbours[1] = this.field[y - 1][x];
           this.neighbours[2] = this.field[y - 1][x + 1];
           
           this.neighbours[3] = this.field[y][x - 1];
           this.neighbours[4] = this.field[y][x + 1];
           
           this.neighbours[5] = this.field[y + 1][x - 1];
           this.neighbours[6] = this.field[y + 1][x];
           this.neighbours[7] = this.field[y + 1][x + 1];
       }
       catch (Exception e)
       {
           System.err.println("Spielfeld zu klein.");
       }       
       return this.neighbours;
   }
   
   public void chooseAction (int x, int y)
   {
       int counter = 0;
       
       for (boolean b : getNeighbours(x, y))
       {
           if (b == true)
           {
               counter++;
           }   
       }
       
       if (this.alive(x, y))
       {
           if (counter < 2)
           {
               lonliness(x, y);
           }
           else if (counter > 3)
           {
               overpopulation (x, y);
           }
       }
       else
       {
           if ( counter == 3)
           {
               reproduction(x, y);
           }
       }       
   }
       
   public void reproduction (int x, int y)
   {
       this.born(y, x);
   }
   
   public void lonliness (int x, int y)
   {
       this.kill(y, x);       
   }
   
   public void overpopulation (int x, int y)
   {
       this.kill(y, x);   
   }   
   
   // Sets cell to alive
   public void born (int y, int x)
   {
       this.field[y][x] = true;
   }
   
   // Kills cell
   public void kill (int y, int x)
   {
       this.field[y][x] = false;
   }
   
   public boolean alive (int y, int x)
   {
       if(this.field[y][x] = true)
       {
           return true;
       }
       else
       {
           return false;
       }
   }
   
   // Getter
   public boolean[][] getFeld()
   {
       return field;
   }   
}

View
Java:
public class View  
{
   private JFrame frame;
   private MyPanel panel;
   
   private boolean[][] field;
   
   
   public void createGUI ()
   {
       frame = new JFrame();
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       
       panel = new MyPanel();
       
       
       frame.getContentPane() .add(BorderLayout.CENTER, panel);
       
       frame.setSize(1000, 1000);
       frame.setVisible(true);     
   }
   
   
   
   public void setField(boolean[][] field)
   {
       this.field = field;
   }


   public JPanel getPanel()
   {
       return panel;
   }
   
   
   public class MyPanel extends JPanel
   {
       private static final long serialVersionUID = 1L;
       
       public void paintComponent(Graphics g)
       {
           g.setColor(Color.white);
           g.fillRect(0, 0, frame.getWidth(), frame.getHeight());   
           
           for (int y = 0; y > field.length; y++)
           {
               for (int x = 0; x > field[y].length; x++)
               {   
                   g.setColor(Color.black);
                   g.fillRect(0, 0, 75, 75);   
               }
           }
       }   
   }
   
}

Controller
Java:
public class Control
{
   private View view;
   private Model model;
   
   public void startSimulation()
   {
       while(true)
       {
           view.createGUI();
           view.setField(model.refreshField());
           view.getPanel().repaint();
           
           try
           {
               Thread.sleep(150);
           }
           catch (InterruptedException e)
           {
               System.err.println("Unterbrochen.");
           }   
       }   
   }
   
   
   public void addView(View view)
   {
       this.view = view;
   }
   
   public void addModel(Model model)
   {
       this.model = model;
   }
}

Starter
Java:
public class Starter
{
   public static void main(String[] args)
   {
       int size = 100;
       
       Model myModel = new Model(size);
       View myView = new View();
       
       Control myController = new Control();
       myController.addView(myView);
       myController.addModel(myModel);
       
       myController.startSimulation();   
   }
}
 

iBrain

Mitglied
Das MVC prinzip was auch observer pattern genannt wird heisst nicht nur so weil es Model View und Controller gibt sondern es sind in der regel auch die abstrakte klasse Observable und das interface Observer(ich weiss man könnte denken es ist andersrum wegen der namensgebung doch dem ist nicht so) involviert.Dazu gleich mehr.

In deiner klasse Model werden die roh daten deines spiels gespeichert(Sprich zb dein spielfeld welches felder hat dein spieler welcher auf einem bestimmten feld steht ein würfel der geworfen wird zb).Allerdings würde ich dir empfehlen dein Spielfeld in einer separaten klasse pitch oder PlayingField oder playground oder wie du sie halt nennen willst abzulegen sodass du die möglichkeit hast später einfach weitere komponenten zu deinem model hinzuzufügen.

Sobald du diese klasse deinem projekt hinzugefügt hast implementierst du sie in dein Model und greifst über dein Model objekt später über den getter direkt auf das playground-objekt zu.
das könnte so aussehen
Java:
public class Model extends Observable{
    Playground playground;
    public Model(){
    playground = new Playground(//irgendwelche initialisierungswerte);
    }

    public Playground getPlayground(){
        return this.playground;
    }

    public void setPlayground(Playground playground){
        this.playground = playground;
        customNotifyAll();
    }
 //hier halt noch eine setter methode für playground und evtl getter und setter für andere elemente

 /*und die methoden die zu obserable gehören (ich habe hier eine methode eingefügt mit beiden befehlen die zu observable gehören.somit müsste man theoretisch nur noch customNotifyAll() aufrufen sobald sich ein objekt über einen setter ändert und die observer werden sofort über die status veränderung benachrichtigt.)
Beide methoden setchanged und notifyobservers sind nötig um "angemeldete observer" zu benachrichtigen falls sich was am zustand des models ändert,sollten also von jeder methode implementiert werden die Observable erweitern*/

    private void customNotifyAll(){
        setChanged();
        notifyObservers(this);
    }

}

soweit zu deinem model.

Also zu deiner frage gehören die methoden da hin . Jein.Sie gehören zu deinem objekt im model aber sollten nicht im model selber stehen.das model bietet nur zugriff auf die spielobjekte welche wiederrum zugriff über getter auf die jeweiligen objekt-attribute geben.

Zum controller:
Der controller muss unbedingt von überall ausser aus dem model heraus zugänglich sein.Dein model hat keine ahnung vom controller.Der controller wiederrum muss vom model wissen und von der view.Die view muss auch vom controller wissen.wie realisiert man das ? Also.

Deine main methode von der du aus startest könnte ungefähr so aussehen.
Java:
public class MainApp
{
    MainApp(){
        Model model = new Model();
        Controller controller = new Controller(model);
        View view = new View(controller);
    }

    public static void main(String[] args){
        new MainApp();
    }
}
1.Das model wird initialisiert und bringt die daten mit die nötig sind um das spiel zu bestreiten.
2.Der Controller wird als zweites initialisiert und diesem wird im konstruktor das model übergeben(der controller hat ein Model objekt in der klasse).
3.Der view wird der controller übergeben damit man aus der view heraus darauf zugreifen kann UND damit man dem controller die view ebenfalls übergeben kann(wie das funktioniert kommt gleich).

Die klasse Controller könnte ungefährt so aussehen
Java:
public class Controller{
Model model;
View view;
    public Controller(Model model){
        this.model = model
    }

    //getter und setter für das model und die view
}
Somit weiss der controller über alles bescheid was im programm abläuft.

Zur view:
Die view wird wie oben beschrieben nach dem controller initialisiert(Die reihenfolge ist ausgeprochen wichtig).Aussderdem sollte deine view aus meiner sicht aber das ist vllt ansichtssache direkt JFrame erweitern.

Deine view könnte folgendermassen aussehen:

Java:
public class View extends JFrame{

    private Controller controller;
    private MeinJPanel jPanel;

    public View(Controller controller) {
        this.controller = controller;
        controller.setView(this); //HIER WIRD DEM CONTROLLER DIE VIEW ÜBER this ÜBERGEBEN
        this.setTitle("Dein spielname");
        this.setSize(grösse xy zb 300,300 oder so);
        this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        jPanel = new JPanel(controller);
        this.add(jPanel);
        this.setVisible(true);

    }

    public Controller getcontroller() {
        return controller;
    }

    public void setController(Controller controller) {
        this.controller = controller;
    }//getter und setter für deinen panel

Jetzt wird dein fenster initialisiert sobald du deine main methode aufrufst und deine view ist direkt visible weil
this.setVisible(true);
schon im konstruktor aufgerufen wird.wie du siehst wurde auch dem jpanel der controller übergeben.Das ist der letzte wichtige punkt im pattern.

Deine klasse jpanel ist ja die jenige klasse die das zeichnen soll was sich im model verändert.du hast jetzt in deinem panel die möglichkeit über dein model auf jede erdenkliche veränderung zu reagieren und zwar durch das interface Observer.

Deine klasse könnte (ohne implementierung deiner spielspezifischen sachen) jetzt ca so aussehen:
Java:
public class MeinJPanel implements Observer{
    Controller controller;
    //hier verschiedene elemente deines panels

    MeinJPanel(Controller controller){
        this.controller = controller;
    }
 
    //hier spezifische methoden deines panels die direkt auf dein model zugreifen und es verändern     können weil dein controller auf alle eigenschaften deines models zugreifen kann.
/*
Und unten in der klasse implementierst du die methode die zum Observer interface gehört
diese methode wird immer dann aufgerufen wenn dein Observable notifyObservers(this); aufruft
du kannst diese methode übrigens jeder klasse geben die observable erweitert
*/
@Override
public void update(Observable o, Object arg) {
    /*hier noch spezifische dinge die du an der view ändern willst sobald sich was am model ändert auf die du ja direkt über den controller zugreifen kannst weil dieser deinem jpanel bekannt ist und weil diese methode eben nur bei änderung an deinem observable sprich deinem model aufgerufen wird.*/
    revalidate();
    repaint();
    }

}

Man kann sich als fazit merken:Alles aus der view muss den controller übergeben bekommen um aus allem aus deinem model reagieren zu können.Wenn deine view verändert wird kannst du direkt aus der view eine veränderung an deinem model herbeiführen in dem du über deinen controller der in der view liegt auf das model zu greifst und die änderung direkt wie gewünscht herbeiführst.

Falls du noch eine frage hast und ich was vergessen haben sollte tuts mir leid ^^.Ich hoffe meine antwort konnte dir zumindest ein wenig weiterhelfen.
 
Zuletzt bearbeitet:

iBrain

Mitglied
Mir ist gerade noch was eingefallen.Ich habe vergessen zu erwähnen das ein Observer sich beim Observable anmelden muss.

An dem beispiel von oben wäre das folgende zeile im Kontruktor des Observers mehr um sich beim model als observer anzumelden...sonst funktioniert der ganze zauber natürlich nicht richtig :) :
this.controller.getModel().addObserver(this);

Fast vergessen uiuiui..... :)
 

iBrain

Mitglied
Ich muss mich leider etwas berichtigen.Mein erster Satz war nicht ganz richtig.Das MVC prinzip heisst nicht auch observer pattern sondern nutzt das observer pattern im normalfall zum event handling,es gibt da noch ganz andere techniken.Da ist mir wohl ein fehler unterlaufen :) . War für mich schon so selbstverständlich das ich das ganze direkt in einen topf geworfen habe!Sollte aber hoffe ich kein problem sein für deine umsetzung.Sorry hehe :)
 

h4nk

Mitglied
So, konnte soweit alles gut umsetzen, denke ich. Habe das Programm jetzt soweit, dass man Felder anklicken kann um sie zu beleben und seine Startformation zu erstellen. Danach kann man auf einen Button drücken und die Simulation wird gestartet. Jeder Klick zählt dabei für 100 Runden.

Leider habe ich das Problem, dass, egal wo ich die notifyAll-Methode hinpacke, nur am Ende einmal repainted wird. Sprich man klickt auf den Knopf, er rechnet und zeigt die Endposition nach 100 Zügen. MEin Ziel ist es allerdings den Prozess dahin abzubilden, also am besten nach jeder Feldänderung einen repaint. Habe schon versucht mit Thread.sleep(..) zu arbeiten und die notifyAll-Methode an verschiedene Stellen positioniert – leider kein Erfolg.

Starter
Java:
public class Starter
{
   public Starter()
   {
       Model myModel = new Model();
       Controller myController = new Controller(myModel);  
      
       @SuppressWarnings("unused")
       View myView = new View(myController);          
   }
  
   public static void main(String[] args)
   {
       new Starter();
   }
}

Model
Java:
public class Model extends Observable
{
   private Playfield field;
  
   // Konstruktor
   public Model()
   {
       this.field = new Playfield(100);
   }

   // Getter und Setter
   public Playfield getPlayfield()
   {
       return field;
   }  
}

Playfield
Java:
package model;

import java.util.Observable;

public class Playfield extends Observable
{
   private boolean[][] field;
   private boolean[] neighbours = new boolean[8];
  
  
   // Konstruktor
   public Playfield (int size)
   {
       this.field = new boolean[size][size];
   }
  
  
   public void startSimulation() throws InterruptedException
   {
       // Endlosschleife in der nach jeder Runde das Spielfeld neu geladen wird.
       // while(true)
       for (int i = 0; i < 100; i++)
       {
           refreshField();
       }
   }
  
  
   // Erneuert das Spielfeld und gibt ein neues zurück
   public void refreshField()
   {
       for (int y = 0; y < this.field.length; y++)
       {
           for (int x = 0; x < this.field[y].length; x++)
           {  
               if (y != 0 || x != 0 || y != this.field.length-1 || x != this.field.length-1)
               {
                   chooseAction(x, y);  
               }
           }
       }   
   }
  
  
   // Wählt Aktion um Zelle zu töten oder wiederzubeleben
   public void chooseAction (int x, int y)
   {
       int counter = 0;
      
       for (boolean b : getNeighbours(x, y))
       {
           if (b == true)
           {
               counter++;
           }  
       }
      
       if (this.field[y][x] == true)
       {
           if (counter < 2)
           {
               lonliness(x, y);
           }
           else if (counter > 3)
           {
               overpopulation (x, y);
           }
       }
       else
       {
           if ( counter == 3)
           {
               reproduction(x, y);
           }
       }      
   }
      
  
   // Get neighbours
   public boolean[] getNeighbours (int x, int y)
   {
       try
       {
           this.neighbours[0] = this.field[y - 1][x - 1];
           this.neighbours[1] = this.field[y - 1][x];
           this.neighbours[2] = this.field[y - 1][x + 1];
          
           this.neighbours[3] = this.field[y][x - 1];
           this.neighbours[4] = this.field[y][x + 1];
          
           this.neighbours[5] = this.field[y + 1][x - 1];
           this.neighbours[6] = this.field[y + 1][x];
           this.neighbours[7] = this.field[y + 1][x + 1];
       }
       catch (Exception e)
       {
           // System.err.println("Spielfeld zu klein.");
       }      
       return this.neighbours;
   }
  
  
   // Methoden um zu ermitteln, ob die Zelle lebt oder stirbt.
   public void reproduction (int x, int y)
   {
       this.born(x, y);
   }
  
   public void lonliness (int x, int y)
   {
       this.kill(x, y);      
   }
  
   public void overpopulation (int x, int y)
   {
       this.kill(x, y);  
   }  
  
   public boolean isAlive(int x, int y)
   {
       if (field[y][x] == false)
       {
           return false;
       }
       return true;          
   }
  
   // Zelle töten oder wiederbeleben
   public void born (int x, int y)
   {
       this.field[y][x] = true;
       customNotifyAll();
      
   }
  
   public void kill (int x, int y)
   {
       this.field[y][x] = false;
       customNotifyAll();
      
   }
      
   // Getter und Setter
   public boolean[][] getField()
   {
       return field;
   }  
  
   public void setField(boolean[][] field)
   {
       this.field = field;
   }
  
   public void customNotifyAll()
   {
       setChanged();
       notifyObservers(this);
   }
}

View
Java:
package view;

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

import javax.swing.JButton;
import javax.swing.JFrame;

import control.Controller;


public class View extends JFrame implements ActionListener
{
   private static final long serialVersionUID = 1L;
  
   private Controller controller;
   private MyPanel panel;
  
  
   public View(Controller controller)
   {
       this.controller = controller;
      
       controller.setView(this);
      
       JButton button = new JButton("Wenn du eine Startformation gewählt hast, klicke hier um die Simulation zu starten.");
       button.addActionListener(this);
      
       panel = new MyPanel(controller);
      
       this.getContentPane() .add(BorderLayout.CENTER, panel);
       this.getContentPane() .add(BorderLayout.SOUTH, button);
      
       this.setSize(1100, 1150);
       this.setTitle("Game of Life");
      
       this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       this.setVisible(true);    
   }
  
  
   // Getter und Setter – Controller
   public Controller getController()
   {
       return controller;
   }

   public void setController(Controller controller)
   {
       this.controller = controller;
   }

  
   // Getter und Setter – Panel
   public MyPanel getPanel()
   {
       return panel;
   }

   public void setPanel(MyPanel panel)
   {
       this.panel = panel;
   }


   @Override
   public void actionPerformed(ActionEvent e)
   {
       try
       {
           this.controller.getModel().getPlayfield().startSimulation();
       }
       catch (InterruptedException e1)
       {
           e1.printStackTrace();
       }
   }
}

MyPanel
Java:
package view;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.MouseInfo;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JPanel;

import control.Controller;

public class MyPanel extends JPanel implements Observer
{
   private static final long serialVersionUID = 1L;
   Controller controller;
  
  
   // Konstruktor
   public MyPanel(Controller controller)
   {
       this.controller = controller;
       this.controller.getModel().getPlayfield().addObserver(this);
      
       this.addMouseListener(new MouseAdapter()
       {
           @Override
            public void mousePressed(MouseEvent e)
           {
               // Position of mouse
               double frameX = controller.getView().getLocationOnScreen().getX();
               double frameY = controller.getView().getLocationOnScreen().getY();  
              
               double mouseX = MouseInfo.getPointerInfo().getLocation().getX();
               double mouseY = MouseInfo.getPointerInfo().getLocation().getY();
              
               int x = (int) (mouseX - frameX) / 11;
               int y = (int) ((mouseY - frameY) / 11) - 2;
              
               // clicked on cell set to alive
               controller.getModel().getPlayfield().born( x, (int) y);
            }
        });
   }
  
  
   public void paintComponent(Graphics g)
   {
       g.setColor(Color.white);
       g.fillRect(0, 0, controller.getView().getWidth(), controller.getView().getHeight());  
      
       for (int y = 0; y < controller.getModel().getPlayfield().getField().length; y++)
       {
           for (int x = 0; x < controller.getModel().getPlayfield().getField()[y].length; x++)
           {  
               if (controller.getModel().getPlayfield().isAlive(x, y))
               {
                   g.setColor(Color.white);
                   g.fillRect(x + (x * 10), y + (y * 10), 10, 10);  
               }
               else
               {
                   g.setColor(Color.black);
                   g.fillRect(x + (x * 10), y + (y * 10), 10, 10);  
               }
           }
       }
   }

   @Override
   public void update(Observable o, Object arg)
   {
       revalidate();
       repaint();
   }  
}

Controller
Java:
package control;

import view.View;
import model.Model;


public class Controller
{
   private View view;
   private Model model;
  
   public Controller(Model model)
   {
       this.model = model;
   }
  
   public void setView(View view)
   {
       this.view = view;
   }
  
   public View getView()
   {
       return this.view;
   }

   public Model getModel()
   {
       return model;
   }

   public void setModel(Model model)
   {
       this.model = model;
   }
}
 

Harry Kane

Top Contributor
Wenn deine view verändert wird kannst du direkt aus der view eine veränderung an deinem model herbeiführen in dem du über deinen controller der in der view liegt auf das model zu greifst und die änderung direkt wie gewünscht herbeiführst.
Nach meinem Verständnis vom MVC sollte der Programmfluss gerade so NICHT sein (view wird geändert und das model wird informiert), sondern umgekehrt.

Hier mal ein paar Kommentare zum Code:

Dein Model enthält nur das Playfield und bietet sonst keine Funktionalität. Die Klasse kann weg. Überall dort, wo Model verwendet wird, kannst du gleich Playfield verwenden. Dann kannst du dir es auch sparen, dass beide Klassen Observable erweitern. Das Argument von iBrain mit der späteren Erweiterung des Model um weitere Komponenten ist mir zu abstrakt.

Die Trennung zwischen der „View“ und dem „MyPanel“ ist überflüssig. Dein MyPanel ist deine View! Das erkennt man schon daran, dass MyPanel Observer implementiert, View aber nicht. Du brauchst natürlich noch ein JFrame, um das MyPanel anzuzeigen, aber dazu reicht ein „normales“ JFrame. Wenn MyPanel die View ist, kannst du die View z. B. ohne weiteres in eine JScrollPane oder ein JTabbedPane packen.

Deine Controller-Klasse ist weitgehend arbeitslos. Sie hält lediglich Referenzen auf View und Model und bietet sonst keine Funktionalität. Vorschlag: mache deiner View (also dem MyPanel) das Model (also das Playfield) direkt zugänglich.

Das boolean[][] field der Klasse Playfield ist öffentlich zugänglich. Dies halte ich für ein NoGo, weil dadurch jedes Objekt, das eine Referenz auf Playfield bekommt, das Array nach Belieben ändern kann, sogar die Dimensionen (z. B. mit field[0] = new boolean[4]). Andererseits fehlt ein getter für die size. Mit einer getSize() und der Änderung aus dem letzten Absatz wird aus den folgenden Zeilen der paintComponent von MyPanel:
Java:
        for (int y = 0; y < controller.getModel().getPlayfield().getField().length; y++) {
            for (int x = 0; x < controller.getModel().getPlayfield().getField()[y].length; x++) {
                if (controller.getModel().getPlayfield().isAlive(x, y)) {
                } else {
                }
            }
        }
Das hier:
Java:
        for (int y = 0; y < model.getSize(); y++) {
            for (int x = 0; x < model.getSize(); x++) {
                if (model.isAlive(x, y)) {
                } else {                }
            }
        }
Das Verarbeiten des MouseEvents hat in der View-Klasse nichts zu suchen! Das ist mMn eine klassische Controlleraufgabe. Das einzige, was die View dabei tun sollte, ist, die Koordinaten des Mausevents (an die du übrigens mit e.getX() und e.getY() rankommst) in „Feldkoordinaten“ umzurechnen. Das heisst:

- Der Benutzer klickt auf das Feld, der Controller wird informiert.
- Der Controller fragt bei der View nach den Feldkoordinaten, an denen die Maus geklickt wurde.
- Der Controller ruft auf dem Model born() mit den Feldkoordinaten auf.

Nun zu der Frage, warum du die einzelnen Schritte nicht als „Film“ angezeigt bekommst, sondern nur das fertige Ergebnis. Du rufst startSimulation aus der actionPerformed auf. Damit läuft startSimulation im sogenannten Event Dispatching Thread. Dieser ist auch für das Neuzeichnen der Komponenten verantwortlich. Bei jeder Änderung im Playfield, die customNotifyAll aufruft, ruft MyPanel zwar repaint() auf, dies führt aber nicht zu einem sofortigen Neuzeichnen, sondern nur dazu, dass die MyPanel Instanz beim Repaintmanager für das „Neuzeichnen bei der nächsten Gelegenheit“ vorgemerkt wird. Diese Gelegenheit ist aber erst gekommen, wenn die actionPerformed beendet ist, und das wiederum ist erst dann der Fall, wenn die Simulation durchgelaufen ist.

Ich würde die empfehlen, einen javax.swing.Timer zu benutzen. Als ActionListener für den Timer käme der Controller in Frage. Wenn der Timer gestartet wird, informiert er in festgelegten Abständen den Controller. Der Controller zählt die Aufrufe mit und stoppt den Timer nach 100 Aufrufen. Der Controller ruft ausserdem bei jedem Aufruf refreshField() auf dem Model (dem Playground) auf.

Noch ein paar Kleinigkeiten:
b == true bzw. this.field[y][x] == true schreibt man einfach b bzw. this.field[y][x]
Statt
Java:
        for (int y = 0; y < this.field.length; y++) {
            for (int x = 0; x < this.field[y].length; x++) {
                if (y != 0 || x != 0 || y != this.field.length - 1 || x != this.field.length - 1) {
                }
            }
        }
Kannst du schreiben
Java:
        for (int y = 1; y < this.field.length - 1; y++) {
            for (int x = 1; x < this.field[y].length - 1; x++) {
            }
        }
 

Harry Kane

Top Contributor
Bei einer genaueren Betrachtung des Codes und ein paar Testläufen sind mir ein paar Fehler und Unstimmigkeiten aufgefallen.
1. Die Verwendung von [x] und [y] in der Playfield-Klasse ist verwirrend. Manchmal tauschen x und y die Rollen, z. B. bei
Java:
public boolean isAlive(int x, int y){
      if (field[y][x] == false){
            return false;
      }
      return true;          
}
Du scheinst da aber konsequent bei jedem Zugriff auf field zu machen, von daher mag es "richtig" sein. ich fands aber verwirrend.
2. Bei jedem Simulationsdurchlauf veränderst du die Felder. So wie ich das Konzept verstanden habe, sollen die Änderungen (welche Zelle stirbt oder neu geboren wird) erst nach dem Durchlauf greifen, d. h. eine lebende Zelle mit zuviel oder zuwenig Nachbarn soll erst im nächsten Durchgang sterben, du tötest sie aber sofort.
Erst nachdem ich das korrigiert hatt, hat sich das Spiel so verhalten wie erwartet.
 

h4nk

Mitglied
Ja, gut beobachtet. Das habe ich absichtlich gemacht, weil ein Array bei mir im Kopf senkrecht liegt. Bei einem mehrdimensionalen Array sind dann noch horizontale Arrays in den Fächern. Dadurch würde der erste Platz beim Field[1][2] zu y werden. Wenn man so drüber nachdenkt, macht es natürlich Sinn es einheitlich zu machen und sich von diesem Model zu verabschieden :D Das andere ist mir auch schon aufgefallen, das ändere ich noch.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
A Grundverständnis Programmaufbau Java Java Basics - Anfänger-Themen 8
F Programmaufbau Java Basics - Anfänger-Themen 14
C Programmaufbau bei JBuilder Java Basics - Anfänger-Themen 2
A Programmaufbau Java Basics - Anfänger-Themen 12
A Pendeltouren beim Savings erfahren "schummeln" in Ordnung? Java Basics - Anfänger-Themen 1
I Ordnung JLayeredPane mit Button ändern Java Basics - Anfänger-Themen 5
N Schlüsselworte Bubble Sort nach eigener Ordnung Java Basics - Anfänger-Themen 8
S Wurzelberechnung n-ter Ordnung (Geometrisches Mittel) Java Basics - Anfänger-Themen 4
B Die Ordnung einer Hashmap beibehalten? Java Basics - Anfänger-Themen 4
0x7F800000 Elemente einer Äquivalenzklasse bzgl einer Ordnung vereinen? Java Basics - Anfänger-Themen 18
I Drucker fragen, ob alles in ordnung Java Basics - Anfänger-Themen 3
R Best Practice Problem mit (einfacher) Doppelt-Schleife Java Basics - Anfänger-Themen 53
K Verständnis Problem bei Server/Client Java Basics - Anfänger-Themen 2
I WildFily - unterschiedliche Libs im Projekt verursachen Problem Java Basics - Anfänger-Themen 11
imocode Vererbung Problem mit Vererbung Java Basics - Anfänger-Themen 2
L Taschenrechner Problem Java Basics - Anfänger-Themen 4
I Applikationsserver (WildFly) - Zugriff auf Ressourcen.. Problem mit Pfade Java Basics - Anfänger-Themen 10
A ScheduledExecutorService problem Java Basics - Anfänger-Themen 7
marcelnedza Problem mit Weltzuweisung, JavaKarol Java Basics - Anfänger-Themen 13
XWing Methoden rückgabe Problem? Java Basics - Anfänger-Themen 6
M Erste Schritte Collatz Problem max int Java Basics - Anfänger-Themen 3
M Problem bei verschachtelter for-Schleife bei zweidimensionalen Arrays Java Basics - Anfänger-Themen 3
C GLOOP Problem beim Erstellen der Kamera Java Basics - Anfänger-Themen 9
nelsonmandela Problem bei Ausgabe einer Switch - Case Funktion Java Basics - Anfänger-Themen 5
frager2345 Problem mit Methode Java Basics - Anfänger-Themen 4
L Problem bei Rechnung mit Math.pow Java Basics - Anfänger-Themen 13
A Thread-Schreibe-Lese-Problem Java Basics - Anfänger-Themen 4
SUPERTJB return Problem Java Basics - Anfänger-Themen 3
sserio BigInteger Problem Java Basics - Anfänger-Themen 4
JordenJost Taschenrechner problem Java Basics - Anfänger-Themen 5
K Problem mit "Random" Java Basics - Anfänger-Themen 5
S Datei anlegen Problem! Groß- und Kleinschreibung wird nicht unterschieden Java Basics - Anfänger-Themen 4
sserio Problem beim Anzeigen Java Basics - Anfänger-Themen 5
xanxk Problem For-Schleife mit Charakter Java Basics - Anfänger-Themen 2
L Unbekanntes Problem mit 2d Array Java Basics - Anfänger-Themen 6
sserio Liste erstellt und ein Problem mit dem Index Java Basics - Anfänger-Themen 8
sserio Schwimmen als Spiel. Problem mit to String/ generate a card Java Basics - Anfänger-Themen 4
J Schleife Problem Java Basics - Anfänger-Themen 2
D Problem mit der Erkennung von \n Java Basics - Anfänger-Themen 2
milan123 das ist meine aufgabe ich hab das problem das bei mir Wenn ich die Richtung der Linien verändern will und drei davon sind richtig, verändere ich die 4 Java Basics - Anfänger-Themen 3
M Verständins Problem bei Aufgabe Java Basics - Anfänger-Themen 4
HeiTim Problem mit der Kommasetzung an der richtigen stelle Java Basics - Anfänger-Themen 59
Temsky34 Problem mit dem Code Java Basics - Anfänger-Themen 17
P Problem mit Calendar.getDisplayName() Java Basics - Anfänger-Themen 8
C Problem mit mehreren Methoden + Scanner Java Basics - Anfänger-Themen 5
P Datei einlesen, nach Begriff filtern und in Datei ausgeben. Problem Standardausgabe über Konsole Java Basics - Anfänger-Themen 19
M Problem mit Klassenverständnis und Button Java Basics - Anfänger-Themen 8
EchtKeineAhnungManchmal hallo habe ein Problem mit einer Datei -> (Zugriff verweigert) Java Basics - Anfänger-Themen 4
H Problem mit Verzweigungen Java Basics - Anfänger-Themen 6
H Problem mit Rückgabewert Java Basics - Anfänger-Themen 7
josfe1234 JAVA FX problem Java Basics - Anfänger-Themen 3
A Code Problem Java Basics - Anfänger-Themen 6
Henri Problem von Typen Java Basics - Anfänger-Themen 7
J Problem mit "ArrayIndexOutOfBoundsException" Java Basics - Anfänger-Themen 11
K jackson Mapping - Problem mit Zeitzonen Java Basics - Anfänger-Themen 10
B Threads Problem mit mehreren Threads Java Basics - Anfänger-Themen 38
I Output BigDecimal anstatt double / Problem beim Rechnen Java Basics - Anfänger-Themen 16
D Schleifen Problem Java Basics - Anfänger-Themen 2
H So viele Fehlermeldungen, dass ich nicht weiß wo das Problem ist. Java Basics - Anfänger-Themen 6
J JAVA-Problem blockiert MEDIATHEKVIEW Java Basics - Anfänger-Themen 13
T Problem mit Lehrzeichen und String bei einfacher Chiffre Java Basics - Anfänger-Themen 8
J extends Problem Java Basics - Anfänger-Themen 2
C Polymorphie-Problem Java Basics - Anfänger-Themen 3
Kalibru Problem bei Ausgabe von Objekt Java Basics - Anfänger-Themen 1
I Format Problem mit Wert - bekomme 0,10 anstatt 10,00 Java Basics - Anfänger-Themen 6
J Problem mit einer Methode die gewissen Inhalt einer Array löschen soll Java Basics - Anfänger-Themen 9
J Problem mit einer Methode, die beliebig viele Objekte in Array speichern soll Java Basics - Anfänger-Themen 6
J Allgemeines Problem mit Klassen Java Basics - Anfänger-Themen 5
U Problem mit dem initialisieren meines Strings in einer Schleife Java Basics - Anfänger-Themen 5
amgadalghabra algorithmisches Problem Java Basics - Anfänger-Themen 19
J Traveling Salesman Problem [Arrays] Java Basics - Anfänger-Themen 9
R ArrayList Problem Java Basics - Anfänger-Themen 6
InfinityDE Problem mit Datenübergabe an Konstruktor Java Basics - Anfänger-Themen 7
C RegEx Problem Java Basics - Anfänger-Themen 4
J Anfänger TicTacToe, Problem bei Gewinnoption, sowohl Unentschieden Java Basics - Anfänger-Themen 8
E Taschenrechner GUI Problem mit Fehlerhandling Java Basics - Anfänger-Themen 6
M Input/Output Fallunterscheidung Problem Java Basics - Anfänger-Themen 17
P Problem beim Überschreiben einer vererbten Methode Java Basics - Anfänger-Themen 4
M Problem bei Ausgabe Java Basics - Anfänger-Themen 7
Splayfer Java Array Problem... Java Basics - Anfänger-Themen 2
G Problem bei der Ausgabe einer Main Claase Java Basics - Anfänger-Themen 7
F Problem mit KeyListener in kombination mit dem ActionListener Java Basics - Anfänger-Themen 4
G Subset sum problem mit Backtracking Java Basics - Anfänger-Themen 18
N Problem mit Scanner Java Basics - Anfänger-Themen 2
J Klassen Problem Java Basics - Anfänger-Themen 8
A Out.format problem. Java Basics - Anfänger-Themen 3
J Problem bei der Programmierung eines Tannenbaums Java Basics - Anfänger-Themen 9
A Array problem Java Basics - Anfänger-Themen 16
2 Taschenrechner mit GUI Problem bei der Berechnung Java Basics - Anfänger-Themen 8
W Remote Method Invocation RMI - Problem Java Basics - Anfänger-Themen 0
I Ich habe ein Problem Java Basics - Anfänger-Themen 3
A Problem bei returnen eines Wertes Java Basics - Anfänger-Themen 6
M Regex Erstellung Problem Java Basics - Anfänger-Themen 2
D Input/Output Problem bei der Benutzereingabe eines Befehls Java Basics - Anfänger-Themen 14
M (Sehr großes Problem) Listen als static in anderen Klassen verwendet Java Basics - Anfänger-Themen 12
F Habe ein problem mit dem ActionListener Java Basics - Anfänger-Themen 3
C Regex-Problem Java Basics - Anfänger-Themen 4
J Problem beim vergleich von zwei Integer Java Basics - Anfänger-Themen 3
M Problem in der Modellierung Java Basics - Anfänger-Themen 20
W Wo ist das URL-Problem ? Java Basics - Anfänger-Themen 1

Ähnliche Java Themen

Neue Themen


Oben