0

I want to make a timer that displays 30, 29 etc going down every second and then when there is an input it stops. I know you can do this:

    for (int i = 60; i > 0; i--)
    {
        cout << i << endl;
        Sleep(1000);
    }

This will output 60, 59 etc. But this doesn't allow for any input while the program is running. How do I make it so you can input things while the countdown is running?

Context

This is not a homework assignment. I am making a text adventure game and there is a section where an enemy rushes at you and you have 30 seconds to decide what you are going to do. I don't know how to make the timer able to allow the user to input things while it is running.

Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234
evilhoomans42
  • 75
  • 1
  • 7
  • 1
    Sounds like a good homework assignment. What have you tried? What problems have you run into? What questions do you have about your implementation? How can we help you short of doing your homework for you? – Howard Hinnant Dec 17 '19 at 03:20
  • This isn't homework, sorry for the bad question, I cleared it up tell me if you need more information. – evilhoomans42 Dec 17 '19 at 03:31
  • Fwiw, I don't downvote without warning. These downvotes aren't mine. I recommend a thread to do the countdown and display, and that thread should also check a flag for "input". I also recommend `std::this_thread::sleep_until`, over `Sleep` to keep your count down accurate, and to make the logic simpler. – Howard Hinnant Dec 17 '19 at 03:37
  • 1
    There's no way around this without doing some asynchronous/multithreaded programming, unfortunately, since both of `Sleep` and `cin` will stall your program. Take a look at this answer which uses `std::future` with a timeout to read user input: https://stackoverflow.com/a/47774984/5023438 – alter_igel Dec 17 '19 at 03:38
  • I don't totally understand that, I put it into my code but doesn't it just do the same thing as ```string input getline(cin, input) cout << input; ``` – evilhoomans42 Dec 17 '19 at 03:53

2 Answers2

2

Your game is about 1 frame per second, so user input is a problem. Normally games have higher frame rate like this:

#include <Windows.h>
#include <iostream>

int main() {
    // Initialization
    ULARGE_INTEGER initialTime;
    ULARGE_INTEGER currentTime;
    FILETIME ft;
    GetSystemTimeAsFileTime(&ft);
    initialTime.LowPart = ft.dwLowDateTime;
    initialTime.HighPart = ft.dwHighDateTime;
    LONGLONG countdownStartTime = 300000000; // 100 Nano seconds
    LONGLONG displayedNumber = 31; // Prevent 31 to be displayed

    // Game loop
    while (true) {
        GetSystemTimeAsFileTime(&ft); // 100 nano seconds
        currentTime.LowPart = ft.dwLowDateTime;
        currentTime.HighPart = ft.dwHighDateTime;

        //// Read Input ////
        bool stop = false;
        SHORT key = GetKeyState('S');
        if (key & 0x8000)
            stop = true;

        //// Game Logic ////
        LONGLONG elapsedTime = currentTime.QuadPart - initialTime.QuadPart;
        LONGLONG currentNumber_100ns = countdownStartTime - elapsedTime;
        if (currentNumber_100ns <= 0) {
            std::cout << "Boom!" << std::endl;
            break;
        }
        if (stop) {
            std::wcout << "Stopped" << std::endl;
            break;
        }

        //// Render ////
        LONGLONG currentNumber_s = currentNumber_100ns / 10000000 + 1;
        if (currentNumber_s != displayedNumber) {
            std::cout << currentNumber_s << std::endl;
            displayedNumber = currentNumber_s;
        }
    }

    system("pause");
}
landings
  • 686
  • 1
  • 8
  • 25
  • I don't exactly understand what you're saying here – evilhoomans42 Dec 17 '19 at 04:52
  • To make it simple: Include `conio.h`. Insert `if (_getch()=='s')break;` before `Sleep(1000)` so that the loop can stop early when you press s key. – landings Dec 17 '19 at 05:08
  • Now it only runs when I type a character – evilhoomans42 Dec 17 '19 at 06:08
  • @evilhoomans42: Answer suggests to use [non-blocking-call-for-reading-descriptor](https://stackoverflow.com/questions/5616092/non-blocking-call-for-reading-descriptor), so you avoid thread with that solution. – Jarod42 Dec 17 '19 at 10:20
  • @evilhoomans42 Sorry that is a bad idea. I'll rewrite my answer. – landings Dec 18 '19 at 04:26
  • Is there a way to make it take string inputs so like multiple character inputs? – evilhoomans42 Dec 29 '19 at 02:11
  • @evilhoomans42 Of course. You have to respond to and store all keys you need in "Read Input" section, process all input chars in "Game Logic" secion, and show those chars in "Render" section if you want. All games have this kind of structure, and you can virtually do anything. – landings Dec 31 '19 at 07:08
  • What does the `0x8000` in the if statement in the read input section do? – evilhoomans42 Jan 01 '20 at 02:33
  • Also how would I do what you said – evilhoomans42 Jan 01 '20 at 02:46
  • `0x8000` is to get the highest bit of `key`. Check `GetKeyState()`'s document [link](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getkeystate). – landings Jan 01 '20 at 04:52
  • Just want full code? Implementing a text input area in games, you need quite some work. I suggest you just make a single-key version. – landings Jan 01 '20 at 05:06
0

If you're running this on Linux, you can use the classic select() call. When used in a while-loop, you can wait for input on one or more file descriptors, while also providing a timeout after which the select() call must return. Wrap it all in a loop and you'll have both your countdown and your handling of standard input.

https://linux.die.net/man/2/select

El Stepherino
  • 600
  • 6
  • 18