0

OS: Windows 10 Compiler: Visual Studio 2017 C++ version: 14, apparently

I am making a text adventure game for school. On some screens, I want paragraphs of text to end with a blinking line of text that says, "Press "ENTER" to continue", with animated ellipses, and when the user presses "Enter", that line is cleared and the next paragraph follows. I wrote some code, that I thought would work, but that particular line doesn't change from

Press ENTER to continue

over time. Here is the minimum "working" example:

    #include <iostream>
    #include <string>
    #include <chrono>
    #include <thread>;

    using namespace std;
    using namespace this_thread;     // sleep_for, sleep_until
    using namespace chrono_literals; // ns, us, ms, s, h, etc.
    using chrono::system_clock;

    //class to implement UI methods
    class UI {
    public:
        UI() {}

        void printTitleCard() {
            cout << "+---------------------------+" << endl;
            cout << "|    \"A Text Adventure\"     |" << endl;
            cout << "|            by             |" << endl;
            cout << "|            Me             |" << endl;
            cout << "+---------------------------+" << endl << endl;
        }

        void intro() {
            cout << "Your mother warned you about getting into cars with strangers." << endl;
            cout << "She didn't say anything about vans, true, and it did have a" << endl;
            cout << "friendly moustache on the front bumper, but you knew the risks." << endl;
            cout << "Now you're waking up in the wilderness with no wallet and no clues..." << endl;
            pressEnterToContinue();
            cout << "...and there's something on your shoes." << endl;
        }

        void pressEnterToContinue() {
        //This prompts the user to press "ENTER" to continue
        while (1) {
            string str = "Press ENTER to continue";
            cout << str;
            sleep_until(system_clock::now() + 0.25s); // wait a quarter-second
            cout << string(str.length(), '\b'); // delete printed line
            //repeat
            str += ".";
            cout << str;
            sleep_until(system_clock::now() + 0.25s); // wait a quarter-second
            cout << string(str.length(), '\b'); // delete printed line
            //repeat
            str += ".";
            cout << str;
            sleep_until(system_clock::now() + 0.25s); // wait a quarter-second
            cout << string(str.length(), '\b'); // delete printed line
            //repeat
            str += ".";
            cout << str;
            sleep_until(system_clock::now() + 0.25s); // wait a quarter-second
            cout << string(str.length(), '\b'); // delete printed line
            if (cin.get()) break;
        }
        cin.ignore();
    }
};

    void main()
    {   
        UI ui;
        //Title Card
        ui.printTitleCard();

        //Intro
        ui.intro();
    }

Not only does the text not "animate", I have to press ENTER twice to continue to the next line.

I am asking this question here because I have been doing research for almost an hour, knowing that I have tried to figure this out before, and the only solutions offered are

  • Not in my language
  • Regardless of that, erase the entire screen, not just the line I am on, which turns the entire console into a 'light-bright' with no room for other content.
  • Appear to be using ancient compilers or OS.

Can somebody please help me (and maybe the rest of the internet) close the book on this topic? A solution that works in this context can work in many other contexts. Thank you always for your time.

ALSO: Please keep in mind that this is a drastically stripped-down version of my project to isolate this problem.

UPDATE: I have succeeded in making it "blink". I just need the break condition to work, because right now it just prevents the while loop from starting over (inexplicably):

#include <iostream>
#include <string>
#include <chrono>
#include <thread>

using namespace std;
using namespace this_thread;     // sleep_for, sleep_until
using namespace chrono_literals; // ns, us, ms, s, h, etc.
using chrono::system_clock;

//class to implement UI methods
class UI {
public:
    UI() {}

    void printTitleCard() {
        cout << "+---------------------------+" << endl;
        cout << "|    \"A Text Adventure\"    |" << endl;
        cout << "|            by             |" << endl;
        cout << "|            Me             |" << endl;
        cout << "+---------------------------+" << endl << endl;
    }

    void intro() {
        cout << "Your mother warned you about getting into cars with strangers." << endl;
        cout << "She didn't say anything about vans, true, and it did have a" << endl;
        cout << "friendly moustache on the front bumper, but you knew the risks." << endl;
        cout << "Now you're waking up in the wilderness with no wallet and no clues..." << endl;
        pressEnterToContinue();
        cout << "...and there's something on your shoes." << endl;
    }

void pressEnterToContinue() {
        //This prompts the user to press "ENTER" to continue
        while (1) {
            string str = "Press ENTER to continue";
            cout << str;
            sleep_until(system_clock::now() + 0.5s); // wait a half-second
            cout << string(str.length(), '\b'); // go to front of line
            //repeat
            str += ".";
            cout << str;
            sleep_until(system_clock::now() + 0.5s); // wait a half-second
            cout << string(str.length(), '\b'); // go to front of line
            //repeat
            str += ".";
            cout << str;
            sleep_until(system_clock::now() + 0.5s); // wait a half-second
            cout << string(str.length(), '\b'); // go to front of line
            //repeat
            str += ".";
            cout << str;
            sleep_until(system_clock::now() + 0.5s); // wait a half-second
            cout << string(str.length(), '\b'); // go to front of line
            cout << string(str.length(), ' ');; //print spaces
            cout << string(str.length(), '\b'); // go to front of line
            sleep_until(system_clock::now() + 0.5s); // wait a half-second
            //if (cin.get()) break;
        }
        cin.ignore();
    }
};

void main()
{   
    UI ui;
    //Title Card
    ui.printTitleCard();

    //Intro
    ui.intro();
}

UPDATE2: I have asynchronous methods now but I am unable to get the "ENTER" key to register consistently. It always takes at least 2 attempts for the while loop to exit, and I don't know if that's because of memory issues (wouldn't that be pathetic), inherent flaws with , or a logic error on my part. Here is my minimum working example. I am so close, please offer COMPLETE SOLUTIONS!

#include <iostream>
#include <string>
#include <chrono>
#include <thread>
#include <future>
//#include <C:/Program Files/boost/boost_1_71_0/boost/atomic.hpp>

using namespace std;
using namespace this_thread;     // sleep_for, sleep_until
using namespace chrono_literals; // ns, us, ms, s, h, etc.
using chrono::system_clock;

//class to implement UI methods
class UI {
public:
    UI() {}

    void printTitleCard() {
        cout << "+---------------------------+" << endl;
        cout << "|    \"A Text Adventure\"     |" << endl;
        cout << "|            by             |" << endl;
        cout << "|            Me             |" << endl;
        cout << "+---------------------------+" << endl << endl;
    }

    void intro() {
        cout << "Your mother warned you about getting into cars with strangers." << endl;
        cout << "She didn't say anything about vans, true, and it did have a" << endl;
        cout << "friendly moustache on the front bumper, but you knew the risks." << endl;
        cout << "Now you're waking up in the wilderness with no wallet and no clues..." << endl;
        pressEnterToContinue();
        cout << "...and there's something on your shoes." << endl;
    }

    void pressEnterToContinue() {

        using namespace std::literals;
        string str;

        auto f = std::async(std::launch::async, [str] {
            string userStr;
            getline(cin, userStr);
            if (userStr == "") {
                cout << string(str.length(), '\b'); // go to front of line
                cout << string(str.length(), ' ');; //print spaces
                cout << string(str.length(), '\b'); // go to front of line
                cin.ignore();
                return;}
        });

        while (f._Is_ready()==false/*f.wait_for(1s) != std::future_status::ready*/) {
            str = "Press ENTER to continue";
            cout << str;
            sleep_until(system_clock::now() + 0.5s); // wait a half-second
            cout << string(str.length(), '\b'); // go to front of line
            //repeat
            str += ".";
            cout << str;
            sleep_until(system_clock::now() + 0.5s); // wait a half-second
            cout << string(str.length(), '\b'); // go to front of line
            //repeat
            str += ".";
            cout << str;
            sleep_until(system_clock::now() + 0.5s); // wait a half-second
            cout << string(str.length(), '\b'); // go to front of line
            //repeat
            str += ".";
            cout << str;
            sleep_until(system_clock::now() + 0.5s); // wait a half-second
            cout << string(str.length(), '\b'); // go to front of line
            cout << string(str.length(), ' ');; //print spaces
            cout << string(str.length(), '\b'); // go to front of line
            sleep_until(system_clock::now() + 0.5s); // wait a half-second
        }
    }
};

void main()
{   
    UI ui;
    //Title Card
    ui.printTitleCard();

    //Intro
    ui.intro();
}
hatinacat2000
  • 135
  • 10
  • 1
    You are missing important information like what OS (and version) and what kind of terminal you are using. terminals that are used in the Linux world support `VT100` escape sequences, and there a plenty of questions about that here on SO. If you want to have a cross-platform solution you need to use a library like `ncurses`. – t.niese Sep 14 '19 at 09:09
  • If you don't flush in between "printings" you may not see the result... Anyway, your method is not the right one, you may take advantages of your terminal capabilities. Most of terminal emulators accept escape sequences to obtain several effects. If you are on *nix variant, try to use ncurses library. – Jean-Baptiste Yunès Sep 14 '19 at 09:12
  • Whoops, I should have known better. OS and Compiler are now listed at the start of my question – hatinacat2000 Sep 14 '19 at 09:20
  • I'm not sure if you can use the standard library to do this. Using a library is the most common approach, I believe, and the most popular for this kind of task is ncurses. – Ayjay Sep 14 '19 at 09:26
  • For good terminal handling the C++ standard library isn't really good (as it's unaware of terminals and their capabilities). In my opinion the best solution if you want any kind of "advanced" (like blinking) terminal handling is to use operating-system specific functions (like termcap on POSIX systems, abstracted with the curses library, or the [Windows console](https://learn.microsoft.com/en-us/windows/console/console-reference)). – Some programmer dude Sep 14 '19 at 09:28
  • Please see updated code snippet as I fixed it to "animate" exactly once but I am still unable to clear and rewrite the line that has been written – hatinacat2000 Sep 14 '19 at 09:30
  • I have succeeded in making the line "blink" but now I can't seem to get the exit condition to work, it holds up the animation (see last escaped line in "pressEnterToContnue" function). If we get this to work I will post it publicly on my GitHub and give whoever can help me fix the break point credit – hatinacat2000 Sep 14 '19 at 10:02
  • Looks like this is an asynchronous thingy here so I need to get the boost library or something installed, maybe – hatinacat2000 Sep 14 '19 at 10:12
  • https://stackoverflow.com/q/26127073/1216776 – stark Sep 14 '19 at 11:16
  • @stark I was just looking at but am having trouble using it in my program, could you write me an example that would work with my use-case? Do I have to run stuff in main() or can I run them inside the intro() or pressEnterToContinue() methods? – hatinacat2000 Sep 14 '19 at 11:43
  • I have updated my question with revised code. It almost works but requires more than one tap of the ENTER key (UPDATE: Problem was that "cin.ignore()". I don't remember if I even need that any place, in this context.) – hatinacat2000 Sep 14 '19 at 13:32
  • `void main()` is ill-formed, you should use `int main()` instead. https://stackoverflow.com/q/204476 – L. F. Sep 14 '19 at 13:40
  • I know, I meant to change it before but it was like that in the bare-bones template for the game loop that my professor gave us. All that I have posted here is original, besides that artifact. – hatinacat2000 Sep 14 '19 at 13:44

1 Answers1

0

I got it. This solves 3 FAQs for C++ on a Windows platform (and I don't claim the second is a novel or needed solution):

  • How to make a line of text "blink" without clearing the screen
  • How to delete and overwrite a console line
  • How to run and escape from a continuously running, asynchronous loop.

Here is my minimum, actually-working example:

EDIT: Ok, so there is still a flaw: The loop only runs a single time if you try to run the pressEnterToContinue() function again. It might have to do with how I set the loop condition. Would appreciate feedback.

#include <iostream>
#include <string>
#include <chrono>
#include <thread>
#include <future>

using namespace std;
using namespace this_thread;     // sleep_for, sleep_until
using namespace chrono_literals; // ns, us, ms, s, h, etc.
using chrono::system_clock;

//class to implement UI methods
class UI {
public:
    UI() {}

    void printTitleCard() {
        cout << "+---------------------------+" << endl;
        cout << "|    \"A Text Adventure\"     |" << endl;
        cout << "|            by             |" << endl;
        cout << "|            Me             |" << endl;
        cout << "+---------------------------+" << endl << endl;
    }

    void intro() {
        cout << "Your mother warned you about getting into cars with strangers." << endl;
        cout << "She didn't say anything about vans, true, and it did have a" << endl;
        cout << "friendly moustache on the front bumper, but you knew the risks." << endl;
        cout << "Now you're waking up in the wilderness with no wallet and no clues..." << endl;
        pressEnterToContinue();
        cout << "...and there's something on your shoes." << endl;
    }

    void pressEnterToContinue() {

        using namespace std::literals;
        string str;

        auto f = std::async(std::launch::async, [str] {
            string userStr;
            getline(cin, userStr);
            if (userStr == "\r") {
                cout << string(str.length(), '\b'); // go to front of line
                cout << string(str.length(), ' ');; //print spaces
                cout << string(str.length(), '\b'); // go to front of line
                return;}
        });

        while (f._Is_ready()==false/*f.wait_for(1s) != std::future_status::ready*/) {
            str = "Press ENTER to continue";
            cout << str;
            sleep_until(system_clock::now() + 0.5s); // wait a half-second
            cout << string(str.length(), '\b'); // go to front of line
            //repeat
            str += ".";
            cout << str;
            sleep_until(system_clock::now() + 0.5s); // wait a half-second
            cout << string(str.length(), '\b'); // go to front of line
            //repeat
            str += ".";
            cout << str;
            sleep_until(system_clock::now() + 0.5s); // wait a half-second
            cout << string(str.length(), '\b'); // go to front of line
            //repeat
            str += ".";
            cout << str;
            sleep_until(system_clock::now() + 0.5s); // wait a half-second
            cout << string(str.length(), '\b'); // go to front of line
            cout << string(str.length(), ' ');; //print spaces
            cout << string(str.length(), '\b'); // go to front of line
            sleep_until(system_clock::now() + 0.5s); // wait a half-second
        }
    }
};

int main()
{   
    UI ui;
    //Title Card
    ui.printTitleCard();

    //Intro
    ui.intro();
    return 0;
}
hatinacat2000
  • 135
  • 10