std::cout prints extra characters in my keyPressed string, probably because of "\r", for example, if keyPressed = "Right arrow", when I press the up arrow, it prints "keyPressed = Up arrowoww", then, when I press right arrow again, it prints "keyPressed = Right arrow" normally again, but if I press any arrow key except "Right arrow" it prints some unwanted extra characters at the end
Error example
Source code:
game.cpp
#include "engine.h"
#include <iomanip>
Engine eng;
int main() {
while (eng.isRunning) {
eng.getInput();
std::cout << std::setw(5);
std::cout << "\r X = " << eng.playerX;
std::cout << "| Y = " << eng.playerY;
std::cout << "| KEY = " << eng.keyPressed;
Sleep(100);
}
return 0;
}
engine.h
#ifndef ENGINE_H
#define ENGINE_H
#include <iostream>
#include <Windows.h>
#include <string>
class Engine {
public:
// Game
bool isRunning = true;
bool gettingInput = true;
// Player
int playerX = 1;
int playerY = 1;
char playerModel = 'P';
// Test / Debug
std::string keyPressed;
// Functions
char getInput() {
// Gets arrow keys states
while (this->gettingInput) {
this->keyPressed = "";
if (GetAsyncKeyState(VK_RIGHT)) {
// Right arrow key
this->playerX++;
this->keyPressed = "Right arrow";
break;
}
else if (GetAsyncKeyState(VK_LEFT)) {
// Left arrow key
this->playerX--;
this->keyPressed = "Left arrow";
break;
}
else if (GetAsyncKeyState(VK_UP)) {
// Up arrow key
this->playerY++;
this->keyPressed = "Up arrow";
break;
}
else if (GetAsyncKeyState(VK_DOWN)) {
// Down arrow key
this->playerY--;
this->keyPressed = "Down arrow";
break;
}
else if (GetAsyncKeyState(VK_END)) {
exit(0);
}
Sleep(255);
}
}
};
#endif
Best / easiest way to fix this?
I searched and tested for 3 days but didn't find anything, please, help me.
Since you're overwriting the previous output, when you print a shorter string the extra characters from the previous output are still displayed. Replace the \r with a \n to see what's actually being output.
You can output some spaces after your key name to overwrite those extra characters with spaces and erase them.
After looking over your provided code I do see a few issues or concerns with the code design: I'll break it down and explain some of the things that I see that could improve the quality of your code. I will start with your main.cpp then move on to your Engine class.
You initially have this:
#include "engine.h"
#include <iomanip>
Engine eng;
int main() {
while (eng.isRunning) {
eng.getInput();
std::cout << std::setw(5);
std::cout << "\r X = " << eng.playerX;
std::cout << "| Y = " << eng.playerY;
std::cout << "| KEY = " << eng.keyPressed;
Sleep(100);
}
return 0;
}
The first main issue that I see is that you have declared Engine eng at the global level. We can fix this by
#include "engine.h"
#include <iostream>
#include <iomanip>
int main() {
Engine eng; // declare it here as the first object in main; now it has local
// scope within main's function and is now in Automatic Storage
// instead of Global Storage.
while( ... ) {
// ....
}
return 0;
};
The next issue starts with while loop's conditional expression in the main function.
You currently have:
while( engine.isRunning ) { //... }
This is okay but this is more of an issue with your Engine class's design. Here you are providing a public member that anyone can access. So let's look at your class declaration/definition; you currently have:
#ifndef ENGINE_H
#define ENGINE_H
#include <iostream>
#include <Windows.h>
#include <string>
class Engine {
public:
// Game
bool isRunning = true;
bool gettingInput = true;
// Player
int playerX = 1;
int playerY = 1;
char playerModel = 'P';
// Test / Debug
std::string keyPressed;
// Functions
char getInput() { // ... }
};
#endif
Here you should protect your data members and have access modifiers functions to them:
#ifndef ENGINE_H
#define ENGINE_H
#include <iostream>
#include <Windows.h>
#include <string>
class Engine {
private:
bool isRunning;
bool gettingInput;
// Player
int playerX;
int playerY;
char playerModel;
// Test / Debug
std::string keyPressed;
public:
Engine() :
isRunning( false ),
isGettingInput( false ),
playerX( 1 ),
playerY( 1 ),
playerModel( 'P' )
{}
void run() { isRunning = true; // set or call other things here... }
// Since we protected our members variables by making them private,
// we now need some access functions to retrieve and modify them.
bool isActive() const { return isRunning; } // make this const so it doesn't change anything
void toggleIsActive() { isRunning = !isRunning; }
bool retrievingInput() const { return isGettingInput; }
void toggleRetrievingInput() { isGettingInput = !isGettingInput; }
int getPlayerX() const { return playerX; }
void setPlayerX( int newX ) { playerX = newX; }
int getPlayerY() const { return playerY; }
void setPlayerY( int newY ) { playerY = newY; }
// set both in one function call
void setPlayerPosition( int newX, int newY ) {
playerX = newX;
playerY = newY;
}
char getPlayerModel() const { return playerModel; }
// don't know if you want to change this: uncomment if you do
// void setPlayerModel( char c ) { playerModel = c; }
std::string& getPressedKey() const { return keyPressed; }
char getInput() { // ... }
};
This should fix the interface design of your class. The only major difference here is that I had set your Boolean member variables to false by default because typically when you first start an Engine it is currently not already running. So to fix this we can call a public run function that will trigger this. So main would look like this instead:
int main () {
Engine eng;
eng.run(); // this now starts the engine sets the flag to true
while (...) { //... }
return 0;
}
However, I have also seen few concerns in your Engine's getInput() function, so let's take a look at it.
char getInput() {
// Gets arrow keys states
while (this->gettingInput) {
this->keyPressed = "";
if (GetAsyncKeyState(VK_RIGHT)) {
// Right arrow key
this->playerX++;
this->keyPressed = "Right arrow";
break;
}
else if (GetAsyncKeyState(VK_LEFT)) {
// Left arrow key
this->playerX--;
this->keyPressed = "Left arrow";
break;
}
else if (GetAsyncKeyState(VK_UP)) {
// Up arrow key
this->playerY++;
this->keyPressed = "Up arrow";
break;
}
else if (GetAsyncKeyState(VK_DOWN)) {
// Down arrow key
this->playerY--;
this->keyPressed = "Down arrow";
break;
}
else if (GetAsyncKeyState(VK_END)) {
exit(0);
}
Sleep(255);
}
}
The first part is the while loop's condition statement and your class's member. Originally you have this set to true by default, yet no where in the code did I see this value being updated. We don't need to change this but the fix is simple now that we have a way to change this member through a public interface call. Since I have made your isGettingInput false by default; you can now set this within this function before you enter the while loop. The only last issue that I see is that when this function is called back in main's while loop; this function never return's a value and the return value is never being used.
As to your actual problem with your bug for cout user : 1201programalarm has pretty much already answered that for you. Just thought I'd help you out a little bit more with your code.
Related
I'm trying to write a "conditions" class to check if a given condition returns true or false, in an rpg game.
Conditions.h
#pragma once
#include <functional>
#include <vector>
#include <iostream>
class Conditions
{
public:
bool check(int i);
void initialize();
private:
std::vector<std::function<bool()>> functions;
};
Conditions.cpp
bool Conditions::check(int i)
{
if (i >= functions.size())
{
std::cout << "Conditions::functions's size is " << functions.size() << " but you've tried to enter: " << i << std::endl;
return false;
}
else
{
return functions[i]();
}
}
void Conditions::initialize()
{
//Here I want to initialize all the conditions manually and push them
//into functions member variable.
}
the question is , how can I create this functions without creating new member functions for each of them(there will be most likely more than 200 functions)
is it possible to write something like:
functions.push_back(
{
if(GameInfo::player.gold>200) return true;
else return false;
}
);
You are probably looking for lambdas:
functions.push_back([this]
{
if (GameInfo::player.gold > 200) return true;
else return false;
});
If you don't actually need to access any members of Conditions in the function, then you can also remove the this capture entirely:
functions.push_back([]
{
if (GameInfo::player.gold > 200) return true;
else return false;
});
By the way, your specific function example can be extremely simplified like this:
functions.push_back([]
{
return GameInfo::player.gold > 200;
});
Thank you for reading this thread. I am a beginning programmer and am trying to make a simple snake game with C++. It isn't finished yet, but I think I got a nice start to it. However, when I try to run the program it instantly crashes. (The compiler says there are 0 warnings and errors. I am using the Code::Blocks IDE. Does anyone know why my program isn't working? I think it may have something to do with the "vector coordHistory", but I can't tell for sure. At least that is the last thing I added to the program.
This is my code:
#include <iostream>
#include <cstdlib>
#include <string>
#include <cstdio>
#include <windows.h>
#include <conio.h>
#include <vector>
#define MAXX 156 //Number of columns that fits on my screen
#define MAXY 62 //Number of rows that fits on my screen
using namespace std;
// This function clears the console window
void clearConsole()
{
system("cls"); //empties console window
};
// This function returns the x position of the cursor
int getcursorX()
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
if(GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
return csbi.dwCursorPosition.X;
}
};
// This function returns the y position of the cursor
int getcursorY()
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
if(GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
return csbi.dwCursorPosition.Y;
}
};
// This function sets the x position of the cursor
void setcursorX(int x)
{
COORD coord = {x, getcursorY()};
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}
// This function sets the y position of the cursor
void setcursorY(int y)
{
COORD coord = {getcursorX(), y};
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}
// The snake class contains the coordinates of the snake and direction in which it is moving
class Snake
{
private:
bool isAlive;
int snakexCoord;
int snakeyCoord;
char snakeDirection;
int snakeLength;
public:
//getters
int getsnakexCoord() { return snakexCoord; };
int getsnakeyCoord() { return snakeyCoord; };
char getsnakeDirection() { return snakeDirection; };
bool getisAlive() { return isAlive; };
int getsnakeLength() { return snakeLength; };
//setters
void setsnakexCoord(int newsnakexCoord) { snakexCoord = newsnakexCoord;};
void setsnakeyCoord(int newsnakeyCoord) { snakeyCoord = newsnakeyCoord;};
void setsnakeDirection(char newsnakeDirection) { snakeDirection = newsnakeDirection;};
void setisAlive(bool newisAlive) { isAlive = newisAlive; };
void setsnakeLength(int newsnakeLength) { snakeLength = newsnakeLength; };
//constructor
Snake()
{
snakexCoord = MAXX / 2;
snakeyCoord = MAXY / 2;
snakeDirection = 'E';
isAlive = true;
snakeLength = 1;
};
//destructor
~Snake(){};
};
int main()
{
int i; //iterator
system("mode 650"); //makes console window full-screen
Snake snake; //initializes Snake object snake
char c; //char that stores user input to change snake direction
vector<int[2]> coordHistory; //vector of arrays that stores previous locations of snake
while (snake.getisAlive())
{
//Adds snake coordinates to coordHistory
coordHistory[coordHistory.size()][0] = snake.getsnakexCoord();
coordHistory[coordHistory.size()-1][1] = snake.getsnakeyCoord();
//Iterates backwards through coordHistory and draws an "O" until the snake is as long as it should be
for(i = coordHistory.size() - 1; i > coordHistory.size() - 1 - snake.getsnakeLength(); i--)
{
setcursorX(coordHistory[i][0]);
setcursorY(coordHistory[i][1]);
cout << "O";
}
//Allows user to change snake direction
c = _getch();
switch (c){
case 'w':
snake.setsnakeDirection('N');
break;
case 'd':
snake.setsnakeDirection('E');
break;
case 's':
snake.setsnakeDirection('S');
break;
case 'a':
snake.setsnakeDirection('W');
break;
}
//Checks in which direction snake is going and changes coordinates accordingly
switch (snake.getsnakeDirection())
{
case 'N':
snake.setsnakeyCoord(snake.getsnakeyCoord()-1);
break;
case 'E':
snake.setsnakexCoord(snake.getsnakexCoord()+1);
break;
case 'S':
snake.setsnakeyCoord(snake.getsnakeyCoord()+1);
break;
case 'W':
snake.setsnakexCoord(snake.getsnakexCoord()-1);
break;
}
//Checks if snake goes out of boundaries
if ((snake.getsnakexCoord() > MAXX) || (snake.getsnakexCoord() < 0) || (snake.getsnakeyCoord() > MAXY) || (snake.getsnakeyCoord() < 0))
{
snake.setisAlive(false);
}
//Sleep(200); Ignore WIP
clearConsole();
}
return 0;
}
Your vector usage is wrong.
coordHistory[coordHistory.size()][0] = snake.getsnakexCoord();
coordHistory[coordHistory.size()-1][1] = snake.getsnakeyCoord();
You appear to assume that vector[n-1] will automatically create as many elements as needed to result in a vector of size n. It doesn't.
You are twice accessing the first element of vector coordHistory, a vector which is actually empty.
To add elements, in general use the member function push_back or emplace_back. You're really going to struggle with a vector of arrays, though, since arrays are neither assignable nor copyable. That's why I can't use push_back here; I have to explicitly resize the vector then access the newly-created elements as you were doing before:
coordHistory.resize(1);
coordHistory[0][0] = snake.getsnakexCoord();
coordHistory[0][1] = snake.getsnakeyCoord();
This is pretty awkward. why don't you instead store a nice delicious class type with x and y members?
std::vector<deliciousClassType> coordHistory;
coordHistory.emplace_back(
snake.getsnakexCoord(),
snake.getsnakeyCoord()
);
Me and my friend are making a text based game in c++ for fun, and to learn a little more. I have been trying to use pointers to classes, but am having no luck, and some errors are occurring which make absolutely no sense to me at all, and am hoping someone can help me.
Code:
//Map.h
#include "Player.h"
class Map
{
//Virtual functions
};
class StartMap : public Map
{
//Code
}Start;
class JungleMap : public Map
{
//Code
}Jungle;
class RiverMap : public Map
{
//Code
}River;
//Player.h
#ifndef MAP_H
#define MAP_H
#endif
class Player
{
private:
Map *PlayerMap;
//Other variables
public:
void Initialize()
{
//Initialize variables
PlayerMap = &Start; //This is where the error occurs, says there's a
//<error-type>*Player::PlayerMap. Tried putting
//this->PlayerMap = &Start, didn't help
//There's no error when I make the pointer
}
//Bunch of other functions
}Player;
Okay, here's my code since I decided to add .cpp files:
//Command.h
class Command
{
private:
string GameCommand;
void Trim();
public:
Command (string command) {GameCommand = command;}
Command () {}
void operator = (string command) {GameCommand = command;}
void ReadCommand();
string Print();
}
//Command.cpp
#include <iostream>
#include <string>
#include "Command.h"
#include "Parameter.h"
using namespace std;
void Command::Trim()
{
int LeadingPos = 0, MidCount = 0, TrailingPos = GameCommand.length()-1, Size = 0;
string TempCommand = "";
while (GameCommand[LeadingPos] == ' '){LeadingPos += 1;}
while (GameCommand[TrailingPos] == ' '){TrailingPos -= 1;}
Size = ((TrailingPos+1)-LeadingPos);
for (int loops = 0; loops < Size; loops++)
{
if (MidCount > 0 && GameCommand[LeadingPos] == ' ')
{
LeadingPos += 1;
}
else
{
if (GameCommand[LeadingPos] == ' ')
{
MidCount += 1;
}
TempCommand += GameCommand[LeadingPos];
LeadingPos += 1;
}
}
GameCommand = TempCommand;
}
void Command::ReadCommand()
{
Trim();
string Parameter;
if (GameCommand.substr(0,3) == "go ")
{
Parameter = GameCommand.substr(3,string::npos);
CommandParameter.Go(Parameter);
}
else if (GameCommand.substr(0,4) == "dig ")
{
Parameter = GameCommand.substr(4,string::npos);
CommandParameter.Dig(Parameter);
}
else if (GameCommand.substr(0,4) == "eat ")
{
Parameter = GameCommand.substr(4,string::npos);
CommandParameter.Eat(Parameter);
}
else if (GameCommand.substr(0,4) == "exit" || GameCommand.substr(0,4) == "quit")
{
exit(0);
}
else if (GameCommand.substr(0,4) == "use ")
{
Parameter = GameCommand.substr(4,string::npos);
CommandParameter.Use(Parameter);
}
else if (GameCommand.substr(0,5) == "drop ")
{
Parameter = GameCommand.substr(5,string::npos);
CommandParameter.Drop(Parameter);
}
else if (GameCommand.substr(0,5) == "grab " || GameCommand.substr(0,5) == "take ")
{
Parameter = GameCommand.substr(5,string::npos);
CommandParameter.Pickup(Parameter);
}
else if (GameCommand.substr(0,5) == "help ")
{
Parameter = GameCommand.substr(5,string::npos);
CommandParameter.Help(Parameter);
}
else if (GameCommand.substr(0,5) == "look ")
{
Parameter = GameCommand.substr(5,string::npos);
CommandParameter.Look(Parameter);
}
else if (GameCommand.substr(0,5) == "sleep")
{
CommandParameter.Sleep();
}
else if (GameCommand.substr(0,6) == "check ")
{
Parameter = GameCommand.substr(6,string::npos);
CommandParameter.Check(Parameter);
}
else if (GameCommand.substr(0,6) == "climb ")
{
Parameter = GameCommand.substr(6,string::npos);
CommandParameter.Climb(Parameter);
}
else if (GameCommand.substr(0,6) == "throw ")
{
Parameter = GameCommand.substr(6,string::npos);
CommandParameter.Throw(Parameter);
}
else if (GameCommand.substr(0,7) == "attack ")
{
Parameter = GameCommand.substr(7,string::npos);
CommandParameter.Attack(Parameter);
}
else if (GameCommand.substr(0,7) == "search ")
{
Parameter = GameCommand.substr(7,string::npos);
CommandParameter.Search(Parameter);
}
else
{
cout << "Not a valid command.\n";
}
}
string Print()
{
return GameCommand;
}
The string GameCommand is what's not working.
class StartMap : public Map;
is syntactically incorrect. You need
class StartMap : public Map
{
// Details of class
} Start;
You need to make similar changes to JungleMap and RiverMap.
First thing I noticed was the semi-colon after each inheritance declaration..
class XXXXX : public Map; <-- that semi-colon shouldn't be there..
In the initialize function, I'm fairly certain you mean PlayerMap = new StartMap();
You'll need a destructor to delete it and a copy, move constructor as well as an assignment operator in order to assign, move or copy the class.
You can follow this to make the class conform to RAII: What is the copy-and-swap idiom?
There are lots of problems with your code layout.
This doesn't do anything:
//Player.h
#ifndef MAP_H
#define MAP_H
#endif
I guess you are trying to do an include guard. The proper layout is:
#ifndef PLAYER_H
#define PLAYER_H
// all your code for the header file goes here
class Player
{
// ....
};
#endif // no more code after this line
The next issue is that Player.h should include Map.h, not the other way around. Imagine you are the compiler. You are processing Player.h. You get down as far as Map *PlayerMap; . But you don't know what Map is because you haven't seen Map.h yet. So you have to give an error and stop compiling.
The map definitions in Map.h should look like:
class StartMap : public Map
{
//Code
};
The Start; you had on the end is poor style. It would cause undefined behaviour if two different .cpp files included Map.h because there would be two different global variables with the same name.
Moving onto the void Map::Initialize() function. You're supposed to use the constructor for initialization. Either way, my suggestion is that you don't implement this in Player.h. Instead, just have void Initialize();, and then in Map.cpp you could have:
// the global variables
StartMap start_map;
JungleMap jungle_map;
void Map::Initialize()
{
player_map = &start_map;
}
It's a good idea to use a different naming convention for classes than for variables. So that when someone sees StartMap for example, they know immediately whether it is a class name or a variable name.
I'm implementing a simple priority queue in C++.
However when it runs, it prints out gibberish numbers.
Am I somehow trying to access invalid entries in the array in my code?
Below is the code.
Also, is my "remove" function somehow not doing its job? Conceptually, shall I be putting null into the first entry and return whatever was just erased?
Thanks.
[Priority.h]
#ifndef Priority_h
#define Priority_h
class Priority
{
public:
Priority(void);
Priority(int s);
~Priority(void);
void insert(long value);
long remove();
long peekMin();
bool isEmpty();
bool isFull();
int maxSize;
long queArray [5];
int nItems;
private:
};
#endif
[Priority.cpp]
#include <iostream>
#include <string>
#include <sstream>
#include <stack>
#include "Priority.h"
using namespace std;
Priority::Priority(void)
{
}
Priority::Priority(int s)
{
nItems = 0;
}
Priority::~Priority(void)
{
}
void Priority::insert(long item)
{
int j;
if(nItems==0) // if no items,
{
queArray[0] = item; nItems++;
}// insert at 0
else // if items,
{
for(j=nItems-1; j=0; j--) // start at end,
{
if( item > queArray[j] ) // if new item larger,
queArray[j+1] = queArray[j]; // shift upward
else // if smaller,
break; // done shifting
} // end for
queArray[j+1] = item; // insert it
nItems++;
} // end else (nItems > 0)
}
long Priority::remove()
{
return queArray[0];
}
long Priority::peekMin()
{
return queArray[nItems-1];
}
bool Priority::isEmpty()
{
return (nItems==0);
}
bool Priority::isFull()
{
return (nItems == maxSize);
}
int main ()
{
Priority thePQ;
thePQ.insert(30);
thePQ.insert(50);
thePQ.insert(10);
thePQ.insert(40);
thePQ.insert(20);
while( !thePQ.isEmpty() )
{
long item = thePQ.remove();
cout << item << " "; // 10, 20, 30, 40, 50
} // end while
cout << "" << endl;
system("pause");
}
Here is one error:
for(j=nItems-1; j=0; j--) // start at end,
^ this is assignment, not comparison.
I am also not convinced that there isn't an off-by-one error in
queArray[j+1] = item; // insert it
Finally, your default constructor fails to initialize nItems.
There could be further errors, but I'll stop at this.
I agree with the other answers here, but I would add this:
Your "Remove" method isn't actually removing anything - it is just returning the first element - but it doesn't do anything to the array itself.
Edited to say that your insert method needs some work - it may or may not write over the end of the array, but it is certainly confusing as to what it is doing.
Try initializing your queue array in the constructor.
EDIT: After even more code modification, the error is still there, modified code shown:
KeyDown():
const int input_bit_num = 0x8000;
char keys[256];
bool KeyDown(int key)
{
return (keys[key] & input_bit_num) != 0;
}
PollKeyboard():
LPDIRECTINPUTDEVICE8 di_keyboard;
void PollKeyboard()
{
long result = di_keyboard->GetDeviceState(sizeof(keys), (LPVOID)&keys);
char para[16];
itoa(result, para, 17);
if(result != DI_OK) MessageBox(NULL, para, "ERROR", MB_OK);
}
When I try to put MessageBox within a KeyDown() if statement (as seen below in the game loop), the MessageBox simply coninues to appear even if I stop pressing the key, ie: I press , the "Would you like to quit?" message box appears, I say no, It disappears and then instantly reappears, as if I were still holding the key.
This is my loop:
void GameRun(HWND hWnd) //called once every frame
{
PollKeyboard();
if(GetTickCount - start >= 30)
{
if(KeyDown(DIK_LEFT))
MoveLeft();
if(KeyDown(DIK_RIGHT))
MoveRight();
}
if(d3ddev->BeginScene())
{
//rendering
}
if(KeyDown(DIK_ESCAPE))
{
//any MessageBox()
int result = MessageBox(hWnd, "I'm causing so much trouble!", "IMMORTAL", MB_YESNOCANCEL);
if(result == IDYES)
//end
}
}
EDIT: The catch in PollKeyboard() displays the sequence 53gd6bcc, I could not, however, find the error code it corresponds to.
EDIT: After another test, I saw that even if the MessageBox is not within a KeyDown() if statement, the glitch still occurs.
EDIT: After a bit more testing, it appears that MessageBox itself is causing the glitch.
Because the sample code works, something else in your program is causing the bug. Try moving bits of the code below into your own until it works, then you will know which section of code was culprit.
Sample Code
Alright, huge code block coming up. This code works correctly for me. (Escape, along with all other keys successfully activate and deactivate). It's large, commented, and explains things fairly well. Try this, if it works, we'll examine other parts of your program, if not, I can only leave you with "Good luck", and take what you want:
// DirectInput
#define DIRECTINPUT_VERSION 0x0800
#include<dinput.h>
// Standard stuff
#include <iostream>
#include <stdexcept>
#include <sstream>
#include <string>
// Link from code, MSVC specific, could be done in project settings
#pragma comment(lib, "dinput8.lib")
#pragma comment(lib, "dxguid.lib")
// Utility lexical_cast, use Boost if possible.
// Simple replacement, converts a stream-able `T`
// to a string
template <typename T>
const std::string lexical_cast(const T& pValue)
{
std::stringstream ss;
ss << pValue;
return ss.str();
}
// Utility function + macro to execute DirectX code with exceptions.
// Kinda ugly, but helpful for us.
void check_error(HRESULT pResult, const std::string& pFuncName)
{
// DI_OK == S_OK, but S_OK is more general, so we'll use that
if (pResult != S_OK)
{
throw std::runtime_error("Error executing: " + pFuncName +
"! Returned: " + lexical_cast(pResult));
}
}
// Macro, makes calling the function easier. It is wrapped in
// an `if` statement for reasons outlined in:
// http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.5
#define CHECK_ERROR(x) if (true) { check_error(x, #x); } else (void)0
// The above gives the warning:
// "warning C4127: conditional expression is constant", disable below:
#pragma warning(disable: 4127)
// Manages input
class input_manager
{
public:
// Constants
static const int NumberKeys = 256;
// Creation
input_manager(void)
{
// Create input and keyboard (like I said, ugly macro, but helpful :] )
CHECK_ERROR(DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION,
IID_IDirectInput8, reinterpret_cast<void**>(&_input), 0));
CHECK_ERROR(_input->CreateDevice(GUID_SysKeyboard, &_keyboard, 0));
CHECK_ERROR(_keyboard->SetDataFormat(&c_dfDIKeyboard));
CHECK_ERROR(_keyboard->Acquire());
}
~input_manager(void)
{
// Free resources. Note: Many programmers
// unnecessarily wrap this stuff in
// `if (_keyboard !=0)`, and then
// `_keyboard = 0`. This is completely unnecessary,
// because destructors are only run one time.
// Also, I can assume they are valid, because if they
// weren't, we wouldn't be here (an exception would have
// been thrown)
_keyboard->Unacquire();
_keyboard->Release();
_input->Release();
// Also, if we wrapped this into a nice RAII class, we wouldn't
// be forced to write a destructor, but this is outside the scope.
// Feel free to ask how; additionally, since we're on the topic, if you'd
// like more tips handling input (I've written PLENTY of input managers)
// I'm free for asking about things like testing for triggers rather than pressed
// ("was it pressed, regardless if it's being held now" versus
// "is it being pressed"), etc.
}
// Operations
void update(void)
{
CHECK_ERROR(_keyboard->GetDeviceState(NumberKeys, reinterpret_cast<void*>(&_keys)));
}
// Query
bool key_pressed(int pKey) const
{
return test_key(pKey);
}
// Might wrap into an operator[] for convenience.
private:
// Constants
static const int PressMask = 0x80;
// Sorry for the confusion, but indeed, with
// `char`s the mask is simply 0x80.
// Utility
bool test_key(int pKey) const
{
return (_keys[pKey] & PressMask) != 0;
}
// Members
LPDIRECTINPUT8 _input;
LPDIRECTINPUTDEVICE8 _keyboard;
char _keys[NumberKeys];
};
void test_keys(const input_manager& input)
{
bool anyPressed = false;
for (unsigned i = 0; i < input_manager::NumberKeys; ++i)
{
if (input.key_pressed(i))
{
std::cout << "Pressing: " << i << std::endl;
anyPressed = true;
}
}
if (!anyPressed)
{
std::cout << "No keys pressed." << std::endl;
}
}
void execute(void)
{
input_manager input;
std::cout << "Press Q to quit." << std::endl;
bool running = true;
while (running)
{
input.update();
if (input.key_pressed(DIK_Q))
{
running = false;
}
test_keys(input);
Sleep(0); // give some processor time
}
}
int main(void)
{
// Place real code in an execute function, so main
// is clean and ready to catch exceptions:
try
{
execute();
}
catch (const std::exception& e)
{
// Error!
std::cerr << "Unhandled exception:" << e.what() << std::endl;
}
}
Old suggestion:
Try catching the return value from GetDeviceState:
HRESULT result = // v Prefer C++-style casts
di_keyboard->GetDeviceState(sizeof(keys), reinterpret_cast<void*>(&keys);
if (result != DI_OK)
{
// uh-oh
std::cout << result << std::endl;
}
Compare it against the table here.
Old Semi-Answer:
Shortly after editing in the code in the Extra Stuff section, I realized the error, sorry I didn't catch it earlier. You're testing the wrong bit :)
Observe:
// v HERE! Should be 0x8000, not 0x80.
return (GetAsyncKeyState(pKeyCode) & 0x8000) != 0;
Try that:
int KeyDown(int key)
{
return (keys[key] & 0x8000);
}
Also, this should be moved into a constant to avoid magic numbers:
// somewhere, probably in the private section of the class or in a detail namespace:
static const int PushedMask = 0x8000;
// code reads better:
int KeyDown(int key)
{
return (keys[key] & PushedMask);
}
Lastly, in C++ you have a bool type, so take advantage of it!
// v here
bool KeyDown(int key)
{
return (keys[key] & PushedMask);
}
I know Visual Studio will warn about this conversion from int to bool, so you can get rid of it while also making your intents clearer:
bool KeyDown(int key)
{
return (keys[key] & PushedMask) != 0; // or == 1, your choice
}
Extra Stuff:
Try the following code:
#include <iostream>
#include <windows.h>
bool key_pressed(int pKeyCode)
{
return (GetAsyncKeyState(pKeyCode) & 0x8000) != 0;
}
void test_keys(void)
{
for (unsigned i = 0; i < 255; ++i)
{
if (key_pressed(i))
{
std::cout << "Pressing: " << i << std::endl;
}
}
}
int main(void)
{
bool running = true;
while (running)
{
if (key_pressed(VK_ESCAPE))
{
running = false;
}
test_keys();
Sleep(0);
}
}
This works for me (responds to all keys, quits on escape). Minimal test case for GetAsyncKeyState. If this does not work, please add OS, Keyboard, etc, in your comment.
If you create a MessageBox(Null,...) you won't have any control over the window after it's creation. IE, the window won't disappear when you depress the key.
As for why it keeps on appearing, seems to have something to do with this:
const int input_bit_num = 0x8000;
char keys[256];
bool KeyDown(int key)
{
return (keys[key] & input_bit_num) != 0;
}
keys consits of 1 byte long characters, while input_bit_num is a 2 byte value. While I don't honestly know which bit it is that you're looking for (0xff - 0x00 is the domain of 1 byte).
Honestly, I'm surprised that your code runs, unless the & operation is carrying over into keys[key-1] in which case any KeyDown is undefined, and KeyDown(...) when key is 0 is particularly dangerous.