Update Konsole ohne Flackern - C ++

7

Ich versuche einen Konsolen-Scroll-Shooter zu machen, ich weiß, dass dies nicht das ideale Medium dafür ist, aber ich habe mich selbst etwas herausgefordert.

Das Problem ist, dass bei jeder Aktualisierung des Frames die gesamte Konsole flackert. Gibt es eine Möglichkeit, dies zu umgehen?

Ich habe ein Array verwendet, um alle notwendigen Zeichen zu speichern, hier ist meine updateFrame -Funktion. Ja, ich weiß system("cls") ist faul, aber wenn das nicht die Ursache für ein Problem ist, bin ich nicht zu diesem Zweck beschäftigt.

%Vor%     
James Hughes 17.01.2016, 19:16
quelle

3 Antworten

14

Ah, das bringt die guten alten Tage zurück. Ich habe ähnliche Dinge in der Highschool gemacht: -)

Sie werden Leistungsprobleme bekommen. Konsolen-I / O, besonders unter Windows, ist langsam. Sehr, sehr langsam (manchmal langsamer als auf Festplatte schreiben, sogar). In der Tat werden Sie schnell erstaunt sein, wie viel andere Arbeit Sie tun können, ohne die Latenz Ihrer Spielschleife zu beeinflussen, da die I / O dazu neigen, alles andere zu dominieren. Die goldene Regel besteht also einfach darin, die Menge an E / A, die Sie tun, vor allem zu minimieren.

Zuerst schlage ich vor, system("cls") loszuwerden und sie durch Aufrufe der eigentlichen Win32-Konsolensubsystemfunktionen zu ersetzen, die cls umschließt (docs ):

%Vor%

In der Tat, anstatt jedes Mal den gesamten "Rahmen" neu zu zeichnen, ist es viel besser, einzelne Zeichen gleichzeitig zu zeichnen (oder zu löschen, indem man sie mit einem Leerzeichen überschreibt):

%Vor%

Beachten Sie, dass dadurch auch das Flimmern eliminiert wird, da der Bildschirm vor dem Neuzeichnen nicht mehr vollständig gelöscht werden muss - Sie können einfach ändern, was geändert werden muss, ohne eine Zwischenlöschung durchzuführen. Daher wird der vorherige Rahmen inkrementell aktualisiert es ist völlig auf dem neuesten Stand.

Ich schlage vor, eine Doppelpufferungstechnik zu verwenden: Einen Puffer im Speicher haben, der den "aktuellen" Zustand des Konsolenbildschirms darstellt, anfänglich mit Leerzeichen gefüllt. Dann haben Sie einen anderen Puffer, der den "nächsten" Zustand des Bildschirms darstellt. Deine Spielaktualisierungslogik ändert den "nächsten" Status (genau wie es jetzt mit deinem battleField -Array geschieht). Wenn es Zeit ist, den Rahmen zu zeichnen, lösche nicht alles zuerst. Gehen Sie stattdessen beide Puffer parallel durch und schreiben Sie nur die Änderungen aus dem vorherigen Zustand aus (der "aktuelle" Puffer an diesem Punkt enthält den vorherigen Zustand). Kopieren Sie dann den Puffer "next" in den Puffer "current", um den nächsten Frame einzurichten.

%Vor%

Sie können sogar noch einen Schritt weiter gehen und Änderungen in einem einzigen I / O-Aufruf zusammenstellen (was erheblich billiger ist als viele Aufrufe für einzelne Zeichenschreibvorgänge, aber immer proportional teurer, je mehr Zeichen geschrieben werden).

%Vor%

In der Theorie wird das viel schneller laufen als die erste Schleife; In der Praxis wird es jedoch wahrscheinlich keinen Unterschied machen, da std::cout ohnehin schon Puffer schreibt. Aber es ist ein gutes Beispiel (und ein allgemeines Muster, das viel auftaucht, wenn es im zugrunde liegenden System keinen Puffer gibt), also habe ich es trotzdem eingefügt.

Beachten Sie schließlich, dass Sie den Ruhezustand auf 1 Millisekunde reduzieren können. Windows kann nicht wirklich für weniger als 10-15ms schlafen, aber es wird verhindern, dass Ihr CPU-Kern 100% Auslastung mit einer minimalen zusätzlichen Latenz erreicht.

Beachten Sie, dass dies nicht die Art und Weise wie "echte" Spiele Dinge tun; Sie löschen fast immer den Puffer und zeichnen jeden Frame neu. Sie flackern nicht, weil sie das Äquivalent eines Doppelpuffers auf der GPU verwenden, wobei der vorherige Rahmen sichtbar bleibt, bis der neue Rahmen vollständig gezeichnet ist.

Bonus : Sie können die Farbe in eine beliebige 8 verschiedene Systemfarben und auch der Hintergrund:

%Vor%     
Cameron 17.01.2016, 20:39
quelle
6

system("cls") ist die Ursache Ihres Problems. Zum Aktualisieren des Frames muss Ihr Programm einen anderen Prozess generieren und dann ein anderes Programm laden und ausführen. Das ist ziemlich teuer. cls löscht den Bildschirm, was bedeutet, dass für einen kurzen Zeitraum (bis die Steuerung zum Hauptprozess zurückkehrt) nichts mehr angezeigt wird. Von dort kommt das Flimmern. Sie sollten eine Bibliothek wie ncurses verwenden, mit der Sie die "Szene" anzeigen können, und dann die Cursorposition auf & lt; 0,0 & gt; ohne etwas auf dem Bildschirm zu ändern und Ihre Szene "über" dem alten wieder anzeigen. Auf diese Weise vermeiden Sie das Flackern, weil Ihre Szene immer etwas anzeigt, ohne den Schritt "vollständig leerer Bildschirm".

    
nsilent22 17.01.2016 20:19
quelle
1

Eine Methode besteht darin, die formatierten Daten in eine Zeichenfolge (oder einen Puffer) zu schreiben und dann den Puffer in die Konsole zu schreiben.

Jeder Aufruf einer Funktion hat einen Overhead. Versuchen Sie, in einer Funktion mehr zu erledigen. In Ihrer Ausgabe könnte dies eine Menge Text pro Ausgabeanforderung bedeuten.

Zum Beispiel:

%Vor%

E / A-Operationen sind teuer (Ausführungsweise), daher ist es am besten, die Daten pro Ausgabeanforderung zu maximieren.

    
Thomas Matthews 17.01.2016 20:15
quelle

Tags und Links