# LWJGL Update Schleife (Snake)



## JUserToto (10. Dez 2013)

Hallo zusammen,

testweise schreibe ich gerade eine sehr einfache Version von Snake - nur um die grundlegenden Dinge zu begreifen.
Tutorials habe ich mir einige angesehen. Das Problem was ich im Moment habe ist folgendes: Da die Schlange sich ja nicht zu schnell bewegen soll, muss ich den Thread in der Update Schleife schlafen legen. Gleichzeitig muss ich ja aber trotzdem alle Tastatur eingaben abfangen. Gerade weil bei Snake kein einziger Tastendruck ignoriert werden darf.

Die Schleife sieht nun bei mir so aus:


```
while (!Display.isCloseRequested()) {
      checkKeyboard(); /*Tastatureingaben werden verarbeitet */
      bewegeSchlange(); /* Schlange wird um die aktuelle Richtung fortbewegt */
			this.render(); /* Alles wird gezeichnet */
      try {
        Thread.sleep(120); /* Thread wird schlafen gelegt */
      } catch (InterruptedException inte) {
        inte.printStackTrace();
      }
      Display.update(); /* Display wird geupdated */
}
```

Die checkKeyboard sieht so aus:


```
public void checkKeyboard() {
    if (Keyboard.isCreated()) {
      while (Keyboard.next()) {
        int k = Keyboard.getEventKey();
        if (direction != RECHTS &&
                k == Keyboard.KEY_RIGHT && direction != LINKS) {
          direction = RECHTS;
        } else if (direction != LINKS &&
                 k == Keyboard.KEY_LEFT && direction != RECHTS) {
          direction = LINKS;
        } else if (direction != OBEN
                &&  k == Keyboard.KEY_UP && direction != UNTEN) {
          direction = OBEN;

        } else if (direction != UNTEN
                &&  k == Keyboard.KEY_DOWN && direction != OBEN) {
          direction = UNTEN;
        } else if (k == (Keyboard.KEY_ESCAPE)) {
          init();
        }
      }
    }
  }
```

Die Steuerung funktioniert zwar, ist aber so ungenau, dass man sich kaum traut dem Bildschirmrand näher zu kommen... woran könnte das liegen?

lg Toto


----------



## Ruzmanz (11. Dez 2013)

```
if (Keyboard.isCreated()) {
```

Die Abfrage ist relativ unnötig, da man ohne Tastatur in Snake sowieso verloren hat.


```
int k = Keyboard.getEventKey();
```

Sofern dein Compiler den Code nicht optimiert, wird bei jedem Schleifendurchlauf im Speicher ein Platz für den Integer reserviert und wieder freigegeben. Das "int k" sollte vor der Schleife stehen, so muss dieser nur einmal reserviert / freigegeben werden.


```
direction != RECHTS
```

Verbraucht mMn mehr Zeit, als wenn du den Wert einfach nochmal überschreiben würdest:


```
if (k == Keyboard.KEY_RIGHT && direction != LINKS) {
          direction = RECHTS;
        } else if (k == Keyboard.KEY_LEFT && direction != RECHTS) {
          direction = LINKS;
        } else if (k == Keyboard.KEY_UP && direction != UNTEN) {
          direction = OBEN;
        } else if (k == Keyboard.KEY_DOWN && direction != OBEN) {
          direction = UNTEN;
        } else if (k == Keyboard.KEY_ESCAPE) {
          init();
        }
```

Deine Schlafzeit scheint mir ziemlich lange zu dauern. Sofern dein Spiel optimal läuft, wären das max. 8 Bilder pro Sekunde.


```
Thread.sleep(120); /* Thread wird schlafen gelegt */
```


----------



## JUserToto (11. Dez 2013)

Hallo,

danke für die Antwort.
Die Sleep Zeit von 120ms bedeutet, dass die Schlange sich in einer Sekunde 8 Felder weiter bewegt. Das ist schon verdammt schnell. 
Gleichzeitig ist das aber auch genau das Problem: Da mit dem Display.update() auch die Tastatureingaben gepollt werden, ist die Verzögerung zwischen Tastatureingabe und Bewegung der Schlange im schlimmsten Fall wirklich 120ms. Und genau da liegt ja mein Problem: Ich habe keine Ahnung, wie man das anders lösen sollte. Wenn ich garkein Sleep benutze bewegt sie dich Schlange bei jedem Schleifendurchlauf ein Feld vorwärts (Das macht die Methode bewegeSchlange()). Also in unspielbarer Geschwindigkeit.
Der Sleep wiederum verzögert die Tastatureingaben...

lg Toto


----------



## Ruzmanz (11. Dez 2013)

Du musst die Zeit seit deinem letzten Schleifendurchlauf ermitteln, sodass dein Code von der Zeit abhängig ist und nicht von deinem Computer. Nehmen wir mal an mein PC ist sehr schlecht und ich bräuchte für die einzelnen Methoden 200ms + die 120ms = 320ms für einen Schleifendurchlauf. Dein PC ist viel besser und benötigt für die einzelnen Methoden 10ms + die 120ms = 130ms. Dann bewegt sich deine Schlage wesentlich schneller als meine 

Unter "Delta JLWGL" findest du einiges. Z.B. LWJGL Basics 4 (Timing) | NinjaCave

PS: Ich würde das "Display.update(); /* Display wird geupdated */" übrigens direkt nach dem Render aufrufen und ganz zum Schluss deine Schleife schlafen schicken.


----------



## dekka (24. Feb 2014)

Ich habe das selbe Problem.
1.Das Sleep(120) wendest du auf das gesamte Programm an. Das ist keine Lösung.
2.Die delta Time ist für ein Snake nutzlos, da man die Tails immer schrittweise aus der Liste ausliest. 

In C# gibt es einer Klasse DispatcherTimer. An dessen Objekt kann man einem Delegate.
Also eine Funktion wird bsp. alle 120ms aufgerufen.

Hier so :
  timer =new DispatcherTimer();
  timer.Interval = TimeSpan.FromMilliseconds(200);
  timer.IsEnabled = true;
  timer.Tick += function;

Das ist einfach top an C#. 
Ich glaube Java ist da technisch einfach hinterher.
Ich habe jetzt auch keine Lust irgendwie Sender/Listener selbst zu bauen.
Oder umständlich die Schlange in einem eigenen Thread laufen zu lassen. 

Ich suche genauso eine einfach Lösung wie in C#.


----------



## dekka (24. Feb 2014)

Hier stand die Diskussion sei erledigt. Das ist lächerlich, wenn die Lösung, die Benutzung der Delta Time sein soll. Die Delta Time funktioniert ja nicht mehr, wenn bei jedem Tick(Update) der Körper z,b. +10 Pixel bewegt werden soll.

So ich lasse den Mosch(die SnakeKlasse) jetzt doch* in einem extra Thread laufen und führe die Aktualisierung der Liste(tail)* in der run() Methode aus und darin kann dann auch der Sleep verwendet werden. Aber bitte nicht das ganze Programm einschläfern ! Das ist kein Basic.

Anderseits ist der DispatcherTimer in C# ist auch nur eine abgeleitete Threadklasse.

ROtz Java. Wo ein Thread ist, braucht man gleich mehrere.

C# ist einfach durchdachter.


----------



## dekka (24. Feb 2014)

So hab jetzt die wirklich endgültige Lösung gefunden. 

Es gibt für Java die TimerTask Klasse aus dem *util* paket. 
In der Hauptklasse:


```
schlange=new schlange();
      time=new Timer();
      time.schedule(schlange, 0, 300); // ab 0 und dann alle 300ms periodisch wiederholen
```

Schlange erbt dann noch von TimerTask und implementiert die run() Methode, in der dann die Listenaktualisierungen vorgenommen werden. fertig. 

bei mir sieht die zu implementierende run() so aus:


```
Point p=schlangeliste.get(0);  // schlangekopf
			
			int x=p.getX()+direction.getX();
			int y=p.getY()+direction.getY();
			
			schlangeliste.add(0, new Point(x,y));
			schlangeliste.remove(schlange.size()-1);
```

der andere Rest kann dann mit 60fps durchrattern.


----------

