How to implement input without enter in C++ console game - c++

I am stuck with the input with no enter input.
I tried to use kbhit() + getch() from conio.h and it doesn't work on my system (Win10 and Ubuntu - unistd.h and termios.h). Program just skips block with these functions.
Then I used GetAsynkKeyState from windows.h. It works in the game (Level) though buggy, but doesn't in a Menu. Program as well skips (or something) block with input dispatch.
Menu input:
// The menu interface
bool Menu::SelectLevel() {
cout << "Select the level:" << endl;
size_t arrow_pos = 0;
// Prints level's names and char to exit the game
for (size_t i = 0; i <= _levels.size(); ++i) {
// Draw arrow before selected level
if (i == arrow_pos) {
cout << '>' << i + 1 << " - " << _levels[i].first[0] << endl;;
}
// Draw arrow before the exit select
else if (i == _levels.size() && i == arrow_pos) {
cout << '>' << "Exit" << endl;
}
// Draw the exit option
else if (i == _levels.size()) {
cout << ' ' << "Exit" << arrow_pos << endl;
}
// Draw levels list
else {
cout << ' ' << i + 1 << " - " << _levels[i].first[0] << endl;
}
}
// Input from keyboard TODO DOESN'T WORK!:
// If 's' pressed move arrow down
PoollingDelay(1);
if (GetAsyncKeyState(0x53) & 0x8000) {
++arrow_pos;
// If arrow reached top it goes to the bottom
if (arrow_pos == _levels.size() + 1) {
arrow_pos = 0;
}
}
// If 'w' pressed move arrow up
else if (GetAsyncKeyState(0x57) & 0x8000) {
--arrow_pos;
// If arrow reached bottom it goes to the top
if (arrow_pos == 65535) {
arrow_pos = _levels.size() + 1;
}
}
// If Return pressed
else if (GetAsyncKeyState(VK_RETURN) & 0x8000) {
// Don't think it would be worthy
if (arrow_pos < 1 || arrow_pos > _levels.size() - 1) {
throw runtime_error("Wrong select: " + to_string(arrow_pos));
}
// If player tired of this shit
if (arrow_pos == _levels.size() - 1) {
ClearTerminal();
return false;
}
// Play
PlayLevel(arrow_pos);
}
ClearTerminal();
return true;
}
Level input:
// TO DO DOESN'T WORK!:
void Level::ReadCommand() {
PoollingDelay(100);
if (GetAsyncKeyState(0x57)) {
Move(_NORTH);
}
else if (GetAsyncKeyState(0x41)) {
Move(_WEST);
}
else if (GetAsyncKeyState(0x53)) {
Move(_SOUTH);
}
else if (GetAsyncKeyState(0x44)) {
Move(_EAST);
}
else if (GetAsyncKeyState(0x45)) {
throw runtime_error(exit_the_lvl);
}
}

Short answer: you can't using only C++ and its standard library.
This is because the language is not designed to handle low-level hardware events. To do this, you need to rely on a separate library, dedicated to handle I/O. Lots of them, some are more or less easy to integrate. For simple games, SDL is nice.

Related

How to do: when we press and hold, program just do once?

I am making a game console with C++, I have a problem. When I press SPACE, my car in my game will jump. When I press and hold keyboard, my car will jump many times. I want: when I hold SPACE keyboard my car just jump once.
How to do this ?
I have read many topics about GetAsyncKeyState() but I don't know how to use it for my game.
if ( _kbhit() )
{
char key = _getch();
if ((key == 75) && (car.position.x > 2))
{
car.position.x -= 3;
}
else if ((key == 77) && (car.position.x < 24))
{
car.position.x += 3;
}
else if ((key == 32) && (car.position.y > 2))
{
car.position.y -= 5;
}
}
Below I have an example software of one possible way to 'filter' duplicate space chars out of an input stream.
The idea relies on the use of two threads.
Thrd1 reads from a stringstream called ssIn. (Replaced with cin in your code.)
Thrd1 (a filter) detects and discards back-to-back space chars, and only sends the first (of multiple space chars) to thrd2.
Thrd2 - reads from the single char buffer filled by thrd1 which will never see back-to-back space characters.
The 2 thrds are synchronized by a pair of semaphores (not mutex).
In my example, for my convenience, I used my version of a Posix semaphore. I do not know if you have Posix, but I am confident you will easily find many example C++ semaphores available on the web, even within SO, and most using only C++ features.
Note that this is only 1 test ... the alphabet with 1,000,000 spaces injected after 'j'. This is not a thourough test. There probably will be other issues to deal with. I have installed a harsh handling of input mis-behaviour. The assert will help you identify the issues.
"thrd2" represents your toe-hold into this example. Thrd2 receives the filtered stream.
#include "../../bag/src/dtb_chrono.hh"
using namespace std::chrono_literals; // support suffixes like 100ms, 2s, 30us
using std::chrono::duration_cast;
#include <iostream>
using std::cout, std::flush, std::endl;
//using std::cin;
#include <thread>
using std::thread, std::this_thread::sleep_for;
#include <string>
using std::string;
#include <sstream>
using std::stringstream;
// Posix Process Semaphore, local mode, unnamed, unlocked
#ifndef DTB_PPLSEM_HH
#include "../../bag/src/dtb_pplsem.hh"
using DTB::PPLSem_t;
#endif
// string ops
#ifndef DTB_SOPS_HH
#include "../../bag/src/dtb_sops.hh"
using DTB::SOps_t;
#endif
#include <cassert>
namespace DTB
{
class T946_t
{
public:
int operator()(int argc, char* argv[]) // functor entry
{ return exec(argc, argv); }
private:
// uses compiler provided default ctor and dtor
// Posix Process Semaphore, local mode (unnamed, unshared)
// initial value unlocked
PPLSem_t th1Sem;
PPLSem_t th2Sem;
char kar = '\n';
bool done = false;
size_t m_rdy;
thread* th1;
string th1Log;
thread* th2;
string th2Log;
stringstream ssIn; // debug - replaces cin
stringstream ss1DR; // th1 delay'd report
stringstream ss2DR; // th2 delay'd report
// utilities
SOps_t sops; // string ops - digiComma
int exec(int , char** )
{
// test init: insert a possible user input into ssIn
init_ssIn();
int retVal = 0;
Time_t start_ns = HRClk_t::now();
th1Sem.lock(); // block until threads are ready
th2Sem.lock(); // block
// start ---------vvvvvvvvvvvvvvvvvvv
th1 = new thread(&T946_t::thrd1, this);
assert(nullptr != th1);
while (0 == (m_rdy & 0x01))
std::this_thread::sleep_for(10ms);
// start ---------vvvvvvvvvvvvvvvvvv
th2 = new thread(&T946_t::thrd2, this);
assert(nullptr != th2);
while (0 == (m_rdy & 0x02))
std::this_thread::sleep_for(10ms);
th1Sem.unlock();
// spin wait for threads to complete
while (!done)
{
std::this_thread::sleep_for(100ms);
}
th1->join();
th2->join();
cout << "\n join()'s complete";
auto duration_ns = duration_cast<NS_t>(HRClk_t::now() - start_ns).count();
cout << "\n T901_t::exec() duration "
<< sops.digiComma(duration_ns) << " ns" << endl;
// output the delay'd reports
cout << ss1DR.str() << ss2DR.str() << endl;
return retVal;
}
void init_ssIn()
{
ssIn << "abcdefghij";
for (int i=0; i<1000001; ++i) ssIn << ' ';
std::string::size_type k = ssIn.str().size();
ssIn << "klmnopqrstuvwxyz";
// a..j
cout << "\n ssIn: '" << ssIn.str().substr(0, 10)
<< " ...spaces... " << ssIn.str().substr(k, 16) << "'"
<< "\n ssIn.str().size(): "
<< sops.digiComma(ssIn.str().size()) << endl;
}
void thrd1()
{
uint64_t th1Count = 0;
uint64_t th1Skips = 0;
char lkar = '\0';
m_rdy |= 0x01; // sync msg to main
do {
getNextKar(lkar); // read from input (ssIn or cin)
th1Sem.lock(); // wait for thrd2 to give permission
{
if(' ' == lkar) // current input kar
{
if(' ' == kar) // previous kar
{
// filter out back-to-back space chars
th1Skips += 1;
th1Sem.unlock(); // skip the handshake, no char to send,
// give self permission-to-proceed
continue;
}
}
// else, not a duplicate space
th1Count += 1;
kar = lkar; // write to input of thrd2
th1Log += lkar; // log
lkar = ' ';
}
th2Sem.unlock(); // give thrd2 permission-to-proceed
if (ssIn.eof())
{
done = true;
break;
}
}while(!done);
ss1DR
<< "\n th1Count " << sops.digiComma(th1Count)
<< "\n th1Skips " << sops.digiComma(th1Skips)
<< "\n th1Log " << th1Log
<< "\n thrd1 exit " << endl;
}
// read from ssIn for development
// read from cin for app
void getNextKar(char& lkar)
{
// ssIn >> lkar; // reads 1 char, but skips multiple blank chars
// lkar = ssIn.get(); returns an integer (not a char)
(void)ssIn.get (lkar);
if(ssIn.fail())
{
if(ssIn.eof()) return; // not a fail
assert(0); // harsh exit, might want something gentler
}
}
void thrd2()
{
uint64_t th2Count = 0;
m_rdy |= 0x02; // sync msg to main
do {
th2Sem.lock(); // wait for thrd1 to give permission
char t = kar;
th1Sem.unlock(); // give permission-to-proceed to thrd1
// simulate application - no duplicate spaces from input
th2Log += t;
th2Count += 1;
// end of sim
}while(!done);
ss2DR
<< "\n th2Count " << sops.digiComma(th2Count)
<< "\n th2Log " << th2Log
<< "\n thrd2 exit " << endl;
}
}; // class T946_t
} // namespace DTB
int main(int argc, char* argv[]) { return DTB::T946_t()(argc, argv); }
The output looks like:
ssIn: 'abcdefghij ...spaces... klmnopqrstuvwxyz'
ssIn.str().size(): 1,000,027
join()'s complete
T901_t::exec() duration 120,421,582 ns
th1Count 28
th1Skips 1,000,000
th1Log abcdefghij klmnopqrstuvwxyz
thrd1 exit
th2Count 28
th2Log abcdefghij klmnopqrstuvwxyz
thrd2 exit
The duration is 120 ms for 1 Million chars input.
As #Remy Lebeau pointed out you can get the repeat count by install WH_KEYBOARD hook and filter the key held pressed in KeyboardProc.
Of course, for simple, no need install a hook you can filter repeat WM_KEYDOWN messages in window procedure when you press the space key and hold. The following is an example you can refer to:
case WM_KEYDOWN:
if (wParam == VK_SPACE)
{
if (!((HIWORD(lParam) & 0x4000) || (HIWORD(lParam) & 0x8000)))
{
isKeyHold = TRUE; // First time pressed
OutputDebugString(TEXT("pressed !\n"));
}
else if (isKeyHold && (HIWORD(lParam) & 0x4000))
{
OutputDebugString(TEXT("hold !\n"));
return 1; // Don't handle the message when the key is pressed and held.
}
}
break;
case WM_KEYUP:
if (wParam == VK_SPACE && isKeyHold)
{
isKeyHold = FALSE; // Clear the isKeyHold flag when release the key.
OutputDebugString(TEXT("release !\n"));
}
break;

Scrolling Menu is Not Working in c++

This is the code for my scrolling menu where it display different values based on the 3 vectors, welcomeMenuVector, menuOptionsVector and menuActionVector:
welcomeMenuVector - Save function of title
menuOptionsVector - Save menu options of scrolling menu
menuActionsVector - Save the functions to perform after the menu option is selected
vector<string> menuOptionsVector;
vector<function<void()>> menuActionsVector;
vector<function<void()>> welcomeMenuVector;
void scrollingMenu(){
ShowConsoleCursor(false);
int pointer = 0;
while (true){
system("cls");
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 15);
welcomeMenuVector[0]();
for (unsigned int i = 0; i < menuOptionsVector.size(); ++i){
if (i == pointer){
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 14);
cout << center("> " + menuOptionsVector[i] + " <" ,getWindowWidth()) <<endl;
}
else{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 15);
cout << center(" " + menuOptionsVector[i] + " ", getWindowWidth()) << endl;
}
}
while (true){
if (GetAsyncKeyState(VK_UP) != 0){
pointer -= 1;
if (pointer == -1){
pointer = menuOptionsVector.size() - 1;
}
break;
}
else if (GetAsyncKeyState(VK_DOWN) != 0){
pointer += 1;
if (pointer == menuOptionsVector.size()){
pointer = 0;
}
break;
}
else if (GetAsyncKeyState(VK_RETURN) != 0){ //when enter key is pressed
menuActionsVector[pointer]();
}
}
Sleep(100);
}
}
I will need to enter menu title, menu options and menu functions into vector before I need to call the scrollingMenu(). Hence, I will have to clear all the 3 vectors before entering values into vectors because there will be values inside as it might be used by another menu earlier.
This is my main page, user page and add user page:
void mainPage(){
clearMenuVectors();
welcomeMenuVector = { [](){mainPageWelcome(); } };
menuOptionsVector = { "Login", "Exit" };
menuActionsVector = {
[](){ userPage(); },
[](){ exit(0); }
};
scrollingMenu();
}
void userPage(){
clearMenuVectors();
welcomeMenuVector = { [](){userPageWelcome(); } };
menuOptionsVector = { "Add User", "Update User", "Delete User", "Exit" };
menuActionsVector = {
[](){ addUsers(); },
[](){ updateUsers(); },
[](){ deleteUsers(); },
[](){ exit(0); }
};
scrollingMenu();
}
void addUsers(){
clearMenuVectors();
welcomeMenuVector = { [](){cout << center("Which type of user would you like to add?", getWindowWidth()) << endl;; } };
menuOptionsVector = { "Add Admin", "Add Scheduler", "Add Professional", "Exit" };
menuActionsVector = {
[](){ /*addUser();*/ },
[](){ /*updateUsers();*/ },
[](){ /*deleteUsers();*/ },
[](){ exit(0); }
};
}
My main page works fine at first, I'm able to move up & down, after hitting enter on the Login menu option, it goes to the userPage, but here comes the problem, when I'm in the userPage, I can still see the menu options of userPage, but when I just move up/down it will then changed the menu options of addUsers (I haven't even hit the enter), even if I dont move up/down I hit enter on Add User option, nothing happens, I'm not sure what is the problem here.
If you change
[](){ /*addUser();*/ },
to
{ cout << "add user option 1" << endl; },
and it writes it continuously then the key is pressed.
GetAsyncKeyState
whether the key is currently up or down. If the most significant bit
is set, the key is down, and if the least significant bit is set, the
key was pressed after the previous call to GetAsyncKeyState.
So you need to check the least significant bit. In this case
if (GetAsyncKeyState(VK_RETURN) != 0)
should be
if (GetAsyncKeyState(VK_RETURN) & 0x1)

Sfml Text Entered arrow keys

I am making console style game in sfml. It's graphical interface but game itself contains console. I'm using Text Entered event for writing things in console but i also want to move the cursor (console cursor not mouse) with arrow keys. To be more specific in text entered event i am using event.text.unicode for key value. But arrow keys unicode values not working in my pc. I used 37 for left arrow, 39 for right arrow but it doesn't work. This is my console's key process function.
void Console::update(int unicode) {
if(unicode == 8) { // Backspace
deleteLast();
} else if(unicode == 13) { // Enter
newLine();
} else if(unicode == 37) { // Left arrow key
currentX--;
std::cout << "Left arrow" << std::endl;
} else if(unicode == 39) { // Right arrow key
currentX++;
std::cout << "Right arrow" << std::endl;
} else { // Normal characters
buffer[(currentY == 0) ? currentX : (currentY * maxColumn) + currentX] = (char)unicode;
currentX++;
if(currentX == maxColumn - 1) {
newLine();
}
std::cout << "[KEY TYPED] X: " << currentX << " Y: " << currentY << std::endl;
}
}
Thanks in advance :)
Edit: I think i solved. I used KeyPressed event for handling special keys. And TextEntered for handling normal characters.
For those having the same problem:
From an Event type variable, first check if its a keyboard event
if(event.type == sf::Event::KeyPressed)
Then check if its an arrow:
if(event.key.code == sf::Keyboard::Up)
//do stuff
if(event.key.code == sf::Keyboard::Down)
//do stuff

Why does this code execute two branches of if...else in VS Debug mode

I've met some problem in debug some code in vs2013/2015/2008.
#include <iostream>
using namespace std;
int main()
{
int mode = 1;
int size = 1;
if (mode == 0)
{
cout << "mode = 0" << endl;
}
else
{
if (mode == 1)
{
if (size > 0)
{
cout << "mode=1,size=1" << endl; //in debug mode will execute this sentence
}
else
return 0; //however,when I Step in (F10),this will also be executed but have no influence.But When I insert a breakpoint in there and press F5, will not break in there
}
else if (mode == 2)
{
if (size > 0)
{
cout << "mode=2,size=1" << endl;
}
else
return 0;
}
}
return 1;}
So, how to explain this phenomenon? Does it have any problem? And how can I solve it?
This is how the debugger works in VS 2015. Ignore it.
return 0; isn't executed, even if it looks like it. Weird behavior, I was surprised by that too.
They should have made the arrow go to the else statement, not the first line after it, but for some reason they didn't.
Just ignore it. You can make a feature request if you want too :)

destroyWindow (from opencv) closes all windows and stops c++ program

I'm writing an live video processing program in c++, and want to be able to toggle three windows with the same mjpeg stream, in color, grayscale, and monochrome. I have all the image feeds running, but, since my screen is small, I want to be able to toggle them on and off individually. To do this, I have written the code below, but calling destroyWindow("[windowname]"); stops the whole program, instead. I've already read the documentation, and putting void in front of it doesn't help. Can anybody tell me what I'm doing wrong?
Here's the code (it's in an infinite loop, until the break you see below is called):
imshow("Color", imageColor);
imshow("Monochrome", imageMonochrome);
imshow("Grayscale", imageGrayscale);
int keyPressed = waitKey(0);
if (keyPressed > 0)
{
cout << keyPressed;
cout << "key was pressed\n";
// Press C to toggle color window
if (99 == keyPressed)
{
if (colorOpen)
{
cout << "Color window closed\n";
void destroyWindow("Color");
colorOpen = false;
}
if (!colorOpen)
{
cout << "Color window opened\n";
imshow("Color", imageColor);
colorOpen = true;
}
}
// Press M to toggle monochrome window
if (109 == keyPressed)
{
if (monochromeOpen)
{
cout << "Monochrome window closed\n";
void destroyWindow("Monochrome");
monochromeOpen = false;
}
if (!monochromeOpen)
{
cout << "Monochrome window opened\n";
imshow("Monochrome", imagebw);
monochromeOpen = true;
}
}
// Press G to toggle grayscale window
if (103 == keyPressed)
{
if (grayscaleOpen)
{
cout << "Grayscale window closed\n";
void destroyWindow("Grayscale");
grayscaleOpen = false;
}
if (!grayscaleOpen)
{
cout << "Grayscale window opened\n";
imshow("Grayscale", image);
grayscaleOpen = true;
}
}
// Break out of infinite loop when [ESC] is pressed:
if (27 == keyPressed)
{
cout << "Escape Pressed\n";
break;
}
}
The code you pasted terminates after calling destroyWindow (by running off the end of main). If that's not what you want to happen, write code that does something else after calling destroyWindow. Perhaps you want a loop?