Tastenprellen?



  • Hallo liebe Forengemeinschaft,

    Ich heiße Oliver bin ein leidenschaftlicher, aber nicht ausgebildeter Programmierer.
    Ich habe meine Anfänge mit Assembler gemacht und arbeite nun im hobbybereich seit mehreren Jahren mit AVR-GCC und C++.
    Das Wissen habe ich mir hauptsächlich aus Büchern, Online-Tutorials und Try&Error angeeignet.

    Genug von mir. Nun zu meinem Problem.

    Ich habe ein kleines C++ Rollenspiel auf Konsole-Basis geschrieben. Den Spaghetti-Code möchte ich euch wegen Augenkrebs-Gefahr nicht unbedingt präsentieren...
    Eventuell ist es auch so lösbar.
    Das Rollenspiel bedient sich der Steuerung über Tastatur und nun habe ich das Problem, wenn ich zu oft eine Richtungs-Taste drücke, dann hängt es und der Befehl wird ununterbrochen ausgelöst.

    Aus dem embedded C Bereich würde man von Tastenprellen sprechen, aber in C++ ist mir das so noch nicht untergekommen. Pullups wird es hier wohl nicht geben. Ein Delay zum Entprellen (?) hat nicht funktioniert.

    Kann jemand vielleicht mit dieser Aussage etwas anfangen?



  • Die Frage ist so allgemein leider nicht zu beantworten, da nicht klar ist, wie du den Tastendruck erkennst.
    Auf welchem OS läuft das denn?



  • Windows 10, das Programm ist mit Visual Studio Community geschrieben.

    Der Code ist sehr umfangreich, weswegen ich ihn gerne auf die wichtigsten Elemente kürzen möchte. Weiß aber nicht welche Teile wichtig für eine Analyse wären.

    Das wäre der Navigations-Teil, eventuell erkennt man ja bereits meine "Logik" x.X

                        // Opening map
                        system("cls");
                        std::cout << "* World map *\n\nMove with W-A-S-D.\n";
                        std::cout << "You are here: ";
                        maptile();
    
                        // Game Loop
                        while (choiceMap) {
                            std::cout << "\nWhich direction do you want to go?\n";
                            char ctrl = ' ';
                            std::cin >> ctrl;
    
                            // W-A-S-D control
                            if (ctrl == 'w') {
                                system("cls");
                                //   std::cout << "north\n";
                            }
                            else if (ctrl == 'a') {
                                system("cls");
                                //    std::cout << "west\n";
                            }
                            else if (ctrl == 's') {
                                system("cls");
                                //    std::cout << "south\n";
                            }
                            else if (ctrl == 'd') {
                                system("cls");
                                //    std::cout << "east\n";
                            }
                            else if (ctrl == 'b') {
                                system("cls");
                                std::cout << "* Backpack *\n\n";
                                std::cout << "Gold: " << gold << "\n";
                                if (key == true) {
                                    std::cout << "Key:  " << key << "\n";
                                }
                                else {
                                    std::cout << "You have no items.\n";
                                }
    
                            }
                            else if (ctrl == 'p') {
                                system("cls");
                                std::cout << "* Profile *\n\n";
                                std::cout << "Name:  " << name << "\n";
                                std::cout << "Level: " << lvl << "\n";
                                std::cout << "Exp.:  " << xp << "\n\n";
                                std::cout << "Str:   " << str << "\n";
                                std::cout << "Dex:   " << dex << "\n";
                                std::cout << "Vit:   " << vit << "\n";
                                std::cout << "Life:  " << lifeCurrent << "\n";
                            }
                            else if (ctrl == 'x') {
                                system("cls");
                                choiceMap = false;
                                location = 101;
                                //    std::cout << "Exit Map.\n";
                            }
                            else {
                                system("cls");
                                std::cerr << "Wrong input, please try again.\n\n";
                            }
    
                            // Navigation on the map
                            if ((location == 101) && (ctrl == 'w')) {
                                std::cout << "You can't move there.\n";
                                ctrl = ' ';
                            }
                            if ((location == 101) && (ctrl == 'a')) {
                                std::cout << "You can't move there.\n";
                                ctrl = ' ';
                            }
                            if ((location == 101) && (ctrl == 's')) {
                                location = 201;
                                ctrl = ' ';
                                maptile();
                            }
                            if ((location == 101) && (ctrl == 'd')) {
                                location = 102;
                                ctrl = ' ';
                                maptile();
                            }
    


  • Zustandsautomat:

    2 Zustände: KeyIsDown, KeyIsUp
    Initialzustand ist KeyIsUp
    2 Ereignisse: Keydown, Keyup

    Transitionen:
    Kommt Keydown in Zustand KeyIsUp, mach, was passieren soll und nächster Zustand ist KeyIsDown
    Kommt Keyup in Zustand KeyIsDown, mach, was passieren soll und nächster Zustand ist KeyIsUp

    Keyup wird ignoriert in Zustand KeyIsUp
    Keydown wird ignoriert in Zustand KeyIsDown



  • Die Frage ist auch, wie sich dein Programm verhalten soll. Sollen bei gedrückter Taste die Eingabe wiederholt werden, oder soll jede Taste erst "losgelassen" werden und muss erneut gedrückt werden, um eine Aktion auszulösen?

    Das von dir beschriebene Verhalten kommt vermutlich daher, dass das OS schneller in den Eingabepuffer schreibt, als dein Programm sie auslesen und behandeln kann. Als Quick & Dirty Lösung kannst mal versuchen, alle Zeichen im Puffer zu löschen, bevor du das nächste Zeichen liest.
    Füge zwischen Zeile 9 und 10 mal folgende Zeile ein:

    std::cin.ignore( std::numeric_limits<std::streamsize>::max() );
    

    Eventuell musst du zusätzlich die beiden Header <limits> und <ios>inkludieren.



  • @cpp-n00b sagte in Tastenprellen?:

    std::cin >> ctrl;

    Das Problem ist ja, dass cin erst liest, wenn du Enter drückst - und dann alle Zeichen direkt nacheinander kommen. Das ist für ein Spiel also wohl keine gute Wahl. Auch das system("cls") ist nicht wirklich toll.

    Was tun stattdessen?

    Da es ja um eine Consolenapp unter Windows geht, da mal nachschauen:

    1. Tasten lesen: https://learn.microsoft.com/en-us/windows/console/readconsoleinput?redirectedfrom=MSDN
    2. Bildschirm löschen: https://learn.microsoft.com/en-us/windows/console/clearing-the-screen

    Vielleicht kannst du mal in die Richtung weitergucken.



  • @DocShoe

    Mit copy & paste in die besagte Stelle eingefügt und limits und ios nach einem Test inkludiert.
    Der Befehl wird nicht mehr ausgeführt. Das Programm bleibt "stehen". Dasselbe Verhalten wenn ich die Includes einfüge. Noch eine Idee? Ich mag es Quick & Dirty...

    @wob

    Danke für den Tipp, das werde ich mir anschließend mal durchlesen und schauen ob das besagtes Problem löst.


Anmelden zum Antworten