# Pufferproblem bei sich wiederholenden MouseEvents



## JonnyAd (8. Mai 2011)

Hallo,

ich habe ein kleines Konzentrationsspiel in Form eines Applets programmiert, welches online gespielt werden kann. Eine Anbindung an eine Datenbank ist vorhanden. Hier werden User spezifische Parameter gespeichert und zudem ist eine Highscoreaufstellung möglich. Aufgabe des Spiels ist es, die Zahlen von 1 bis n anzuklicken. Es wird also eine Matrix gezeigt mit n Label Feldern. In jedem Label steht eine Zahl. Drückt man auf einen Start Button, so wird nach einem Zufallsprinzip gemischt, eine Stopuhr gestartet und anschließend sollen die Zahlen so schnell wie möglich in der richtigen Reihenfolge angeklickt werden. Klickt man die richtige Zahl an, so färbt sich die Hintergrundfarbe des Labels kurz grün und ein angenehmer Sound (im .wav Format) von ca. 0,3 Sekunden wird abgespielt. Bei einer falschen Zahl: Farbe rot und ein entsprechend unangenehmer Sound.

Problem der ganzen Geschichte ist jedoch, dass gelegentlich der Sound bei jedem Klick zweimal hintereinander abgespielt wird. Dieser Fehler zieht sich dann die gesamte Spielzeit durch, zur Abschaffung muss das Spiel also noch einmal neu gestartet werden. Zudem passiert es gelegentlich, dass bei Klick auf ein Label keine Färbung erfolgt, weil der PC rechen- oder speichertechnisch scheinbar nicht hinterherkommt. Man klickt also schon das nächste und übernächste Label an, aber es passiert nichts. Erst nach wenigen Sekunden färben sich dann die entsprechenden Labels kurz. 

Hier der relevante Code:


```
private class zjbtMouseListener extends  MouseAdapter {
 public void mousePressed(MouseEvent e) {
     labeldummy = (JLabel)(e.getSource());
     if (labeldummy.isEnabled()) {
        if (Integer.valueOf(labeldummy.getText()) == (NumbertoFind)) {
            NumbertoFind++;
            new Thread(new TaskPressedRight(a[1][Integer.valueOf(labeldummy.getText())])).start();
        }
        else {
            new Thread(new TaskPressedWrong(a[1][Integer.valueOf(labeldummy.getText())])).start();
        }

     }
}
}

private class TaskPressedWrong implements Runnable {
    private int indexlabeldummy2;

    private TaskPressedWrong(int indexlabeldummy2){
    this.indexlabeldummy2 = indexlabeldummy2;
    }

    public void run() {

        audioWrong[nfieldsoundw].play();

        try {
            SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                jlblnumberField[indexlabeldummy2].setForeground(nfieldfcp);
                jlblnumberField[indexlabeldummy2].setBackground(nfieldbgcpw);
            }
            });

            Thread.sleep(250);

            SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                jlblnumberField[indexlabeldummy2].setForeground(nfieldfc);
                jlblnumberField[indexlabeldummy2].setBackground(nfieldbgc);
              }
            });

        }
        catch (InterruptedException ex) {

        }

    }
}

private class TaskPressedRight implements Runnable {
    private int indexlabeldummy2;

    public TaskPressedRight(int indexlabeldummy2){
    this.indexlabeldummy2 = indexlabeldummy2;
    }

    public void run() {

        audioRight[nfieldsoundr].play();
        try {
            SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                jlblnumberField[indexlabeldummy2].setForeground(nfieldfcp);
                jlblnumberField[indexlabeldummy2].setBackground(nfieldbgcpr);
              }
            });

            Thread.sleep(250);

            SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                jlblnumberField[indexlabeldummy2].setForeground(nfieldfc);
                jlblnumberField[indexlabeldummy2].setBackground(nfieldbgc);
               }
            });

        }
        catch (InterruptedException ex) {

        }
    }
}
```

Bei jedem Klick wird also ein neuer Thread erstellt, da man durchaus Geschwindigkeiten erreichen kann, bei der man pro Sekunde  3 - 4 Labels anklickt. Selbst wenn ich das Spiel komplett ohne Ton laufen lasse, habe ich noch immer das Problem mit der Färbung. Wer kann mir hier weiterhelfen? 
Zuvor lies ich übrigens alles vom Hauptthread abarbeiten, (neben MausPressed war auch MouseReleased mit eingebunden). Hierbei zeigte sich das Phänomen auch schon.
Ich spekuliere ja darauf, dass man das irgendwie puffern muss. Im Übrigen zeigt sich das Phänomen bei Firefox und Google Chrom, egal ob ich das Spiel auf meinem alten Laptop oder auf meinem leistungsfähigen Desktop-PC laufen lasse.


----------



## Marco13 (8. Mai 2011)

Als erstes würde ich nicht jedes mal einen neuen Thread starten. Stattdessen die Aufgaben ("Richtig" oder "Falsch" ausgeben) z.B. in eine BlockingQueue legen, aus der der Thread sie sich abholt....


----------



## JonnyAd (8. Mai 2011)

Weitere Vorschläge?


----------



## Fu3L (9. Mai 2011)

Was hast du von Marcos Vorschlag umgesetzt? 

Hier das Sun-Tutorial zu Marcos Vorschlag, falls du dir nichts darunter vorstellen kannst:
Guarded Blocks (The Java™ Tutorials > Essential Classes > Concurrency)

So verringerst du die Anzahl der Threads, was alleine schon mehr Performance bringen wird und wenn der Consumer-Thread alleine über das Abspielen des Sounds wacht, kann er sich selbst auch eine Variable setzen, ob gerade noch ein Sound spielt, ohne dass man mit synchronized oder volatile rumhantieren muss.
Du musst das obrige Beispiel natürlich so abändern, das der Consumer nicht auf eine neue Eingabe wartet, sondern dann mit seinem Loop fortfährt, weil du ja noch die Entfärbung des Labels vorrantreiben musst. (und evtl. den Sound weiter abspielen/beenden musst. Die Sound API ist mir relativ unbekannt. Ka, wie viel man da selbst machen muss^^)


----------



## JonnyAd (18. Mai 2011)

Ich habe nun 4 Threads integriert (2 für richtig/ 2 für falsch) dies sich dann immer ihre Aufgaben holen.
Klappt sehr gut. Signifikante Performancesteigerung. Selbst auf dem Laptop läuft's nun sehr gut.
Die Sounddatieien habe ich nun auch gepuffert:

Und zwar so:


```
URL urlright = new URL("http://xxxxxxxx" + nfieldsoundr + ".wav");
            AudioInputStream audioInright = AudioSystem.getAudioInputStream(urlright);
            BufferedInputStream bufferedInputStreamr = new BufferedInputStream(audioInright);

            afr = audioInright.getFormat();
            sizer = (int) (afr.getFrameSize() * audioInright.getFrameLength());
            audior = new byte[sizer];
            infor = new DataLine.Info(Clip.class, afr, sizer);
            bufferedInputStreamr.read(audior, 0, sizer);
            rightSound= (Clip) AudioSystem.getLine(infor);
            rightSound.open(afr, audior, 0, sizer);
```

Bisher haben sich die Probleme noch nicht wieder gezeigt. Ich hoffe, dass es dabei auch bleibt.

JonnyAd


----------

