I have tried various setups with input and my one second timer but nothing is working. The entire code is brought to a halt when it reaches the part asking for input. I have an unbuffered stream, so I don't need to press enter to send the input. Also the purpose of this is for a pac-man game I'm designing for terminal use. What I want is basically to have a one second interval where the user can enter a command. If no command is entered, I want the pac-man to continue moving the direction it was moving the last time a command was entered.
EDIT:
time_t startTime, curTime;
time(&startTime);
do
{
input=getchar();
time(&curTime);
} while((curTime - startTime) < 1);
You could try using alarm() (or similar timer function) to throw and have your application catch a SIGALRM, though this is definitely overkill for PacMac. Consider using a separate thread (POSIX thread) to control a timer.
On Unix, you can simply use select or poll with a timeout on the standard input file descriptor (STDIN_FILENO, or fileno(stdin)). I would not bring in mouse traps built of signals and threads just for this.
My gut feeling tells me this:
Have one thread dedicated to processing user input and putting key events into a queue
The timer-activated thread, on every activation, consumes all key events in the queue, using the one that happened last, at the point of thread activation.
Make sure your access to the queue is synchronized.
// I/O Thread:
while (!stop) {
input = getchar();
lock_queue();
queue.push_back(input);
unlock_queue();
}
// Timer Thread:
while (!stop) {
lock_queue();
if (queue.size() == 0) {
action = DEFAULT_ACTION;
} else {
// either handle multiple key events somehow
// or use the last key event:
action = queue.back();
queue.clear();
}
unlock_queue();
perform_action(action);
sleep();
}
Full example posted as a Github Gist.
You could use a non-blocking input function such as getch() but it isn't very cross platform compatible.
Ideally you should be using events to update the game state, depending on which OS you are targeting you could use the OS events for key press or maybe a library such as SDL.
Related
I'm building a tetris game and I need the pieces to fall every x seconds; something like:
while(true){
moveDown();
sleep(x)
}
The problem is, I need to be able to move the pieces left and right in the meantime, i.e., call a function while it's sleeping.
How can I do that in c++?
Both time and key presses can be events which can be used to wait on. On UNIXes you'd use something like poll() with a suitable time for timeout and the input device used to recognize key presses. On other systems there are similar facilities (I'm a UNIX persons and I have never worked on Windows specific stuff although it seems the Windows facilities are actually more flexible). Depending on the result of poll() (timeout or activity on the I/O device in that case) you'd do the appropriate action.
This problem is solvable in multiple ways (another idea that comes to mind is multithreading, but that seems overkill). One approach would be to keep track of the number of "game cycles" and execute some function every n-th cycle like this:
for(int32_t count{1};;count++)
{
if (!count % 5)
{
// do something every 5th cycle
}
// do something every cycle
sleep(x);
}
you can measure how much time has passed since last fall and move piece down after given amount and then reset counter. In pseudo-code it could look like this:
while(true)
{
counter.update();
if(counter.value() == fall_period)
{
move_piece_down();
couter.reset();
}
// rotate pieces
}
If you are using typical implementation of game loop your counter can just accumulate elapsed time since last frame.
I build the following program in VS2017/Windows 10. When I run it, I hit close and ctrl_handler() is called as expected, but after ~three seconds the process is forcefully terminated anyway.
This is a problem because my real application writes large log files and three seconds is not long enough to get them onto disk.
Where is the documentation that describes this behaviour? Its not in those for the CTRL+CLOSE signal.
Where is the timeout set? Can it be modified at the application level? Or with a group policy?
#include <Windows.h>
bool mainThreadRunning;
bool mainThreadFinished;
BOOL ctrl_handler(DWORD event)
{
if (event == CTRL_CLOSE_EVENT) {
mainThreadRunning = false;
while (!mainThreadFinished) {
Sleep(100);
}
return TRUE;
}
return FALSE;
}
int main()
{
mainThreadRunning = true;
mainThreadFinished = false;
SetConsoleCtrlHandler((PHANDLER_ROUTINE)(ctrl_handler), TRUE); // make sure when the user hits the close button in the console we shut down cleanly
while (true)
{
}
return 0;
}
I suppose this is the reference you were looking for:
Unfortunately, this is determined by the OS. There is documentation describing the behavior in the HandlerRoutine Callback docs:
" In this case, no other handler functions are called, and the system displays a pop-up dialog box that asks the user whether to terminate the process. The system also displays this dialog box if the process does not respond within a certain time-out period (5 seconds for CTRL_CLOSE_EVENT, and 20 seconds for CTRL_LOGOFF_EVENT or CTRL_SHUTDOWN_EVENT)."
There is no (at least public, documented) API to change this timeout.
Note:
A process can use the SetProcessShutdownParameters function to prevent the system from displaying a dialog box to the user during logoff or shutdown. In this case,the system terminates the process when HandlerRoutine returns TRUE or when the time-out period elapses.
The operating system intentionally forces termination if it considers handler is taking too much time to complete.
Important note pulled from comments below:
... Ctrl+C is not subject to the time-out (I've tested it, and that's what I am using now).
I am trying to create a ping pong game and I need to say "If the user doesn't press space within 500 milliseconds, say 'You lost!' And if not, send the ball flying toward the other end."
The problem is that I can't use a function like Sleep() in if statements like:
if (Sleep(500)) {cout << "You lost!"}
Is there any time function that I could use in an if statement?
No.
You're programming at a lower level than a language with simple expressions to define rules like this.
Some ideas
Fundamentally, you're going to need to:
Set up a timer for 500ms
Set up a handler for keypresses
Have your timer expiry handler say "You lost" if a boolean flag is set, or otherwise "send the ball flying into the net".
Have your handler toggle that boolean flag if the keypress was Space
At a very basic level you could achieve this:
directly with two worker threads, or
crudely just by "waiting" for keypress activity (select, poll, epoll come to mind) with a timeout parameter (and ensure you don't reset the timeout if some other key were pressed instead), or
with help from your operating system, using e.g. a POSIX timer (though be cautious; this will send you into platform-specific, C-compatible land, which is probably not where you ultimately want to end up).
Usually, though, to do things "properly", we'd embed this logic into functionality provided by some engine implementing an "event loop" (particularly common in games, or in I/O-heavy applications).
Further information is going to take a book to explain adequately, and is thus not really appropriate in this medium. However, at least now you know which topics to explore in existing material.
Potentially interesting anecdote
I once wrote a little scripting language for my application, so that I could just define neat, high-level rules and the "compiler" would make use of my event loop to implement them. It made maintenance and expansion of my application really easy, which was important at the time because the rules became fairly complex and the application was broad in scope. It's not uncommon for computer game engines to embed scripting abilities of their own (say, using the "Lua" language), to achieve the same goal.
It's good to start thinking about your game design as a whole from the beginning as it helps solve problems like this in the design phase rather than further into development.
I'm not sure what library you're using but in sfml it would look something like this:
// Clock holds the current time.
// When we call Restart it returns the elapsed time since the last restart.
// With the current time and the start time we can find the elapsed time.
sf::Clock clock;
sf::Time maxTime = sf::milliseconds(500);
while(window.isOpen())
{
clock.Restart();
while(window.PollEvents(sf::Event e))
{
if(e.type == sf::Event::KeyPressed)
{
if(e.key.code == UP || e.key.code == DOWN || e.key.code == AnyOfOurOtherKeys)
{
maxTime = sf::milliseconds(500);
}
if(e.key.code == UP)
{
// Handle each individual key
}
if(e.key.code == DOWN)
{
// Handle each individual key
}
etc..
}
maxTime -= clock.Restart();
if(maxTime < sf::milliseconds(0))
{
std::cout << "Game Over, no input detected for 500 ms" << std::endl;
}
}
Regardless of library you can always use std::chrono::high_resolution_clock, std::chrono::milliseconds and std::chrono::duration to achieve the same results.
When you do your game loop you want to get the startLoopTime and then at the end of the game loop you want your endLoopTime. You can then do:
totalLoopTime = endLoopTime - startLoopTime;
maxTime -= totalLoopTime;
When you handle input you want a system where if a key that has a function is used, you set the maxTime back to 500ms. You could alternatively set a bool to true when a key is pressed and then run a check on the bool to restart maxTime.
I want my console application to stop and wait for the user to press a key. If there is no keyboard input after a limited time, let us say 2 seconds, execution should resume.
I know that no solution will be portable across all implementations, but are there at least simple solutions?
Some posts like this one or this one deal with similar issues, but they don’t seem to offer an answer to that specific problem.
If I could use ncurses (which I can't), I would do something like this using the getch() and timeout() functions:
#include <ncurses.h>
int main()
{
initscr(); // Start curses mode
cbreak(); // Turn off line buffering
timeout(2000); // Wait 2000 ms for user input
while (true) {
addstr("You have 2 sec. to press 'q', otherwise ...\n");
refresh();
int inKey = getch();
if (inKey == 'q') break;
}
endwin();
return 0;
}
One way of doing this would be to use low-level read() (assuming you are on Posix) coupled together with timeout signal. Since read() will exit with EINTR after signal processing, you'd know you reached the timeout.
I am using GetAsyncKeyState() in a simple pong game of mine to check if the user has pressed the arrow keys. I read online that you need to use this function a certain way however I found out that it is very CPU heavy (using 50% of my CPU!). This was rather disconcerting, however, after some playing around I found out that if I added a sleep(1); then the CPU usage went down to 0% and everything still worked fine. There must be a better way of using this function or at least a better way of lowering CPU usage.
Any help here would be much appreciated!
My Code:
while(true)
{
for(i = 8; i < 191; ++i)
{
if(GetAsyncKeyState(i) == -32767)
{
if(i == VK_LEFT)
// do stuff
else if(i == VK_RIGHT)
// do stuff
else if(i == VK_UP)
// do stuff
else if(i == VK_DOWN)
// do stuff
}
}
Sleep(1);
}
It's not that GetAsyncKeyState is CPU-heavy; it's that you're calling it all the time as quickly as you can. It's equivalent to saying that i++ is CPU-heavy when you run it in an infinite loop.
You either should use GetMessage to wait until you actually have input, or if that's not good enough (because you perhaps also want to update some animation without waiting for a message), then you should pick some polling interval and use something like MsgWaitForMultipleObjects (or create frequently recurring timer messages with SetTimer).
Anything your program repeats infinitely without some sort of message mechanism, user input or something that blocks program execution, will by default cause the program to use up all the available CPU resources.
Your original implementation of keyboard input handling was grossly inefficient. By blocking the execution flow with Sleep you have managed to overcome that problem, but in the cost of accuracy - if the user manages to press a key in less than 1ms your program will simply ignore it.
Instead of using GetAsyncKeyState for constantly checking the keys for presses, you can utilize SetWindowsHookEx to set a keyboard hook and directly intercept and handle keystrokes directly. These should help.
Other options are to use DirectInput, external input handling libraries or wrappers.