• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
iceman76

Old Keyboard State in Input Handler

16 posts in this topic

I've been working on an input handler, using SDL2 and C++.  Right now I'm testing it with a Pong clone, but I want to be able to use it my next games.  Right now it detects if a key is currently down or not, but I start running into issues when I try to see if a key was pressed or released that frame.
 
I'm using two arrays, one for the current state and another for the state the previous frame.  Every update, I use memcpy to copy the new state to the old state, and then use SDL_PumpEvents to update the current array.
 
The issue is that this actually isn't keeping an array of the old keyboard state.  Instead, the old and new arrays are always the same, so the keys are never able to be tagged as "pressed" or "released". (I did some debugging and can confirm that keyboardOld is changing)  I'm sure this is an issue with memcpy and constant pointers and all of that fun stuff, I just don't have enough experience with them to figure out what's going wrong.
 
So, why are keyboardOld and keyboardNew always the same, even though I copy New to Old before updating New, and how can I fix it?  Thanks in advance!
 
Here's the code:

Input.h

#ifndef Input_h
#define Input_h

#include <SDL.h>
#include <cstring>
#include <stdio.h>

class Input{
public:
	enum keys{
		A = SDL_SCANCODE_A,
		B = SDL_SCANCODE_B,
		C = SDL_SCANCODE_C,
		D = SDL_SCANCODE_D,
		E = SDL_SCANCODE_E,
		F = SDL_SCANCODE_F,
		G = SDL_SCANCODE_G,
		H = SDL_SCANCODE_H,
		I = SDL_SCANCODE_I,
		J = SDL_SCANCODE_J,
		K = SDL_SCANCODE_K,
		L = SDL_SCANCODE_L,
		M = SDL_SCANCODE_M,
		N = SDL_SCANCODE_N,
		O = SDL_SCANCODE_O,
		P = SDL_SCANCODE_P,
		Q = SDL_SCANCODE_Q,
		R = SDL_SCANCODE_R,
		S = SDL_SCANCODE_S,
		T = SDL_SCANCODE_T,
		U = SDL_SCANCODE_U,
		V = SDL_SCANCODE_V,
		W = SDL_SCANCODE_W,
		X = SDL_SCANCODE_X,
		Y = SDL_SCANCODE_Y,
		Z = SDL_SCANCODE_Z,
		UP = SDL_SCANCODE_UP,
		DOWN = SDL_SCANCODE_DOWN,
		LEFT = SDL_SCANCODE_LEFT,
		RIGHT = SDL_SCANCODE_RIGHT,
	};

	void init();
	void update();
	void close();

	//True if a key is pressed
	bool keyDown(int key);
	//True if the key was pressed this frame
	bool keyPressed(int key);
	//True if the key was released this frame
	bool keyReleased(int key);

private:
	//Length of keyboard arrays (given by SDL_getKeyboardState)
	int length;
	//State of keyboard last frame
	Uint8* keyboardOld;
	//State of keyboard this frame
	const Uint8* keyboardNew;
};

#endif

Input.cpp

#include "Input.h"

void Input::init(){
	keyboardNew = SDL_GetKeyboardState(&length);

	keyboardOld = new Uint8[length];
}

void Input::update(){
	std::memcpy(keyboardOld, keyboardNew, length);

	SDL_PumpEvents();
}

void Input::close(){
	delete keyboardOld;
	keyboardOld = nullptr;
}

//Returns true if the key is currently pressed
bool Input::keyDown(int key){
	return keyboardNew[key];
}

//Returns true if the key was pressed this frame
bool Input::keyPressed(int key){
	return (keyboardNew[key] && !keyboardOld[key]);
}

//Returns true if the key was released this frame
bool Input::keyReleased(int key){
	return (!keyboardNew[key] && keyboardOld[key]);
}

And the relevant code from the main program:

				//Handle player input
				input.update();

				if(input.keyPressed(input.UP)){
					player->y -=50;
				}
				if(input.keyReleased(input.UP)){
					player->y -=50;
				}
				if(input.keyDown(input.DOWN)){
					player->y +=1;
				}
				if(input.keyDown(input.LEFT)){
					player->x -=1;
				}
				if(input.keyDown(input.RIGHT)){
					player->x +=1;
				}
0

Share this post


Link to post
Share on other sites

Just to confirm, you ARE seeing changes in the keyboardNew buffer, and the only problem you're having is copying that buffer?

Yes, the keyboardNew buffer is definitely being updated correctly.  Like you said, SDL_PumpEvents updates the buffer that it's pointing at.

 

I just tried copying the values with the for loop, and the result is the same.

 

In addition, just FYI, the docs for SDL_PumpEvents states:

You can only call this function in the thread that set the video mode.

 

I haven't set up anything with multi-threading myself, so I don't think this should be an issue (unless SDL automatically does threading).

0

Share this post


Link to post
Share on other sites

I just tried copying the values with the for loop, and the result is the same.

 

For further info (or perhaps a solution,) try copying the new buffer to the "old" in your Init function.

 

Then change Update() to just:

 

SDL_PumpEvents(NULL);

 

Make your keyboard comparisons, THEN copy the new buffer to the old.

 

I.e., the process would be something like:

keyboardNew = GetKeyboardStates(&length);
keyboardOld = new Uint8[length];
// if the following shows correct results, try std::memcpy
for(int i=0; i < length; ...) { copy keyboardNew to keyboardOld } // make the initial copy
loop:
   SDL_PumpEvents(NULL);
   // check for changes
   isThisKeyPressed();
   ...
   isThatKeyPressed();
   // done using keyboardNew for this time through the loop
   { copy keyboardNew to keyboardOld }

Also just confirming: input.keyPressed() is used to detect ONLY the initial downstate of a key (single event), as it will be false the next time through the loop, no matter what happens.

Edited by Buckeye
2

Share this post


Link to post
Share on other sites

I've tried it here and it works fine so far. Could we see more of the code before input.update() is called?

0

Share this post


Link to post
Share on other sites

I've also tried it with your Input class and it works as well. Here's the complete program.

 

There's one strange thing so far: if I hold both the g and b keys and then press h, the h isn't registered. I have no idea why that is at this point.

 

EDIT: sorry for the unformatted code, this should be better.

EDIT2: use delete[] instead of delete to get rid of the keyboardOld array

 

[source]

#include <iostream>

#include <SDL.h>

 

const int SCREEN_WIDTH    = 800;

const int SCREEN_HEIGHT    = 600;

 

class Input

{

public:

    enum keys

    {

        A = SDL_SCANCODE_A,

        B = SDL_SCANCODE_B,

        C = SDL_SCANCODE_C,

        D = SDL_SCANCODE_D,

        E = SDL_SCANCODE_E,

        F = SDL_SCANCODE_F,

        G = SDL_SCANCODE_G,

        H = SDL_SCANCODE_H

    };

 

    void init();

    void update();

    void close();

 

    //True if a key is pressed

    bool keyDown(int key);

    //True if the key was pressed this frame

    bool keyPressed(int key);

    //True if the key was released this frame

    bool keyReleased(int key);

private:

    //Length of keyboard arrays (given by SDL_getKeyboardState)

    int length;

    //State of keyboard last frame

    Uint8* keyboardOld;

    //State of keyboard this frame

    const Uint8* keyboardNew;

};

 

void Input::init()

{

    keyboardNew = SDL_GetKeyboardState(&length);

    keyboardOld = new Uint8[length];

}

 

void Input::update()

{

    std::memcpy(keyboardOld, keyboardNew, length);

    SDL_PumpEvents();

}

 

void Input::close()

{

    delete[] keyboardOld;

    keyboardOld = nullptr;

}

 

bool Input::keyDown(int key)

{

    return keyboardNew[key];

}

 

bool Input::keyPressed(int key)

{

    return (keyboardNew[key] && !keyboardOld[key]);

}

 

bool Input::keyReleased(int key)

{

    return (!keyboardNew[key] && keyboardOld[key]);

}

 

 

 

int main(int argc, char *argv[])

{

    SDL_Window* window = nullptr;

    SDL_Surface* screenSurface = nullptr;

    Uint8 r, g, b;

    bool hidden = false;

    Input input;

 

    // initialize SDL

    if (SDL_Init( SDL_INIT_VIDEO ) < 0)

        return 0;

    // create window

    window = SDL_CreateWindow("SDLTest", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOW);

    if (!window)

    {

        SDL_Quit();

        return 0;

    }

    // initialize our input object

    input.init();

 

    // event loop

    while (true)

    {

        // fetch keyboard state

        input.update();

 

        // process keyboard state

        // -> quit?

        if (input.keyPressed(Input::C) || input.keyPressed(SDL_SCANCODE_ESCAPE))

            break;

        // -> visibility

        if (input.keyPressed(Input::H))

            hidden = true;

        else if (input.keyReleased(Input::H))

            hidden = false;

        // -> color

        if (!hidden)                    r = 0xFF;

        else                        r = 0;

        if (input.keyDown(Input::G) && !hidden)    g = 0xFF;

        else                        g = 0;

        if (input.keyDown(Input::B) && !hidden)    b = 0xFF;

        else                        b = 0;

        // draw a color on screen

        screenSurface = SDL_GetWindowSurface(window);

        SDL_FillRect(screenSurface, nullptr, SDL_MapRGB(screenSurface->format, r, g, b));

        SDL_UpdateWindowSurface(window);

    }

 

    // clean up before exit

    input.close();

    SDL_DestroyWindow(window);

    SDL_Quit();

 

    return 0;

}

[/source]

1

Share this post


Link to post
Share on other sites

 

Input.cpp
void Input::close(){
	delete keyboardOld;
	keyboardOld = nullptr;
}

 

One remark about this: you should use delete[] to release the keyboardOld array, since you also assigned it with the new[] operator.

0

Share this post


Link to post
Share on other sites

There's one strange thing so far: if I hold both the g and b keys and then press h, the h isn't registered. I have no idea why that is at this point.


This is a common problem with non-mechanical and/or cheap keyboards. Due to the way they're constructed, they can only register a certain number of key presses at once, and pressing certain keys prevents other keys from being registered. The only solution (short of getting a better keyboard) is to allow users to change their keyboard bindings to avoid these issues.
0

Share this post


Link to post
Share on other sites

 

There's one strange thing so far: if I hold both the g and b keys and then press h, the h isn't registered. I have no idea why that is at this point.


This is a common problem with non-mechanical and/or cheap keyboards. Due to the way they're constructed, they can only register a certain number of key presses at once, and pressing certain keys prevents other keys from being registered. The only solution (short of getting a better keyboard) is to allow users to change their keyboard bindings to avoid these issues.

 

 

Yes, I thought it might be something like that. It seems strange that it happens for two very specific combinations (of the ones I've tried), but I guess that may be due to the way the keyboard is wired.

0

Share this post


Link to post
Share on other sites

Haegarr, you're right- the problem was that I was accidentally calling SDL_PumpEvents.  Right before I check for the input in main program, there's this little snippet to exit when the user presses X:

//Handle events on the queue
while( SDL_PollEvent( &e ) != 0 ){
	//User requests quit
	if( e.type == SDL_QUIT ){
		quit = true;
	}
}

I read online several times that SDL_PollEvent calls SDL_PumpEvents internally, but it completely slipped my mind!  Serves me right for purposefully ignoring this from the FAQ:

 

 

When posting code (which you should do if the problem is at all code-related), post it verbatim without transcribing or omitted code you believe is relevant as you can easily hide the problem that way.

 

Anyways, commenting out the above lines worked, so I can detect keyPressed and keyReleased to my heart's content!  Of course, that means that you can't quit my program without resorting to the Windows Task Manager, but that's a feature, right? =P  I've also changed the delete to delete[]

 

Thanks to everybody for helping me figure this out!

0

Share this post


Link to post
Share on other sites

you can't quit my program without resorting to the Windows Task Manager, but that's a feature, right? =P

 

One man's feature is another man's bug. That, IMHO, is a bad "feature." Maybe just me, but I would remove your app (or not download it to be begin with) the moment I discovered that was the only method to terminate it. angry.png

 

Also just my humble opinion, but if an app (particularly a game) isn't dedicated to user convenience as it's first priority, it's not ready for distribution.

Edited by Buckeye
1

Share this post


Link to post
Share on other sites


One man's feature is another man's bug. That, IMHO, is a bad "feature." Maybe just me, but I would remove your app (or not download it to be begin with) the moment I discovered that was the only method to terminate it.

Not only that, but letting the "feature" inside is just a time bomb. After a while the developer things of new features based on input … and suddenly keyboard input is again no longer running. Such things should be wiped out at the roots.

0

Share this post


Link to post
Share on other sites

Oh, don't worry, I am most certainly going to fix that.  I was just joking about the same thing Buckeye said, actually- calling it a feature when it is so obviously a bug.  Sorry that I wasn't clear.  Also, this isn't something I'm planning on distributing, this is just me learning how to use SDL2 in the first place.

0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0