C++ How do I Handle all possible Player Movement inputs? - c++

I am trying to clean up movement code I followed from a video tutorial series that was never finished. My intent is for the character to only ever be able to move on X or Y at any given time (so no diagonal). The character has direction facing to keep in mind.
My issue is the player can still press any key they want, or accidently press two keys at the same time.
Ex. if you move Up and make a right turn, accidentally press Right before letting go of Up.
Or if you press Up, press and let go Right to make a slight movement right while continuing to press Up, the player should continue to move up after letting go of Right without having to re-press Up. etc.
Just to make sure all possible input cases are handled intuitively.
EDIT: This is the code so far and I'm getting weird errors I don't know what's wrong
#pragma once
#include "../game.h"
#include "ECS.h"
#include "Components.h"
#include <list>
using namespace std;
class KeyboardController : public Component
{
public:
TransformComponent *transform;
SpriteComponent *sprite;
std::list<SDL_Event> keyDownList;
SDL_Event lastDirection;
void updateKeyState()
{
if (Game::event.type == SDLK_ESCAPE) {
Game::isRunning = false;
}
else if (Game::event.type == SDL_KEYDOWN) {
keyDownList.push_back(Game::event.key.keysym.sym);
}
else if (Game::event.type == SDL_KEYUP) {
keyDownList.remove(Game::event.key.keysym.sym);
}
}
void init() override
{
transform = &entity->getComponent<TransformComponent>();
sprite = &entity->getComponent<SpriteComponent>();
}
void update() override
{
void updateKeyState();
void updateMovement();
}
};
Severity Code Description Project File Line Suppression State
Error (active) E0304 no instance of overloaded function "std::list<_Ty, _Alloc>::push_back [with _Ty=SDL_Event, _Alloc=std::allocator]" matches the argument list Sandbox C:\file_path\KeyboardController.h 31
Severity Code Description Project File Line Suppression State
Error (active) E0415 no suitable constructor exists to convert from "SDL_Keycode" to "SDL_Event" Sandbox C:\file_path\KeyboardController.h 34

You should basically clean up your code by separating the logic between key events and player movement. So your update() method could look like this:
void update() override
{
updateKeyState();
updateMovement();
}
Since you want the player to move only vertically or horizontally (never diagonally), you have to store the key press order in a data structure that can be easily accessed. I think you could use a doubly-linked list:
std::list<SDL_Event> keyDownList;
and we should also store the last key pressed in order to restore the idle animation of the player:
SDL_Event lastDirection;
The updateKeyState() method should add or remove the key to/from the linked list. We should also check if the player wants to leave the game by pressing ESC:
void updateKeyState() {
if (Game::event.type == SDLK_ESCAPE) {
Game::isRunning = false;
} else if (Game::event.type == SDL_KEYDOWN) {
keyDownList.push_back(Game::event.key.keysym.sym);
} else if (Game::event.type == SDL_KEYUP) {
keyDownList.remove(Game::event.key.keysym.sym);
}
}
The updatePlayerMovement() method is where the magic happens. We should basically check which key was pressed first and update the player position accordingly. We also save the down key in the lastDirection field in order to use it when no key is pressed.
void updateMovement() {
// if any key is down
if (keyDownList.size() > 0) {
const SDL_Event downKey = keyDownList.front();
switch (downKey) {
case SDLK_w:
transform->velocity.y = -1;
transform->velocity.x = 0;
sprite->Play("BackWalk");
lastDirection = downKey;
break;
case SDLK_a:
transform->velocity.x = -1;
transform->velocity.y = 0;
sprite->Play("SideWalk");
sprite->spriteFlip = SDL_FLIP_HORIZONTAL;
lastDirection = downKey;
break;
case SDLK_s:
transform->velocity.y = 1;
transform->velocity.x = 0;
sprite->Play("FrontWalk");
lastDirection = downKey;
break;
case SDLK_d:
transform->velocity.x = 1;
transform->velocity.y = 0;
sprite->Play("SideWalk");
sprite->spriteFlip = SDL_FLIP_NONE;
lastDirection = downKey;
break;
}
} else {
// no key is down, handle idle situations
transform->velocity.x= 0;
transform->velocity.y = 0;
switch (lastDirection) {
case SDLK_w:
sprite->Play("BackIdle");
break;
case SDLK_a:
sprite->Play("SideIdle");
break;
case SDLK_s:
sprite->Play("FrontIdle");
break;
case SDLK_d:
sprite->Play("SideIdle");
break;
}
}
}
Note: I haven't tested this code because I don't have the code and structures from your game. So you may have to edit a piece here and there to make it work for you.

Related

Clueless about how to use switch

this is my very first post :)
I'm a graphic design student in my second year and we have to make code for business cards.
What I want is when I click with my mouse the background of the business card will change.
It worked with a boolean for two pictures.
But now I want it to work with four images.
Have asked the teacher for help and she sent me information about Switch.
But I have no idea where to put in my code and how to fill it in.
This is my code so far
String Instagram="#Famoys_Saz";
String quote = "Guess my passion ;)";
PFont font;
PImage bg;
PImage Snake2;
PImage snake3;
PImage snake4;
boolean click = true;
void setup() {
size(850, 550);
font = createFont("Galaxyfaceano-4yM9.ttf", 45);
textFont(font);
textAlign(CENTER);
bg = loadImage("Snake.jpg");
Snake2= loadImage ("Snake2.jpg");
click = true;
}
void draw() {
background(bg);
if (click == true ) {
image(bg, 0, 0);
} else {
image(Snake2, 0, 0);
}
text(Instagram, mouseX, mouseY);
text(quote, mouseY, mouseX, 05);
}
void mousePressed() {
click =! click;
}
and this is the code my teacher send me:
if( imageNumber == 1 ) {
} else if ( imageNumber == 2 ) {
} else {
}
Could someone help me out? I'm quite a beginner when it comes to coding.
it will look something like this:
switch( imageNumber ) {
case 1: // same as saying imageNumber == 1
//some code
break;
case 2: // same as saying imageNumber == 2
//some code here
break;
}
switch(imageNumber) {
case 1:
// do stuff
break;
case 2:
// do stuff
break;
case 3:
// do stuff
break;
case 4:
// do stuff
break;
default:
// if no case was selected, do this
}
Switches are easy to read and efficient, and some programmers prefers them to else if. Both are fine, as long as they are readable, but the switch has some unique particularities, like having to use break; between cases. The break; will exit the switch, else it would evaluate every other possible case (thus you're saving some execution time).
It happened to me in a robotic project to deliberately skip the break; statement so the robot would do "everything from this point on", but it's very uncommon and a source of many beginner's bugs to forget the break;.
Depending on the language, switches can evaluate different things, but the classics are integers and strings.
Have fun!

OpenGL - Left mouse click keeps removing my grid

I have created a drawGrid() function that draws a squared grid along my X and Y axis, which works fine. I have then created a menu() function (called in the main()), that toggles the grid on and off, here's the code for that:
void menu(int item)
{
switch (item)
{
case MENU_SWITCH_OFF_GRID:
{
if (gridActive == true)
{
gridActive = true;
}
}
break;
case MENU_SWITCH_ON_GRID:
{
if (gridActive == true)
{
gridActive = false;
}
}
break;
default:
{ /* Nothing */ }
break;
}
glutPostRedisplay();
return;
}
}
The menu switch works fine, as I have created a global variable called gridActive without a true or false value so it doesn't reset each time, that way it can be accessed in my display() function like so:
if (gridActive != true)
{
drawGrid();
gridActive = true;
}
All of this works just fine.
What's my issue?
My issue is, whenever I click the left mouse button, my grid disappears, which I don't want. So I've made a mouse() function like this:
case GLUT_LEFT_BUTTON: if (state == GLUT_DOWN)
{
exit(0); // this has been added to see if
// my program will exit!
}
break;
To test if my program exits when I click the left mouse, and it does.
So instead of using exit(0); what code can i put here so that my grid doesn't disappear when I click the left mouse button? Or is the issue beyond that?
UPDATE:
Here's the mouse function:
void mouse(int button, int state, int x, int y)
{
// these have simply been set-up for me to use
// in the future
switch (button)
{
case GLUT_LEFT_BUTTON: if (state == GLUT_DOWN)
{
}
break;
case GLUT_RIGHT_BUTTON: if (state == GLUT_DOWN)
{
}
break;
default: break;
}
}
Based on your code:
if (gridActive != true)
{
drawGrid();
gridActive = true;
}
You only draw the grid when gridActive is false. However, after every time you draw it, you set gridActive=true which will then stop it from being drawn.
Without more of your code, it's impossible to tell exactly what's going on, but these lines may not be doing what you think they are, and that may be causing some issues.
This never does anything.
if (gridActive == true)
{
gridActive = true;
}
This:
if (gridActive == true)
{
gridActive = false;
}
is the same as:
gridActive = false;
In order to tell what's going on, though, we need to know what happens when you click your mouse button when the exit call isn't there, but you didn't post that code yet.
Also, i don't quite know what you mean by:
I have created a global variable called gridActive without a true or false value so it doesn't reset each time
but it sounds like you made an uninitialized global variable and expect that it has some specific meaning because it's uninitialized?

How to get notification for keydown for CMFCRibbonComboBox?

I have CMFCRibbonComboBox on ribonbar and I want when that user press on a key open droplist and select Item acurding to chars that press by user.
For this purpose I want to get notification for keydown.
How can I to do it?
Thanks
I asked a very similar question on MSDN here and eventually solved it myself with the following hack;
Save a local copy of C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\src\mfc\afxribbonedit.cpp to your project
In BOOL CMFCRibbonRichEditCtrl::PreTranslateMessage(MSG* pMsg) replace this
case VK_DOWN:
if (m_edit.m_bHasDropDownList && !m_edit.IsDroppedDown())
{
m_edit.DropDownList();
return TRUE;
}
with this
case VK_DOWN:
if (m_edit.m_bHasDropDownList && !m_edit.IsDroppedDown())
{
m_edit.DropDownList();
CMFCRibbonBaseElement* pRibbonBaseElement = m_edit.GetDroppedDown();
if (pRibbonBaseElement && (pRibbonBaseElement->IsKindOf(RUNTIME_CLASS(CMFCRibbonComboBox))))
{
CString str;
GetWindowText(str);
CMFCRibbonComboBox *pCombo = (CMFCRibbonComboBox*)pRibbonBaseElement;
int ItemNo = -1;
for (int i = 0; i < pCombo->GetCount(); i++)
{
CString ItemText = pCombo->GetItem(i);
if (ItemText.Left(str.GetLength()).CompareNoCase(str) == 0)
{
ItemNo = i;
break;
}
}
if (ItemNo != -1)
{
pCombo->OnSelectItem(ItemNo);
// Draw and redraw dropdown for selection to show
m_edit.DropDownList();
m_edit.DropDownList();
}
}
return TRUE;
}
For drop lists (as opposed to drop downs) you can similarly hand WM_CHAR to do a first letter search based on the next item after the current position. Note that the above hack would need to be checked against any future updates to the ribbon library and should be dumped once it has been properly implemented in the library.

How to make keyword to work instantly with no delay in Qt::4.6 (C++)?

OS:: win xp sp3.
Qt:: 4.6
I have class Gameboard in which i have some rectangle.I defined keyPressEvent for that rectangle in order to move him around the screen.Key_A :: rectangle.moveToLeft & Key_D :: rectangle.moveToRight.Problem is that keys work with delay.
When i release one key and press another one it takes some time before that one begin to work.I checked Qt online documentation and now for that effect but dont now how to make those keys to work instantly without delay beetween them?
code snippet:
//in Gameboard class
ship = new QRect(x,y,w,h);
void Gameboard::keyPressEvent(QKeyEvent* event)
{
switch(event->key()) {
case Qt::Key_A :
{
x = x-10;
ship->moveTo(x,y);
break;
}
case Qt::Key_D :
{
x = x+10;
ship->moveTo(x,y);
break;
}
}
}
Put input cursor into any applicable text box and press the 'A' key. What you'll see is once you press the key, letter 'A' will be printed, then there will be a pause, and then first letter 'A' will be quickly followed by many others. That's the way keypress events work. And your program is receiving them exactly like this. First you receive one event when the key is actually pressed, and then after a delay you get a lot of automatically repeated events, in case user wants to enter one character many-many times.
It works perfectly for text input, but in games you usually need smooth movement. If that's the case, you need to move your ship not upon receiving the event, but regularly, for example, on timer event. And you will need to catch both keyPressEvent and keyRelease event and use them to remember what movement keys are currently pressed. So you could for example do this:
struct Ship {
bool is_moving_left, is_moving_right;
QPoint position;
int speed;
...
void timerEvent()
{
if (is_moving_left) position.setX (position.x() - speed);
if (is_moving_right) position.setX (position.x() + speed);
}
...
};
...
void Gameboard::keyPressEvent (OKeyEvent *_event)
{
switch(event->key()) {
case Qt::Key_A :
ship->is_moving_left = true;
break;
case Qt::Key_D :
ship->is_moving_right = true;
break;
}
}
...
void Gameboard::keyReleaseEvent (OKeyEvent *_event)
{
switch(event->key()) {
case Qt::Key_A :
ship->is_moving_left = false;
break;
case Qt::Key_D :
ship->is_moving_right = false;
break;
}
}
Then just make sure Ship::timerEvent() gets called on every timer event in the game.

How to handle multiple keypresses at once with SDL?

been getting myself familiar with OpenGL programming using SDL on Ubuntu using c++. After some looking around and experimenting I am starting to understand. I need advice on keyboard event handling with SDL.
I have a 1st person camera, and can walk fwd, back, strafe left and right and use the mouse to look around which is great. Here is my processEvents function:
void processEvents()
{
int mid_x = screen_width >> 1;
int mid_y = screen_height >> 1;
int mpx = event.motion.x;
int mpy = event.motion.y;
float angle_y = 0.0f;
float angle_z = 0.0f;
while(SDL_PollEvent(&event))
{
switch(event.type)
{
case SDL_KEYDOWN:
switch(event.key.keysym.sym)
{
case SDLK_ESCAPE:
quit = true;
break;
case SDLK_w:
objCamera.Move_Camera( CAMERASPEED);
break;
case SDLK_s:
objCamera.Move_Camera(-CAMERASPEED);
break;
case SDLK_d:
objCamera.Strafe_Camera( CAMERASPEED);
break;
case SDLK_a:
objCamera.Strafe_Camera(-CAMERASPEED);
break;
default:
break;
}
break;
case SDL_MOUSEMOTION:
if( (mpx == mid_x) && (mpy == mid_y) ) return;
SDL_WarpMouse(mid_x, mid_y);
// Get the direction from the mouse cursor, set a resonable maneuvering speed
angle_y = (float)( (mid_x - mpx) ) / 1000;
angle_z = (float)( (mid_y - mpy) ) / 1000;
// The higher the value is the faster the camera looks around.
objCamera.mView.y += angle_z * 2;
// limit the rotation around the x-axis
if((objCamera.mView.y - objCamera.mPos.y) > 8) objCamera.mView.y = objCamera.mPos.y + 8;
if((objCamera.mView.y - objCamera.mPos.y) <-8) objCamera.mView.y = objCamera.mPos.y - 8;
objCamera.Rotate_View(-angle_y);
break;
case SDL_QUIT:
quit = true;
break;
case SDL_VIDEORESIZE:
screen = SDL_SetVideoMode( event.resize.w, event.resize.h, screen_bpp, SDL_OPENGL | SDL_HWSURFACE | SDL_RESIZABLE | SDL_GL_DOUBLEBUFFER | SDL_HWPALETTE );
screen_width = event.resize.w;
screen_height = event.resize.h;
init_opengl();
std::cout << "Resized to width: " << event.resize.w << " height: " << event.resize.h << std::endl;
break;
default:
break;
}
}
}
now while this is working, it has some limitations. The biggest one and the purpose of my question is that it seems to only process the latest key that was pressed. So if I am holding 's' to walk backwards and I press 'd' to strafe right, I end up strafing right but not going backwards.
Can someone point me in the right direction for better keyboard handling with SDL, support for multiple keypresses at once, etc?
Thanks
SDL keeps track of the current state of all keys. You can access this state via:
SDL_GetKeyState()
So, each iteration you can update the movements based on the key state. To make the movement smooth you should update the movement magnitude based on the time elapsed between updates.
A good approach will be to write a keyboard ("input") handler that will process input events and keep the event's state in some sort of a structure (associative array sounds good - key[keyCode]).
Every time the keyboard handler receives a 'key pressed' event, it sets the key as enabled (true) and when it gets a key down event, it sets it as disabled (false).
Then you can check multiple keys at once without pulling events directly, and you will be able to re-use the keyboard across the entire frame without passing it around to subroutines.
Some fast pseudo code:
class KeyboardHandler {
handleKeyboardEvent(SDL Event) {
keyState[event.code] = event.state;
}
bool isPressed(keyCode) {
return (keyState[keyCode] == PRESSED);
}
bool isReleased(keyCode) {
return (keyState[keyCode] == RELEASED);
}
keyState[];
}
...
while(SDL Pull events)
{
switch(event.type) {
case SDL_KEYDOWN:
case SDL_KEYUP:
keyHandler.handleKeyboardEvent(event);
break;
case SDL_ANOTHER_EVENT:
...
break;
}
}
// When you need to use it:
if(keyHandler.isPressed(SOME_KEY) && keyHandler.isPressed(SOME_OTHER_KEY))
doStuff(TM);
If you're using SDL2 then use SDL_GetKeyboardState.
Example:
const Uint8 *keyboard_state_array = SDL_GetKeyboardState(NULL);
SDL_PollEvent(&event);
if(event.type == SDL_KEYDOWN || event.type == SDL_KEYUP)
{
// Move centerpoint of rotation for one of the trees:
if (keyboard_state_array[SDL_SCANCODE_UP] && !(keyboard_state_array[SDL_SCANCODE_DOWN]))
{
--location.y;
}
else if (!keyboard_state_array[SDL_SCANCODE_UP] && keyboard_state_array[SDL_SCANCODE_DOWN])
{
++location.y;
}
if (keyboard_state_array[SDL_SCANCODE_RIGHT] && !keyboard_state_array[SDL_SCANCODE_LEFT])
{
++location.x;
}
else if (!keyboard_state_array[SDL_SCANCODE_RIGHT] && keyboard_state_array[SDL_SCANCODE_LEFT])
{
--location.x;
}
}
Instead of only looking at keydown events, any solution which is going to be caring about multiple keys at once is going to have to be looking at both keydown and keyup events, and keeping track of the state of the keys in question.
So instead of (pseudocode):
on keydown:
case left_key:
object.setMovement(left)
case forward_key:
object.setMovement(forward)
instead you'd have something more like (again pseudocode):
on keydown:
case left_key:
keystates[left] = true
object.updateMovement(keystates)
case forward_key:
keystates[forward] = true
object.updateMovement(keystates)
on keyup:
case left_key:
keystates[left] = false
object.updateMovement(keystates)
case forward_key:
keystates[forward] = false
object.updateMovement(keystates)
Then the updateMovement routine would look at keystates and figure out a composite movement based on the states of all movement keys together.
use SDL_GetKeyState to get the keyboard state