JavaFX Memory Spiel

KinaFina

Mitglied
Java:
package de.fernschulen;

import javafx.application.Application;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
import javafx.scene.Scene;

public class MemoryFX extends Application {
    @Override
    public void start(Stage meineStage) throws Exception {
        //den obersten Knoten erzeugen
        //hier verwenden wir ein FlowPane
        //erzeugt wird die Oberfläche über eine eigene Methode in der Klasse MemoryFeld
        FlowPane rootNode = new MemoryFeld().initGUI(new FlowPane());
        //die Szene erzeugen
        //an den Konstruktor werden der oberste Knoten und die Größe übergeben
        Scene meineScene = new Scene(rootNode, 500, 600);
        
        //den Titel über stage setzen
        meineStage.setTitle("Memory");
        //die Szene setzen
        meineStage.setScene(meineScene);
        //Größenänderungen verhindern
        meineStage.setResizable(false);
        //und anzeigen
        meineStage.show();
        
    }

    public static void main(String[] args) {
        launch(args);

    }



}

package de.fernschulen;


import javafx.event.EventHandler;
import javafx.scene.control.Button;
import javafx.scene.image.ImageView;
import javafx.event.ActionEvent;


//die Klasse für eine Karte des Memory-Spiels
//Sie erbt von Button
public class MemoryKarte extends Button {
    //die Instanzvariablen
    //eine eindeutige ID zur Identifizierung des Bildes
    private int bildID;
    //für die Vorder- und Rückseite
    private ImageView bildVorne, bildHinten;
    
    //wo liegt die Karte im Spielfeld
    private int bildPos;

    //ist die Karte umgedreht?
    private boolean umgedreht;
    //ist die Karte noch im Spiel?
    private boolean nochImSpiel;
    
    //das Spielfeld für die Karte
    private MemoryFeld spielfeld;
    
    
    //die innere Klasse für den Eventhandler der Karte
    class KartenHandler implements EventHandler<ActionEvent>{
        @Override
        public void handle(ActionEvent arg0) {
            //ist die Karte überhaupt noch im Spiel?
            //und sind Züge erlaubt
            if ((nochImSpiel == false) || (spielfeld.zugErlaubt() == false))
                return;
            //wenn die Rückseite zu sehen ist, die Vorderseite anzeigen
            if (umgedreht == false) {
                vorderseiteZeigen();
                //die Methode karteOeffnen() im Spielfeld aufrufen
                //übergeben wird dabei die Karte
                //also die this-Referenz der äußeren Klasse
                spielfeld.karteOeffnen(MemoryKarte.this);
            }
        }
    }
    

    //der Konstruktor
    //er setzt die Bilder
    public MemoryKarte(String vorne, int bildID, MemoryFeld spielfeld) {
        //die Vorderseite, der Dateiname des Bildes wird an den Konstruktor übergeben
        bildVorne = new ImageView(vorne);
        //die Rückseite, sie wird fest gesetzt
        bildHinten = new ImageView("grafiken/back.jpg");
        setGraphic(bildHinten);
        //die Bild-ID
        this.bildID = bildID;
         //die Karte ist erst einmal umgedreht und noch im Feld
        umgedreht = false;
        nochImSpiel = true;
        //mit dem Spielfeld verbinden
        this.spielfeld = spielfeld;

        //die Action setzen
        setOnAction(new KartenHandler());

    }
    
    //da MemoryKarte von der Klasse Button erbt und somit
    //Schaltflächen erzeugen kann, erstelle ich
    //einen weiteren Konstruktor, der es ermöglicht einen Button
    //in der Klasse MemoryFeld
    //mit dem Typ der Klasse MemoryKarte zu erzeugen
    //der dann Methoden der Klasse MemoryKarte aufrufen kann
    public MemoryKarte (String text) {
        setText(text); // setzt den Text auf der Schaltfläche, wird im Konstruktor als Argument übergeben
    }
    
    //die Methode zeigt die Vorderseite der Karte an
    public void vorderseiteZeigen() {
        setGraphic(bildVorne);
        umgedreht = true;
    }

    //die Methode zeigt die Rückseite der Karte an
    public void rueckseiteZeigen(boolean rausnehmen) {
        //soll die Karten komplett aus dem Spiel genommen werden?
        if (rausnehmen == true) {
            //das Bild aufgedeckt zeigen und die Karte aus dem Spiel nehmen
            setGraphic(new ImageView("grafiken/aufgedeckt.jpg"));
            nochImSpiel = false;
        }
        else {
            //sonst nur die Rückseite zeigen
            setGraphic(bildHinten);
            umgedreht = false;
        }
    }
    
    //die Methode liefert die Bild-ID einer Karte
    public int getBildID() {
        return bildID;
    }

    //die Methode liefert die Position einer Karte
    public int getBildPos() {
        return bildPos;
    }
    
    //die Methode setzt die Position einer Karte
    public void setBildPos(int bildPos) {
        this.bildPos = bildPos;
    }
    
    //die Methode liefert den Wert der Variablen umgedreht
    public boolean isUmgedreht() {
        return umgedreht;
    }

    //die Methode liefert den Wert der Variablen nochImSpiel
    public boolean isNochImSpiel() {
        return nochImSpiel;
    }
    
}


package de.fernschulen;


//für die Klassen Arrays und Collections
import java.util.Arrays;
import java.util.Collections;

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Label;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.GridPane;
import javafx.util.Duration;

public class MemoryFeld {
    //eine innere Klasse für den Eventhandler des Timer
    class TimerHandler implements EventHandler<ActionEvent> {
            @Override
            //die Methode ruft die Methode karteSchliessen() auf
            public void handle(ActionEvent arg0) {
                karteSchliessen();
            }
        }
    
    //zu EA 13.3
    //eine innere Klasse für den Schummel-Button
    class Schummler implements EventHandler<ActionEvent>{

        @Override
        //die Methode ruft die Methode allesAufdecken() auf
        public void handle(ActionEvent e) {
            //schummeln.setOnAction(new Schummler());
            //if(e.getSource() instanceof Schummler)
            allesAufdecken();
        }
        
    }

    //das Array für die Karten
    private MemoryKarte[] karten;
    
    //das Array für die Namen der Grafiken
    private String[] bilder = {"grafiken/apfel.jpg", "grafiken/birne.jpg", "grafiken/blume.jpg", "grafiken/blume2.jpg",
            "grafiken/ente.jpg", "grafiken/fisch.jpg", "grafiken/fuchs.jpg", "grafiken/igel.jpg",
            "grafiken/kaenguruh.jpg", "grafiken/katze.jpg", "grafiken/kuh.jpg", "grafiken/maus1.jpg",
            "grafiken/maus2.jpg", "grafiken/maus3.jpg", "grafiken/melone.jpg", "grafiken/pilz.jpg",
            "grafiken/ronny.jpg", "grafiken/schmetterling.jpg","grafiken/sonne.jpg",
            "grafiken/wolke.jpg", "grafiken/maus4.jpg"};
    
    //für die Punkte
    private int menschPunkte, computerPunkte;
    //zwei Labels für die Punkte
    private Label menschPunkteLabel, computerPunkteLabel, aktuellerSpielerLabel; //aktuellerSpielerLabel für EA13.2
    
    //wie viele Karten sind aktuell umgedreht?
    private int umgedrehteKarten;
    
    //für das aktuell umdrehte Paar
    private MemoryKarte[] paar;
    
    //für den aktuellen Spieler
    private int spieler;
    
    //das "Gedächtnis" für den Computer
    //er speichert hier wo das Gegenstück liegt
    private int[][] gemerkteKarten;
    
    //für die Spielstärke
    private int spielstaerke;
    
    //für den Timer
    private Timeline timer;
    
    //für die Ausgabe des aktuellen Spielers
    //zu EA 13.2
    private String esSpielt;
    
    
    //für die Schummelfunktion
    //zu EA 13.3
    private MemoryKarte schummeln;
    //eine Variable des Typs MemoryKarte um
    //Methode aus der Klasse MemoryKarte öffnen zu können
    //private MemoryKarte kartenKlasse;
    
    //der Konstruktor
    public MemoryFeld() {
        //das Array für die Karten erstellen, insgesamt 42 Stück
        karten = new MemoryKarte[42];

        //für das Paar
        paar = new MemoryKarte[2];
    

        //für das Gedächtnis
        //es speichert für jede Karte paarweise die Position im Spielfeld
        gemerkteKarten = new int[2][21];
        
        //keiner hat zu Beginn einen Punkt
        menschPunkte = 0;
        computerPunkte = 0;
        
        //es ist keine Karte umgedreht
        umgedrehteKarten = 0;
        
        //der Mensch fängt an
        spieler = 0;
        
        //die Spielstärke ist 10
        spielstaerke = 10;
        
        //es gibt keine gemerkten Karten
        for (int aussen = 0; aussen < 2; aussen++)
            for (int innen = 0; innen < 21; innen++)
                gemerkteKarten[aussen][innen] = -1;
    
    }

    //die Methode erstellt die Oberfläche und zeichnet die Karten über eine eigene Methode
    //übergeben wird ein FlowPane
    public FlowPane initGUI(FlowPane feld) {
        //für die Ausgaben
        kartenZeichnen(feld);
        menschPunkteLabel = new Label();
        computerPunkteLabel = new Label();
        //Aufgabe 13.2
        //Label erzeugen, das den aktuellen Spieler anzeigt
        //zunächst wird das Label mit "Der Mensch" beschriftet und später in der
        //Methode spielerWechsel() verändert   
        aktuellerSpielerLabel = new Label();
        aktuellerSpielerLabel.setText("Der Mensch");
        menschPunkteLabel.setText(Integer.toString(menschPunkte));
        computerPunkteLabel.setText(Integer.toString(computerPunkte));
        //den Button für die Schummelfunktion erzeugen
        //aktiviert wird er in der Methode spielerWechsel()
        schummeln = new MemoryKarte("Schummeln");
        //schummeln.setActionCommand("geschummelt");
        //Button mit dem EventHandler verbinden
        schummeln.setOnAction(new Schummler());
        
        //in zwei Spalten anzeigen
        GridPane tempGrid = new GridPane();
        //und einfügen, dabei werden die Koordinaten angegeben
        tempGrid.add(new Label("Mensch: "), 0 , 0 );
        tempGrid.add(menschPunkteLabel, 1, 0);
        tempGrid.add(new Label("Computer: "), 0, 1);
        tempGrid.add(computerPunkteLabel, 1 ,1);
        //Label einfügen, das den aktuellen Spieler anzeigt
        tempGrid.add(new Label("Aktueller Spieler: "), 0, 2);
        tempGrid.add(aktuellerSpielerLabel, 1,2);
        //den Schummel-Button einfügen
        tempGrid.add(schummeln,0, 3);
        //Button mit dem EventHandelr verbinden
        //schummeln.setOnAction(new Schummler());
        
        feld.getChildren().add(tempGrid);
        return feld;
    }
    
    
    
    //das eigentliche Spielfeld erstellen
    private void kartenZeichnen(FlowPane feld) {
        int count = 0;
        for (int i = 0; i <= 41; i++) {
            //eine neue Karte erzeugen
            karten[i] = new MemoryKarte(bilder[count], count, this);
            //bei jeder zweiten Karte kommt auch ein neues Bild
            if ((i + 1) % 2 == 0)
                count++;
        }
        //die Karten werden gemischt
        Collections.shuffle(Arrays.asList(karten));

        //und ins Spielfeld gesetzt
        for (int i = 0; i <= 41; i++) {
            feld.getChildren().add(karten[i]);
            //die Position der Karte setzen
            karten[i].setBildPos(i);
        }
    }
    
    //die Methode übernimmt die wesentliche Steuerung des Spiels
    //Sie wird beim Anklicken einer Karte ausgeführt
    public void karteOeffnen(MemoryKarte karte) {
        //zum Zwischenspeichern der ID und der Position
        int kartenID, kartenPos;

        //die Karten zwischenspeichern
        paar[umgedrehteKarten]=karte;
        
        //die ID und die Position beschaffen
        kartenID = karte.getBildID();
        kartenPos = karte.getBildPos();
        
        //die Karte in das Gedächtnis des Computers eintragen
        //aber nur dann, wenn es noch keinen Eintrag an der entsprechenden Stelle gibt
        if ((gemerkteKarten[0][kartenID] == -1))
            gemerkteKarten[0][kartenID] = kartenPos;
        else
            //wenn es schon einen Eintrag gibt
            //und der nicht mit der aktuellen Position übereinstimmt, dann haben wir die
            //zweite Karte gefunden
            //die wird dann in die zweite Dimension eingetragen
            if (gemerkteKarten[0][kartenID] != kartenPos)
                gemerkteKarten[1][kartenID] = kartenPos;
        //umgedrehte Karten erhöhen
        umgedrehteKarten++;
        
        //sind zwei Karten umgedreht worden?
        if (umgedrehteKarten == 2) {
            //dann prüfen wir, ob es ein Paar ist
            paarPruefen(kartenID);
            //den Timer erzeugen
            timer = new Timeline(new KeyFrame(Duration.millis(2000), new TimerHandler()));
            //und starten
            timer.play();
        }
        //haben wir zusammen 21 Paare, dann ist das Spiel vorbei
        if (computerPunkte + menschPunkte == 21) {
            //Methode aufrufen, die den Gewinner-Dialog erzeugt
            gewinnerDialog();
            //dann die Anwendung beenden
            Platform.exit();
        }
    }
    //Einsendeaufgabe 13.1
    //die Methode erstellt einen Dialog, der den Gewinner bekannt gibt
    //sie wird am Ende der Methode karteOeffnen() aufgerufen, bevor die Anwendung geschlossen wird
    private void gewinnerDialog() {
        //Zur Anzeige des Gewinnners
        String gewinner;
        if(computerPunkte > menschPunkte)
            gewinner = "Computer!";
        else
            gewinner = "Mensch!";
        //den Dialog erzeugen
        Alert meinDialog = new Alert(AlertType.INFORMATION,"Gewonnen hat der "+gewinner);
        //den Text setzten
        meinDialog.setHeaderText("Herzlichen Glückwunsch!");
        //den Titel setzten
        meinDialog.setTitle("Gewinner");
        //den Dialog anzeigen
        meinDialog.showAndWait();
    }
    
    //die Methode dreht die Karten wieder auf die Rückseite
    //bzw. nimmt sie aus dem Spiel
    private void karteSchliessen() {
        boolean raus = false;
        //ist es ein Paar?
        if (paar[0].getBildID() == paar[1].getBildID())
            raus = true;
        //wenn es ein Paar war, nehmen wir die Karten aus dem Spiel
        //sonst drehen wir sie nur wieder um
        paar[0].rueckseiteZeigen(raus);
        paar[1].rueckseiteZeigen(raus);
        //es ist keine Karte mehr geöffnet
        umgedrehteKarten = 0;
        //hat der Spieler kein Paar gefunden?
        if (raus == false)
            //dann wird der Spieler gewechselt
            spielerWechseln();
        else
            //hat der Computer ein Paar gefunden?
            //dann ist er noch einmal an der Reihe
            if (spieler == 1)
                computerZug();
    }
    
    //die Methode prüft, ob ein Paar gefunden wurde
    private void paarPruefen(int kartenID) {
        if (paar[0].getBildID() == paar[1].getBildID()) {
            //die Punkte setzen
            paarGefunden();
            //die Karten aus dem Gedächtnis löschen
            gemerkteKarten[0][kartenID]=-2;
            gemerkteKarten[1][kartenID]=-2;
        }
    }

    //die Methode setzt die Punkte, wenn ein Paar gefunden wurde
    private void paarGefunden() {
        //spielt gerade der Mensch?
        if (spieler == 0) {
            menschPunkte++;
            menschPunkteLabel.setText(Integer.toString(menschPunkte));
        }
        else {
            computerPunkte++;
            computerPunkteLabel.setText(Integer.toString(computerPunkte));
        }
    }
    
    //die Methode wechselt den Spieler
    //und zeigt den aktuellen Spieler im Label an (Einsendeaufgabe 13.2)
    private void spielerWechseln() {
        //wenn der Mensch an der Reihe war,
        //kommt jetzt der Computer
        if (spieler == 0) {
            spieler = 1;
            esSpielt = "Der Computer";
            aktuellerSpielerLabel.setText(esSpielt);
            //der Schummel-Button wird deaktiviert
            schummeln.setDisable(true);
            computerZug();
        }
        else {
            spieler = 0;
            esSpielt= "Der Mensch";
            aktuellerSpielerLabel.setText(esSpielt);
            //der Schummel-Button wird aktiviert
            schummeln.setDisable(false);
        }
    }
    
    //zu EA 13.3
    //die Methode für die Schummelfunktion
    //mit Timer
    private void allesAufdecken() {
        /*
        for (int aussen = 0; aussen < 2; aussen++)
            for (int innen = 0; innen < 21; innen++)
            
            if    (gemerkteKarten[aussen][innen] >= -1)
            */
                schummeln.vorderseiteZeigen();   
        }
        
    
    
    //die Methode setzt die Computerzüge um
    private void computerZug() {
        int kartenZaehler = 0;
        int zufall = 0;
        boolean treffer = false;
        //zur Steuerung über die Spielstärke
        if ((int)(Math.random() * spielstaerke) == 0) {
            //erst einmal nach einem Paar suchen
            //dazu durchsuchen wir das Array gemerkteKarten, bis wir in beiden Dimensionen
            //einen Wert finden
            while ((kartenZaehler < 21) && (treffer == false)) {
                //gibt es in beiden Dimensionen einen Wert größer oder gleich 0?
                if ((gemerkteKarten[0][kartenZaehler] >=0) &&  (gemerkteKarten[1][kartenZaehler] >=0)) {
                    //dann haben wir ein Paar
                    treffer = true;
                    //die Vorderseite der Karte zeigen
                    karten[gemerkteKarten[0][kartenZaehler]].vorderseiteZeigen();
                    //und dann die Karte öffnen
                    karteOeffnen(karten[gemerkteKarten[0][kartenZaehler]]);
                    //die zweite Karte auch
                    karten[gemerkteKarten[1][kartenZaehler]].vorderseiteZeigen();
                    karteOeffnen(karten[gemerkteKarten[1][kartenZaehler]]);
                }
                kartenZaehler++;
            }
        }
        //wenn wir kein Paar gefunden haben, drehen wir zufällig zwei Karten um
        if (treffer == false) {
            //solange eine Zufallszahl suchen, bis eine Karte gefunden wird, die noch im Spiel ist
            do {
                zufall = (int)(Math.random() * karten.length);
            } while (karten[zufall].isNochImSpiel() == false);
            //die erste Karte umdrehen
            //die Vorderseite der Karte zeigen
            karten[zufall].vorderseiteZeigen();
            //und dann die Karte öffnen
            karteOeffnen(karten[zufall]);

            //für die zweite Karte müssen wir außerdem prüfen, ob sie nicht gerade angezeigt wird
            do {
                zufall = (int)(Math.random() * karten.length);
            } while ((karten[zufall].isNochImSpiel() == false) || (karten[zufall].isUmgedreht() == true));
            //und die zweite Karte umdrehen
            karten[zufall].vorderseiteZeigen();
            karteOeffnen(karten[zufall]);
        }
    }
    
    //die Methode liefert, ob Züge des Menschen erlaubt sind
    //die Rückgabe ist false, wenn gerade der Computer zieht
    //oder wenn schon zwei Karten umgedreht sind
    //sonst ist die Rückgabe true
    public boolean zugErlaubt() {
        boolean erlaubt = true;
        //zieht der Computer?
        if (spieler == 1)
            erlaubt = false;
        //sind schon zwei Karten umdreht?
        if (umgedrehteKarten == 2)
            erlaubt = false;
        return erlaubt;
    }
    
}
1. Aufgabe:
Lassen Sie am Ende des Memory-Spiels ausgeben, wer gewonnen hat. Damit der Spieler diese Ausgabe lesen kann, sorgen Sie bitte außerdem dafür, dass das Spiel nicht sofort nach dem Ende geschlossen wird.
Welche Technik Sie dazu verwenden, ist Ihnen freigestellt. Sie können zum Beispiel die Ausgabe in einem Label durchführen und das Spiel über einen Timer beenden.
2. Aufgabe:
Erstellen Sie für das Memory-Spiel eine Anzeige des aktuellen Spielers. Lassen Sie dazu in einem Label unterhalb des Spielfelds anzeigen, ob gerade der Mensch zieht oder der Computer.
3. Aufgabe:
Bauen Sie eine „Schummel“-Funktion in das Memory-Spiel ein. Ergänzen Sie dazu eine Schaltfläche Alles aufdecken. Beim Anklicken der Schaltfläche sollen alle noch nicht aufgedeckten Karten für eine bestimmte Zeit angezeigt und danach automatisch wieder umgedreht werden.
Sorgen Sie dafür, dass die „Schummel“-Funktion nur dann aufgerufen werden kann, wenn gerade der Mensch am Zug ist und wenn keine andere Karte angezeigt wird. Dazu können Sie zum Beispiel die Schaltfläche zum Starten der „Schummel“-Funktion je nach Spielzustand aktivieren beziehungsweise deaktivieren.

FRAGE:
Aufgabe 1 + 2 habe ich gelöst und die funktionieren auch. Ob es perfekt ist, sei mal hingestellt :D
Zu Aufgabe 3:
Den Button zur Schummelfunktion habe ich als Instanz der Klasse MemoryKarten erstellt , da die ja von der Klasse Button erbt und Schaltflächen erstellen kann. Die Idee war dann mit dieser Instanz eine Methode der Klasse MemoryKarte in der Klasse MemoryFeld aufzurufen. Man sieht vielleicht noch an den Kommentaren, dass ich einiges versucht habe aber die Verbindung zwischen den Klassen und dem Button nicht hinbekomme und keinen Plan habe , wie ich alle Karten anzeigen lassen soll. Bin für Gedankenhilfen dankbar :)
 

KonradN

Super-Moderator
Mitarbeiter
Was geht denn hier ab? Wenn man nicht helfen kann/will, dann ist es doch ok, aber so typische Fragen benötigen doch nicht solche Kommentare. Ansonsten Duzen wir uns hier im Forum.

Aber kommen wir einmal zu dem eigentlichen Thema:

Ich sehe es problematisch an, wenn Alles mit der UI vermengt wird. Damit hast Du ein großes "Kuddelmuddel" das schwer zu überschauen ist.

Erschwert wird es durch die massive Verwendung von inneren Klassen, die den Code massiv aufblähen (und die vor Java 8 üblich waren, ja, aber mit Java 8 und Dank den funktionalen Elementen so zum Glück Vergangenheit sein sollten!).

Den Button zur Schummelfunktion habe ich als Instanz der Klasse MemoryKarten erstellt , da die ja von der Klasse Button erbt und Schaltflächen erstellen kann.
Ist denn dieser Button eine MemoryKarte? Du brauchst doch nur einen Button - warum nimmst Du dann nicht einfach einen Button!

Die Idee war dann mit dieser Instanz eine Methode der Klasse MemoryKarte in der Klasse MemoryFeld aufzurufen.
Nein! Das klingt schon absolut Überkompliziert! Keep it simple, stupid!

Dein MemoryFeld bekommt also einen Button. Wenn der Button gedrückt wird, dann wird eine Methode in MemoryFeld aufgerufen, die alles macht. Also z.B. alle Karten durchgehen um zu schauen, ob davon eine umgedreht ist oder so.

Auf das Trennen von der UI vom Rest des Programmes gehe ich mal nicht ein, da das vermutlich etwas ist, das ihr noch nicht hattet und das vermutlich zu viel an dieser Stelle wäre. Wenn das interessieren sollte, dann sollte man sich ggf. das MVC Pattern oder noch besser: MVVM Pattern mit mvvmFX Library.
 

KinaFina

Mitglied
Was geht denn hier ab? Wenn man nicht helfen kann/will, dann ist es doch ok, aber so typische Fragen benötigen doch nicht solche Kommentare. Ansonsten Duzen wir uns hier im Forum.

Aber kommen wir einmal zu dem eigentlichen Thema:

Ich sehe es problematisch an, wenn Alles mit der UI vermengt wird. Damit hast Du ein großes "Kuddelmuddel" das schwer zu überschauen ist.

Erschwert wird es durch die massive Verwendung von inneren Klassen, die den Code massiv aufblähen (und die vor Java 8 üblich waren, ja, aber mit Java 8 und Dank den funktionalen Elementen so zum Glück Vergangenheit sein sollten!).


Ist denn dieser Button eine MemoryKarte? Du brauchst doch nur einen Button - warum nimmst Du dann nicht einfach einen Button!


Nein! Das klingt schon absolut Überkompliziert! Keep it simple, stupid!

Dein MemoryFeld bekommt also einen Button. Wenn der Button gedrückt wird, dann wird eine Methode in MemoryFeld aufgerufen, die alles macht. Also z.B. alle Karten durchgehen um zu schauen, ob davon eine umgedreht ist oder so.

Auf das Trennen von der UI vom Rest des Programmes gehe ich mal nicht ein, da das vermutlich etwas ist, das ihr noch nicht hattet und das vermutlich zu viel an dieser Stelle wäre. Wenn das interessieren sollte, dann sollte man sich ggf. das MVC Pattern oder noch besser: MVVM Pattern mit mvvmFX Library.
Hab es hinbekommen :) vielen Dank!
 

KonradN

Super-Moderator
Mitarbeiter
@LaraCroft96 wenn du ein ähnliches Problem hast, dann würde es Sinn machen, dass Du die genauen Details in einem eigenen Thread zeigst und beschreibst, wo Du genau Probleme hast und nicht weiter kommst.

Ich gehe erst einmal davon aus, dass Du einen anderen Code hast, als der TE hier gezeigt hat. Ich hatte ja ein paar Anmerkungen gemacht und wenn Du ähnliche Probleme hast, dann helfen die Dir ggf.
 

LaraCroft96

Mitglied
@KonradN vielen dank für die schnelle Rückmeldung!

Grad hab ich meinen Code nicht parat, hänge noch in der Nachtschicht fest. Allerdings ist mein Code so ziemlich ähnlich. Ich habe einen Button „Schummeln“ erzeugt und weiß nun eben nicht, wie ich die Methode vorderseiteAnzeigen() aus der Klasse MemoryKarte aufrufe. Sollte ich hier eine neue Methode in der Klasse MemoryFeld implementieren? Stehe total auf dem Schlauch…
 

KonradN

Super-Moderator
Mitarbeiter
Ohne den genauen Aufbau zu kennen, ist dies schwer zu sagen. Aber wenn MemoryFeld die einzelnen Karten verwaltet, dann hört es sich erst einmal vernünftig an, da eine Methode zu platzieren, die alle Karten aufdeckt.
 

MrTurner

Mitglied
Habe gerade die gleiche Aufgabe vor mir, bei mir scheitert es jedoch an der Umsetzung im Falle eines Gewinn des Computers (Aufgabe 1), ebenso wie die Anzeige des aktuellen Spielers (Aufgabe 2), bei mir wird dauerhaft der menschliche Spieler angezeigt :(

########################################################################################################
MemoryKarte:
########################################################################################################

Java:
package de.fernschulen;


import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.Button;
import javafx.scene.image.ImageView;
import javafx.util.Duration;


//die Klasse fuer eine Karte des Memory-Spiels
//Sie erbt von Button
public class MemoryKarte extends Button {
    //die Instanzvariablen
    //eine eindeutige ID zur Identifizierung des Bildes
    private int bildID;
    //für die Vorder- und Rueckseite
    private ImageView bildVorne, bildHinten;
    
    //wo liegt die Karte im Spielfeld
    private int bildPos;

    //ist die Karte umgedreht?
    private boolean umgedreht;
    //ist die Karte noch im Spiel?
    private boolean nochImSpiel;
    
    //das Spielfeld für die Karte
    private MemoryFeld spielfeld;
    
    /* ###################################### Aufabe 3; Timer zum Umdrehen setzen ############################################### */
        private Timeline wiederUmdrehen;
    
    //die innere Klasse fuer den Eventhandler der Karte
    class KartenHandler implements EventHandler<ActionEvent>{
        @Override
        public void handle(ActionEvent arg0) {
            //ist die Karte ueberhaupt noch im Spiel?
            //und sind Zuege erlaubt
            if ((nochImSpiel == false) || (spielfeld.zugErlaubt() == false))
                return;
            //wenn die Rueckseite zu sehen ist, die Vorderseite anzeigen
            if (umgedreht == false) {
                vorderseiteZeigen();
                //die Methode karteOeffnen() im Spielfeld aufrufen
                //uebergeben wird dabei die Karte
                //also die this-Referenz der aeusseren Klasse
                spielfeld.karteOeffnen(MemoryKarte.this);
            }
        }
    }
    
    /* ###################################### Aufabe 3; Timer zum Verdecken einstellen ############################################### */
        class TimeHandler implements EventHandler<ActionEvent>{

            @Override
            public void handle(ActionEvent arg0) {
                setGraphic(bildHinten);
                
            }
            
        }
    

    //der Konstruktor
    //er setzt die Bilder
    public MemoryKarte(String vorne, int bildID, MemoryFeld spielfeld) {
        //die Vorderseite, der Dateiname des Bildes wird an den Konstruktor uebergeben
        bildVorne = new ImageView(vorne);
        //die Rueckseite, sie wird fest gesetzt
        bildHinten = new ImageView("grafiken/back.jpg");
        setGraphic(bildHinten);
        //die Bild-ID
        this.bildID = bildID;
         //die Karte ist erst einmal umgedreht und noch im Feld
        umgedreht = false;
        nochImSpiel = true;
        //mit dem Spielfeld verbinden
        this.spielfeld = spielfeld;

        //die Action setzen
        setOnAction(new KartenHandler());
    }
    


    //die Methode zeigt die Rueckseite der Karte an
    public void rueckseiteZeigen(boolean rausnehmen) {
        //soll die Karten komplett aus dem Spiel genommen werden?
        if (rausnehmen == true) {
            //das Bild aufgedeckt zeigen und die Karte aus dem Spiel nehmen
            setGraphic(new ImageView("grafiken/aufgedeckt.jpg"));
            nochImSpiel = false;
        }
        else {
            //sonst nur die Rueckseite zeigen
            setGraphic(bildHinten);
            umgedreht = false;
        }
    }
    
    //die Methode liefert die Bild-ID einer Karte
    public int getBildID() {
        return bildID;
    }

    //die Methode liefert die Position einer Karte
    public int getBildPos() {
        return bildPos;
    }
    
    //die Methode setzt die Position einer Karte
    public void setBildPos(int bildPos) {
        this.bildPos = bildPos;
    }
    
    //die Methode liefert den Wert der Variablen umgedreht
    public boolean isUmgedreht() {
        return umgedreht;
    }

    //die Methode liefert den Wert der Variablen nochImSpiel
    public boolean isNochImSpiel() {
        return nochImSpiel;
    }
    
    //die Methode zeigt die Vorderseite der Karte an
    public void vorderseiteZeigen() {
        setGraphic(bildVorne);
        umgedreht = true;
    }
    
    /* ###################################### Aufabe 3; cheat()Methode initialisieren ############################################### */
    public void cheat() {
        setGraphic(bildVorne);
        /* ###################################### Aufabe 3; Timerzeit einstellen und abspielen ############################################### */
        wiederUmdrehen=new Timeline(new KeyFrame(Duration.millis(3000), new TimeHandler()));
        wiederUmdrehen.play();


    }
    
}


########################################################################################################
MemoryFX
########################################################################################################


Java:
package de.fernschulen;


import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;

public class MemoryFX extends Application{
    @Override
    public void start(Stage meineStage) throws Exception {
        //den obersten Knoten erzeugen
        //hier verwenden wir ein FlowPane
        //erzeugt wird die Oberflaeche ueber eine eigene Methode in der Klasse MemoryFeld
        FlowPane rootNode = new MemoryFeld().initGUI(new FlowPane());
        //die Szene erzeugen
        //an den Konstruktor werden der oberste Knoten und die Groesse uebergeben
        Scene meineScene = new Scene(rootNode, 480, 650);
        
        //den Titel über stage setzen
        meineStage.setTitle("Memory");
        //die Szene setzen
        meineStage.setScene(meineScene);
        //Groessenaenderungen verhindern
        meineStage.setResizable(false);
        //und anzeigen
        meineStage.show();
    }
    
    public static void main(String[] args) {
        //der Start
        launch(args);
    }
}


########################################################################################################
MemoryFeld
########################################################################################################

Java:
package de.fernschulen;

//fuer die Klassen Arrays und Collections
import java.util.Arrays;
import java.util.Collections;
import javafx.application.Platform;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.GridPane;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.util.Duration;

public class MemoryFeld {

    // das Array fuer die Karten
    private MemoryKarte[] karten;

    // das Array fuer die Namen der Grafiken
    private String[] bilder = { "grafiken/apfel.jpg", "grafiken/birne.jpg", "grafiken/blume.jpg", "grafiken/blume2.jpg",
            "grafiken/ente.jpg", "grafiken/fisch.jpg", "grafiken/fuchs.jpg", "grafiken/igel.jpg",
            "grafiken/kaenguruh.jpg", "grafiken/katze.jpg", "grafiken/kuh.jpg", "grafiken/maus1.jpg",
            "grafiken/maus2.jpg", "grafiken/maus3.jpg", "grafiken/melone.jpg", "grafiken/pilz.jpg",
            "grafiken/ronny.jpg", "grafiken/schmetterling.jpg", "grafiken/sonne.jpg", "grafiken/wolke.jpg",
            "grafiken/maus4.jpg" };

    // fuer die Punkte
    private int menschPunkte, computerPunkte;
    // zwei Labels fuer die Punkte
    /* ###################################### Aufabe 2; Spielerzug Label setzen ############################################### */
    private Label menschPunkteLabel, computerPunkteLabel, werIstDranLabel;

    // wie viele Karten sind aktuell umgedreht?
    private int umgedrehteKarten;

    // fuer das aktuell umdrehte Paar
    private MemoryKarte[] paar;

    // fuer den aktuellen Spieler
    private int spieler;

    // das "Gedaechtnis" fuer den Computer
    // er speichert hier wo das Gegenstueck liegt
    private int[][] gemerkteKarten;

    // fuer die Spielstaerke
    private int spielstaerke;

    // fuer den Timer
    private Timeline timer;
    
    
    /*#################################### Aufgabe 1, Sieger ausgeben ############################################*/
    private String sieger;
    
    /*#################################### Aufgabe 3; Aufdeckbutton setzen ############################################*/
    private Button AllesAufdecken;
    
    
    class TimeHandler implements EventHandler<ActionEvent>{
        @Override
        public void handle(ActionEvent arg0) {
            karteSchliessen();           
        }
        
    }
    
    /*#################################### Aufgabe 3 ############################################
     * die for-Schleife geht durch die unaufgedeckten Karten durch, dann wird cheat() ausgelöst in Datei MemoryKarte*/
    
        class SchaltflaecheEventHanlder implements EventHandler<ActionEvent>{

            @Override
            public void handle(ActionEvent arg0) {
                for (int i=0;i<42;i++) {
                    if (karten[i].isNochImSpiel()==true && karten[i].isUmgedreht()==false)
                        karten[i].cheat();
                }
                
            }
            
        }
    

    // der Konstruktor
    public MemoryFeld() {
        // das Array fuer die Karten erstellen, insgesamt 42 Stueck
        karten = new MemoryKarte[42];

        // fuer das Paar
        paar = new MemoryKarte[2];

        // fuer das Gedaechtnis
        // es speichert fuer jede Karte paarweise die Position im Spielfeld
        gemerkteKarten = new int[2][21];

        // keiner hat zu Beginn einen Punkt
        menschPunkte = 0;
        computerPunkte = 0;

        // es ist keine Karte umgedreht
        umgedrehteKarten = 0;

        // der Mensch faengt an
        spieler = 0;

        // die Spielstaerke ist 10
        spielstaerke = 10;

        // es gibt keine gemerkten Karten
        for (int aussen = 0; aussen < 2; aussen++)
            for (int innen = 0; innen < 21; innen++)
                gemerkteKarten[aussen][innen] = -1;
    }

    // die Methode erstellt die Oberflaeche und zeichnet die Karten ueber eine
    // eigene Methode
    // uebergeben wird ein FlowPane
    public FlowPane initGUI(FlowPane feld) {
        // fuer die Ausgaben
        kartenZeichnen(feld);
        menschPunkteLabel = new Label();
        computerPunkteLabel = new Label();
        /* ###################################### Aufabe 2; Spielerzug Label setzen ############################################### */
                werIstDranLabel=new Label();
        menschPunkteLabel.setText(Integer.toString(menschPunkte));
        computerPunkteLabel.setText(Integer.toString(computerPunkte));
        /* ###################################### Aufabe 2; Spielerzug Label initialisieren ############################################### */
        werIstDranLabel.setText("Der Mensch");
        /*#################################### Aufgabe 3; Aufdeckbutton initialisieren ############################################*/
        AllesAufdecken=new Button("Schummeln");
        /*#################################### Aufgabe 3; Aufdeckbutton verlinken ############################################*/
        AllesAufdecken.setOnAction(new SchaltflaecheEventHanlder());

        // in zwei Spalten anzeigen
        GridPane tempGrid = new GridPane();
        // und einfuegen, dabei werden die Koordinaten angegeben
        tempGrid.add(new Label("Der Mensch: "), 0, 0);
        tempGrid.add(menschPunkteLabel, 1, 0);
        tempGrid.add(new Label("Der Computer: "), 0, 1);
        tempGrid.add(computerPunkteLabel, 1, 1);
        tempGrid.add(new Label("Es zieht: "),0,2);
        /*#################################### Aufgabe 2, Label Gridpane adden ############################################*/
        tempGrid.add(werIstDranLabel,1,2);
        /*#################################### Aufgabe 3, Label Gridpane adden ############################################*/
        tempGrid.add(AllesAufdecken,1,3);
        feld.getChildren().add(tempGrid);
        return feld;
    }

    // das eigentliche Spielfeld erstellen
    private void kartenZeichnen(FlowPane feld) {
        int count = 0;
        for (int i = 0; i <= 41; i++) {
            // eine neue Karte erzeugen
            karten[i] = new MemoryKarte(bilder[count], count, this);
            // bei jeder zweiten Karte kommt auch ein neues Bild
            if ((i + 1) % 2 == 0)
                count++;
        }
        // die Karten werden gemischt
        Collections.shuffle(Arrays.asList(karten));

        // und ins Spielfeld gesetzt
        for (int i = 0; i <= 41; i++) {
            feld.getChildren().add(karten[i]);
            // die Position der Karte setzen
            karten[i].setBildPos(i);
        }
    }

    // die Methode uebernimmt die wesentliche Steuerung des Spiels
    // Sie wird beim Anklicken einer Karte ausgefuehrt
    public void karteOeffnen(MemoryKarte karte) {
        // zum Zwischenspeichern der ID und der Position
        int kartenID, kartenPos;

        // die Karten zwischenspeichern
        paar[umgedrehteKarten] = karte;

        // die ID und die Position beschaffen
        kartenID = karte.getBildID();
        kartenPos = karte.getBildPos();

        // die Karte in das Gedaechtnis des Computers eintragen
        // aber nur dann, wenn es noch keinen Eintrag an der entsprechenden Stelle gibt
        if ((gemerkteKarten[0][kartenID] == -1))
            gemerkteKarten[0][kartenID] = kartenPos;
        else
        // wenn es schon einen Eintrag gibt
        // und der nicht mit der aktuellen Position uebereinstimmt, dann haben wir die
        // zweite Karte gefunden
        // die wird dann in die zweite Dimension eingetragen
        if (gemerkteKarten[0][kartenID] != kartenPos)
            gemerkteKarten[1][kartenID] = kartenPos;
        // umgedrehte Karten erhöhen
        umgedrehteKarten++;

        // sind zwei Karten umgedreht worden?
        if (umgedrehteKarten == 2) {
            // dann pruefen wir, ob es ein Paar ist
            paarPruefen(kartenID);
            // den Timer erzeugen
            timer = new Timeline(new KeyFrame(Duration.millis(2000), new TimeHandler()));
            // und starten
            timer.play();
        }
        // haben wir zusammen 21 Paare, dann ist das Spiel vorbei
        if (computerPunkte + menschPunkte == 21) {
            /*#################################### Aufgabe 1, Spiel beenden ############################################*/
            beenden();
        }
    }

    

    // die Methode prueft, ob ein Paar gefunden wurde
    private void paarPruefen(int kartenID) {
        if (paar[0].getBildID() == paar[1].getBildID()) {
            // die Punkte setzen
            paarGefunden();
            // die Karten aus dem Gedächtnis loeschen
            gemerkteKarten[0][kartenID] = -2;
            gemerkteKarten[1][kartenID] = -2;
        }
    }

    // die Methode setzt die Punkte, wenn ein Paar gefunden wurde
    private void paarGefunden() {
        // spielt gerade der Mensch?
        if (spieler == 0) {
            menschPunkte++;
            menschPunkteLabel.setText(Integer.toString(menschPunkte));
        } else {
            computerPunkte++;
            computerPunkteLabel.setText(Integer.toString(computerPunkte));
        }
    }
    
    // die Methode dreht die Karten wieder auf die Rueckseite
        // bzw. nimmt sie aus dem Spiel
        private void karteSchliessen() {
            boolean raus = false;
            // ist es ein Paar?
            if (paar[0].getBildID() == paar[1].getBildID())
                raus = true;
            // wenn es ein Paar war, nehmen wir die Karten aus dem Spiel
            // sonst drehen wir sie nur wieder um
            paar[0].rueckseiteZeigen(raus);
            paar[1].rueckseiteZeigen(raus);
            // es ist keine Karte mehr geoeffnet
            umgedrehteKarten = 0;
            // hat der Spieler kein Paar gefunden?
            if (raus == false)
                // dann wird der Spieler gewechselt
                spielerWechseln();
            else
            // hat der Computer ein Paar gefunden?
            // dann ist er noch einmal an der Reihe
            if (spieler == 1)
                computerZug();
        }

    // die Methode wechselt den Spieler
    private void spielerWechseln() {
        // wenn der Mensch an der Reihe war,
        // kommt jetzt der Computer
        if (spieler == 0) {
            spieler = 1;
            /*#################################### Aufgabe 2, Text auf Computer setzen ############################################*/
            werIstDranLabel.setText("Der Computer");
            /*#################################### Aufgabe 3, Schummelschaltfläche ausblenden ############################################*/
            AllesAufdecken.setVisible(false);
            computerZug();
        
        } else
            spieler = 0;
        /*#################################### Aufgabe 2, Label auf Mensch setzen ############################################*/
        werIstDranLabel.setText("Der Mensch");
        /*#################################### Aufgabe 3, Schummelschaltfläche einblenden ############################################*/
        AllesAufdecken.setVisible(true);
    }

    

    // die Methode setzt die Computerzuege um
    private void computerZug() {
        int kartenZaehler = 0;
        int zufall = 0;
        boolean treffer = false;
        // zur Steuerung ueber die Spielstaerke
        if ((int) (Math.random() * spielstaerke) == 0) {
            // erst einmal nach einem Paar suchen
            // dazu durchsuchen wir das Array gemerkteKarten, bis wir in beiden Dimensionen
            // einen Wert finden
            while ((kartenZaehler < 21) && (treffer == false)) {
                // gibt es in beiden Dimensionen einen Wert groesser oder gleich 0?
                if ((gemerkteKarten[0][kartenZaehler] >= 0) && (gemerkteKarten[1][kartenZaehler] >= 0)) {
                    // dann haben wir ein Paar
                    treffer = true;
                    // die Vorderseite der Karte zeigen
                    karten[gemerkteKarten[0][kartenZaehler]].vorderseiteZeigen();
                    // und dann die Karte oeffnen
                    karteOeffnen(karten[gemerkteKarten[0][kartenZaehler]]);
                    // die zweite Karte auch
                    karten[gemerkteKarten[1][kartenZaehler]].vorderseiteZeigen();
                    karteOeffnen(karten[gemerkteKarten[1][kartenZaehler]]);
                }
                kartenZaehler++;
            }
        }
        // wenn wir kein Paar gefunden haben, drehen wir zufaellig zwei Karten um
        if (treffer == false) {
            // solange eine Zufallszahl suchen, bis eine Karte gefunden wird, die noch im
            // Spiel ist
            do {
                zufall = (int) (Math.random() * karten.length);
            } while (karten[zufall].isNochImSpiel() == false);
            // die erste Karte umdrehen
            // die Vorderseite der Karte zeigen
            karten[zufall].vorderseiteZeigen();
            // und dann die Karte oeffnen
            karteOeffnen(karten[zufall]);

            // fuer die zweite Karte muessen wir ausserdem pruefen, ob sie nicht gerade
            // angezeigt wird
            do {
                zufall = (int) (Math.random() * karten.length);
            } while ((karten[zufall].isNochImSpiel() == false) || (karten[zufall].isUmgedreht() == true));
            // und die zweite Karte umdrehen
            karten[zufall].vorderseiteZeigen();
            karteOeffnen(karten[zufall]);
        }
    }

    // die Methode liefert, ob Zuege des Menschen erlaubt sind
    // die Rueckgabe ist false, wenn gerade der Computer zieht
    // oder wenn schon zwei Karten umgedreht sind
    // sonst ist die Rueckgabe true
    public boolean zugErlaubt() {
        boolean erlaubt = true;
        // zieht der Computer?
        if (spieler == 1)
            erlaubt = false;
        // sind schon zwei Karten umdreht?
        if (umgedrehteKarten == 2)
            erlaubt = false;
        return erlaubt;
    }
    
    
    
    
    
    /*#################################### Aufgabe 1, Beenden > Gewinnerbekanntmachung ausgeben ############################################*/
    public void beenden() {
        if (computerPunkte>menschPunkte)
            /*#################################### Aufgabe 1, Gewinner Computer ausgeben ############################################*/
            sieger="Der Computer hat gewonnen!";
        if (computerPunkte<menschPunkte)
            /*#################################### Aufgabe 1, Gewinner Spieler ausgeben ############################################*/
            sieger="Sie haben gewonnen!";
        /*#################################### Aufgabe 1, Infobox erzeugen ############################################*/
        Alert beenden = new Alert(Alert.AlertType.INFORMATION,sieger,ButtonType.OK);
        beenden.setTitle("Das Spiel ist beendet");
        if (computerPunkte>menschPunkte)
            beenden.setHeaderText("Leider verloren");
        if (computerPunkte<menschPunkte)
            beenden.setHeaderText("Glueckwunsch!");
        beenden.showAndWait();
        /*#################################### Aufgabe 1, OK druecken & Spiel/Programm beenden ############################################*/
        if (beenden.getResult()==ButtonType.OK)
            Platform.exit();
    }
}
 

KonradN

Super-Moderator
Mitarbeiter
Habe gerade die gleiche Aufgabe vor mir, bei mir scheitert es jedoch an der Umsetzung im Falle eines Gewinn des Computers (Aufgabe 1), ebenso wie die Anzeige des aktuellen Spielers (Aufgabe 2), bei mir wird dauerhaft der menschliche Spieler angezeigt :(
Es wird immer der menschliche Spieler angezeigt, da der UI Thread nie die Anzeige aktualisieren kann so lange der Spieler dran ist.

Um das zu verstehen, muss man paar Grundlagen verstehen:

a) Die Event-Loop. Der UI Thread läuft in einer Schleife die immer nach Events für die Anwendung schaut und dieses Event dann abarbeitet. So werden z.B. Button-Klicks verarbeitet: DU klickst mit der Maus auf einen Button, das kommt als Event in die Queue für den UI Thread. Der UI Thread nimmt dann irgendwann dieses Event, stellt fest, was da passieren soll (Der Event Handler vom Button) und führt das dann aus.

b) Das Fenster wird auch vom UI Thread gemalt. Wenn Du also z.B. ein Control anpasst, dann passiert folgendes:
b1) Du rufst auf dem Control eine Methode auf, um den Zustand zu ändern. Das verändert den inneren Status des Controls aber nicht die Anzeige! Da die Anzeige verändert wurde, schickt das Control auch noch ein neues Event los: Das Control muss gemalt werden.
b2) Wenn dann die Ausführung fertig ist, arbeitet der UI Thread das nächste Event ab. Und das geht so lange, bis dann das WM_PAINT Event verarbeitet wird. An der Stelle wird dann die Anzeige angepasst und die Veränderung aus b1 wird sichtbar.

Nun läuft aber in dem UI Thread nicht nur die Veränderung der UI sondern Du führst auch direkt den ComputerMove aus. Das führt dazu, dass die Veränderung eben nicht angezeigt wird. Irgendwann ist der Computer Zug beendet, das Control steht wieder auf menschlichem Spieler und endlich kommt der UI Thread dazu, die WM_PAINT Events zu bearbeiten.

Was Du machen könntest, wäre den UI Thread die Anzeige verarbeiten zu lassen. Du könntest den ComputerMove z.B. über einen Timer nach 1 Sekunde oder so ausführen lassen. Dann würdest Du für 1 Sekunde den Computer als Spieler sehen. Wichtig ist: In der Zwischenzeit musst Du natürlich sicher stellen, dass der Spieler nichts machen kann. Das ist natürlich vom Prinzip her auch weiter aufteilbar. Also ein Timer macht mehrere Dinge. Nach der ersten kurzen Pause deckt er eine erste Karte auf. Dann nach einer weiteren Pause, deckt er die nächste Karte auf. Und dann nach einer weiteren Pause kommt die Auswertung. Aber wirklich aufpassen: Du darfst den UI Thread nicht blockieren, da er ja nach Beendigung der Veränderung die Darstellung machen muss!

Bezüglich Gewinn des Computers habe ich mir den Code jetzt noch nicht gut genug angesehen. Daher kann ich da erst einmal nichts zu sagen. Aber das ist eigentlich gleich zu den Checks, ob der menschliche Spieler gewonnen hat.
 

MrTurner

Mitglied
Es wird immer der menschliche Spieler angezeigt, da der UI Thread nie die Anzeige aktualisieren kann so lange der Spieler dran ist.

Um das zu verstehen, muss man paar Grundlagen verstehen:

a) Die Event-Loop. Der UI Thread läuft in einer Schleife die immer nach Events für die Anwendung schaut und dieses Event dann abarbeitet. So werden z.B. Button-Klicks verarbeitet: DU klickst mit der Maus auf einen Button, das kommt als Event in die Queue für den UI Thread. Der UI Thread nimmt dann irgendwann dieses Event, stellt fest, was da passieren soll (Der Event Handler vom Button) und führt das dann aus.

b) Das Fenster wird auch vom UI Thread gemalt. Wenn Du also z.B. ein Control anpasst, dann passiert folgendes:
b1) Du rufst auf dem Control eine Methode auf, um den Zustand zu ändern. Das verändert den inneren Status des Controls aber nicht die Anzeige! Da die Anzeige verändert wurde, schickt das Control auch noch ein neues Event los: Das Control muss gemalt werden.
b2) Wenn dann die Ausführung fertig ist, arbeitet der UI Thread das nächste Event ab. Und das geht so lange, bis dann das WM_PAINT Event verarbeitet wird. An der Stelle wird dann die Anzeige angepasst und die Veränderung aus b1 wird sichtbar.

Nun läuft aber in dem UI Thread nicht nur die Veränderung der UI sondern Du führst auch direkt den ComputerMove aus. Das führt dazu, dass die Veränderung eben nicht angezeigt wird. Irgendwann ist der Computer Zug beendet, das Control steht wieder auf menschlichem Spieler und endlich kommt der UI Thread dazu, die WM_PAINT Events zu bearbeiten.

Was Du machen könntest, wäre den UI Thread die Anzeige verarbeiten zu lassen. Du könntest den ComputerMove z.B. über einen Timer nach 1 Sekunde oder so ausführen lassen. Dann würdest Du für 1 Sekunde den Computer als Spieler sehen. Wichtig ist: In der Zwischenzeit musst Du natürlich sicher stellen, dass der Spieler nichts machen kann. Das ist natürlich vom Prinzip her auch weiter aufteilbar. Also ein Timer macht mehrere Dinge. Nach der ersten kurzen Pause deckt er eine erste Karte auf. Dann nach einer weiteren Pause, deckt er die nächste Karte auf. Und dann nach einer weiteren Pause kommt die Auswertung. Aber wirklich aufpassen: Du darfst den UI Thread nicht blockieren, da er ja nach Beendigung der Veränderung die Darstellung machen muss!

Bezüglich Gewinn des Computers habe ich mir den Code jetzt noch nicht gut genug angesehen. Daher kann ich da erst einmal nichts zu sagen. Aber das ist eigentlich gleich zu den Checks, ob der menschliche Spieler gewonnen hat.

Danke dir vielmals :) Manchmal helfen diese tiefsinnigeren Erklärungen sehr! Werde mich gleich nochmals drüber machen. Viele Grüsse
 

LaraCroft96

Mitglied
Guten ‚Abend‘ @KonradN ,
danke für die schnelle Rückmeldung!
Ja, gerade hab ich den Code nicht parat, sitze noch in der Nachtschicht fest.

Jedenfalls habe ich einen Schummel-Button erzeugt und diesen mit einem Eventhandler wie eben oben im Code auch verbunden. Eine Methode Namens ‚allesAufdecken()‘ habe ich auch. Nur versteh ich nicht ganz, wie ich an die Methode in der Klasse MemoryKarte kommen soll.. ich würde ja trotzdem eine Instanz von MemoryKarte brauchen oder?

„Dein MemoryFeld bekommt also einen Button. Wenn der Button gedrückt wird, dann wird eine Methode in MemoryFeld aufgerufen, die alles macht. Also z.B. alle Karten durchgehen um zu schauen, ob davon eine umgedreht ist oder so.“

—> bedeutet das, dass es eine Möglichkeit wäre, die Methode vorderseiteZeigen() in der Klasse MemoryFeld neu zu definieren ?

Stehe auf dem Schlauch….

Viele Grüße
 

KonradN

Super-Moderator
Mitarbeiter
Wenn Du die Klassen so hast, wie in diesem Thread bisher angegeben, dann hast Du
  • eine Klasse MemoryFeld, welche die ganzen MemoryKarte Instanzen verwaltet. Diese Klasse baut dann auch die UI selbst auf und daher kannst Du z.B. in dieser Klasse einen Button hinzu fügen für das schummeln.
  • Wenn der Button gedrückt wird, dann kannst Du die MemoryKarten durchgehen um damit etwas zu machen.

Wenn Du in der Instanz von MemoryFeld bist, dann greifst Du also z.B. auf private MemoryKarte[] karten; zu, um dann mit den dort hinterlegten Instanzen etwas zu machen.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
N Hilfe bei Memory-Spiel AWT, Swing, JavaFX & SWT 18
B Swing Memory-Spiel AWT, Swing, JavaFX & SWT 2
M Swing Memory Spiel in Eclipse AWT, Swing, JavaFX & SWT 5
B Memory Spielerwechsel AWT, Swing, JavaFX & SWT 4
E showAndWait is not allowed during animation or layout processing Memory FX AWT, Swing, JavaFX & SWT 2
C Memory Karten nach Paar offen bleiben sonst wieder umdrehen AWT, Swing, JavaFX & SWT 1
eskimo328 Java6 Methode in Java5 verwenden (memory leak JInternalFrame) AWT, Swing, JavaFX & SWT 4
2 Memory Leak mit BufferedImage !! AWT, Swing, JavaFX & SWT 15
P WM 2006 - The Memory Game :P läuft nicht. AWT, Swing, JavaFX & SWT 15
A Problem Spiel auf Panel der GUI zu bringen AWT, Swing, JavaFX & SWT 1
Zeppi Swing Button soll Spiel zurücksetzen AWT, Swing, JavaFX & SWT 5
J Spiel mit Java AWT, Swing, JavaFX & SWT 9
MABY Swing Spiel mit 2 AWT, Swing, JavaFX & SWT 4
O Focus zwischen Chat und Spiel wechselns AWT, Swing, JavaFX & SWT 3
C Benutzername in GUI eingeben und nach Spiel neues Fenster Benutzername wieder anzeigen AWT, Swing, JavaFX & SWT 1
Y Simple Spiel ploten AWT, Swing, JavaFX & SWT 2
H Swing Spiel: Maexchen: method cannot be applied to given types AWT, Swing, JavaFX & SWT 3
C Im Spiel wie auf Pfeiltasten reagieren? AWT, Swing, JavaFX & SWT 3
X Snake - Spiel AWT, Swing, JavaFX & SWT 27
U Event Handling JButton Actionevent: starte Spiel AWT, Swing, JavaFX & SWT 4
B Tastensteuerung im Spiel AWT, Swing, JavaFX & SWT 5
T 2D-Grafik Im 2D Spiel zoomen AWT, Swing, JavaFX & SWT 6
S Swing Spiel Richtig/Falsch implementieren AWT, Swing, JavaFX & SWT 5
J 2D-Grafik Textbasiertes 2D Spiel, jedes Zeichen manipulierbar AWT, Swing, JavaFX & SWT 7
K Swing Spiel flackert sehr häufig AWT, Swing, JavaFX & SWT 2
J Spieleprogramm als "richtiges" Spiel starten AWT, Swing, JavaFX & SWT 19
D Probleme mit Spiel-Engine. Komische Fehler. AWT, Swing, JavaFX & SWT 5
B spiel atomica mit swing AWT, Swing, JavaFX & SWT 6
G Problem mit Swing bei einem Sudoku Spiel AWT, Swing, JavaFX & SWT 2
G Spiel frage zu bilder in java einbinden AWT, Swing, JavaFX & SWT 3
M Problem bei Schiffeversenken-Spiel AWT, Swing, JavaFX & SWT 4
G Spielfeld für ein Spiel erstellen, wie am besten? AWT, Swing, JavaFX & SWT 4

Ähnliche Java Themen

Neue Themen


Oben