Jump to content

  • Log In with Google      Sign In   
  • Create Account


SDL Key/Mouse Simplified Intput Class


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
10 replies to this topic

#1 Konrad Jablonski   Members   -  Reputation: 482

Like
0Likes
Like

Posted 29 June 2013 - 07:31 PM

Hello everybody 

 

Today I decided to create controls class for my engine (when I say engine, It is VERY loosely said).

Basically I am trying to make SDL code a little simpler to use, the idea would be you create controls class and then 

you just do 

if (controls->isKeyDown(SLDK_ESCAPE))
{
std::cout << "Escape was Pressed" << std::endl;
}

if (controls->isKeyHeld(SLDK_ESCAPE))
{
std::cout << "Escape is Held" << std::endl;
}

if (controls->isKeyUp(SLDK_ESCAPE))
{
std::cout << "Escape was Released" << std::endl;
}

if (controls->isMouseUp(SDL_LEFT_MOUSE)
{
std::cout << "Left Mouse was Released" << std::endl;
}

and you can guess the rest for the mouse tongue.png

 

If there is already free controls class like that please let me know, or if there is better way of doing controls in SDL smile.png

 

Unfortunately it does not work the way I intended it too. I managed to get Key Presses working until you start pressing multiple keys, then everything goes messy and it starts thinking keys are pressed when they are not... It will know when key is pressed, held and released as long only that key is being pressed at that every moment.

 

The Mouse does not work more on other hand. It recognizes that the key was pressed/released but for example when I do 

if (controls->isMouseDown(SDL_LEFT_MOUSE)
{
std::cout << "Left Mouse was Pressed" << std::endl;
}

and press any of the mouse buttons, it will read that line even when I use Mouse Wheel button. Also isMouseHeld doesn't work at all. It is never read.

 

Mouse controls also go messy when you start touching multiple mouse keys at once. Gets stuck on release, just like in controls. Thinks the mouse was released until 1 button is pressed and resets it.

 

 

 

If this class would work I think it could be not just useful to me but would save some time for anyone else smile.png Instead of reinventing the wheel each time...

 

I really hate posting large chunks of code, but I have no idea where the problem is in the code... so here is .h and .cpp file. I've commented .h file smile.png

 

Thank you for your time in advance smile.png

 

controls.h

#pragma once
#include "stdafx.h"

class DllExport controls
{
public:
	//event is passed down from the engine class
	controls(SDL_Event* theEvent);
	~controls(void);

	//different types of possible key states
	#define KEY_NONE -1
	#define KEY_PRESSED 0
	#define KEY_RELEASED 1
	#define KEY_HOLDING 2

	//returns true key was pressed
	bool isKeyDown(int SDLK_ID);
	//returns true key is being held
	bool isKeyHeld(int SDLK_ID);
	//returns true key was released
	bool isKeyUp(int SDLK_ID);

	//returns true if Mouse button was pressed
	bool isMouseDown(int MouseButton);
	//returns true if Mouse button is being held
	bool isMouseHeld(int MouseButton);
	//returns true if Mouse button was released
	bool isMouseUp(int MouseButton);

	//Gets SDL event
	SDL_Event* GetEvent();

	//this runs every frame
	void Update();
private:
	SDL_Event* events; //Pointer to an SDL events system

	//Holds Current Mouse states
	int LeftMouseState;
	int RightMouseState;
	int LeftMouseHeld;
	int RightMouseHeld;

	//Press and Release Key States
	int KEYS_STATE[322];  // 322 is the number of SDLK_DOWN events
	//Held Key States
	int KEYS_STATE2[322];
};

controls.cpp

#include "stdafx.h"
#include "controls.h"


controls::controls(SDL_Event* theEvents)
{
	events = theEvents;

	for(int i = 0; i < 322; i++) { // init them all to false
		KEYS_STATE[i] = KEY_NONE;
		KEYS_STATE2[i] = KEY_NONE;
	}

	LeftMouseState = KEY_NONE;
	RightMouseState = KEY_NONE;
	LeftMouseHeld = KEY_NONE;
	RightMouseHeld = KEY_NONE;
}


controls::~controls(void)
{
}

bool controls::isMouseDown(int MouseButton)
{

	if (MouseButton == SDL_BUTTON_LEFT)
	{
		if (LeftMouseState == KEY_PRESSED)
			return true;
	}
	else if (MouseButton == SDL_BUTTON_RIGHT)
	{
		if (RightMouseState == KEY_PRESSED)
			return true;
	}

	return false;
}

bool controls::isMouseHeld(int MouseButton)
{

	if (MouseButton == SDL_BUTTON_LEFT)
	{
		if (LeftMouseState == KEY_HOLDING) 
			return true;
	}
	else if (MouseButton == SDL_BUTTON_RIGHT)
	{
		if (RightMouseState == KEY_HOLDING)
			return true;
	}

	return false;
}

bool controls::isMouseUp(int MouseButton)
{

	if (MouseButton == SDL_BUTTON_LEFT)
	{
		if (LeftMouseState == KEY_RELEASED) 
			return true;
	}
	else if (MouseButton == SDL_BUTTON_RIGHT)
	{
		if (RightMouseState == KEY_RELEASED)
			return true;
	}

	return false;
}

void controls::Update()
{

	switch (events->type) {

	case SDL_MOUSEBUTTONDOWN:


		if (events->button.button == SDL_BUTTON_LEFT);
		{
			if (LeftMouseHeld == KEY_NONE)
				LeftMouseState = KEY_PRESSED;
			else
				LeftMouseState = KEY_NONE;

			LeftMouseHeld = KEY_HOLDING;

		}
		if (events->button.button == SDL_BUTTON_RIGHT);
		{
			if (RightMouseHeld == KEY_NONE)
				RightMouseState = KEY_PRESSED;
			else
				RightMouseState = KEY_NONE;

			RightMouseHeld = KEY_HOLDING;
		}
		break;
	case SDL_MOUSEBUTTONUP:

		if (events->button.button == SDL_BUTTON_LEFT);
		{

			if (LeftMouseHeld == KEY_HOLDING)
				LeftMouseState = KEY_RELEASED;
			else
				LeftMouseState = KEY_NONE;

			LeftMouseHeld = KEY_NONE;

		}
		if (events->button.button == SDL_BUTTON_RIGHT);
		{
			if (RightMouseHeld == KEY_HOLDING)
				RightMouseState = KEY_RELEASED;
			else
				RightMouseState = KEY_NONE;

			RightMouseHeld = KEY_NONE;
		}
		break;
	case SDL_KEYDOWN:

		if (KEYS_STATE2[events->key.keysym.sym] == KEY_NONE)
		{
			KEYS_STATE[events->key.keysym.sym] = KEY_PRESSED;
		}
		else
			KEYS_STATE[events->key.keysym.sym] = KEY_NONE;

		KEYS_STATE2[events->key.keysym.sym] = KEY_HOLDING;


		break; 
	case SDL_KEYUP:

		if (KEYS_STATE2[events->key.keysym.sym] == KEY_HOLDING)
		{
			KEYS_STATE[events->key.keysym.sym] = KEY_RELEASED;
		}
		else
			KEYS_STATE[events->key.keysym.sym] = KEY_NONE;

		KEYS_STATE2[events->key.keysym.sym] = KEY_NONE;

		break;
	default:
		break;

	}
}

bool controls::isKeyDown(int SDLK_ID)
{

	if (KEYS_STATE[SDLK_ID] == KEY_PRESSED)
	{
		return true;
	}
	else 
		return false;
}

bool controls::isKeyHeld(int SDLK_ID)
{

	if (KEYS_STATE2[SDLK_ID] == KEY_HOLDING)
	{
		return true;
	}
	else 
		return false;
}


bool controls::isKeyUp(int SDLK_ID)
{

	if (KEYS_STATE[SDLK_ID] == KEY_RELEASED)
	{
		return true;
	}
	else 
		return false;
}


SDL_Event* controls::GetEvent() { return events; }

Edited by iLoveGameProgramming, 29 June 2013 - 07:33 PM.


Sponsor:

#2 pinebanana   Members   -  Reputation: 475

Like
3Likes
Like

Posted 29 June 2013 - 08:53 PM

Couple of quick tips:

//different types of possible key states
#define KEY_NONE -1
#define KEY_PRESSED 0
#define KEY_RELEASED 1
#define KEY_HOLDING 2

Why are you using #define? #define is evil and should be avoided. Use an enum or const, e.g.:

enum KeyState
{
    KEY_NONE = -1,
    KEY_PRESSED = 0,
    KEY_RELEASED = 1
    KEY_HOLDING = 2
};

I believe this:

#include "stdafx.h"
Makes your code less portable, although a person could delete it/comment it out, but yeah.
 
This:
if (KEYS_STATE[SDLK_ID] == KEY_PRESSED)
{
	return true;
}
else 
	return false;

Could simply be:

return KEYS_STATE[SDLK_ID] == KEY_PRESSED;

Also, KEYS_STATE2 and KEY_STATES are not not a very good names IMO. Perhaps make them more descriptive? KEYS_PRESSED_STATES and KEYS_HELD_STATES? BTW, you could probably use a struct/array to make it into one variable.

 

There's also a lot of some-what duplicate code, the only thing that's different is the variables, this could be avoided.


Edited by pinebanana, 29 June 2013 - 08:55 PM.

anax - An open source C++ entity system


#3 Konrad Jablonski   Members   -  Reputation: 482

Like
0Likes
Like

Posted 29 June 2013 - 09:41 PM

Thank you for reply Pinebanana :)

Why are you using #define? #define is evil and should be avoided. Use an enum or const, e.g.:

good idea, no one ever told me they are bad, but I guess enum are easier to manage :)

 

 

I am using that include because it needs to include SDL. I guess I could move it to include just SDL. It would make it more portable.

 

 

return KEYS_STATE[SDLK_ID] == KEY_PRESSED;

 

I had no idea you can do something like that! So this returns false if its anything else besides KEY_PRESSED, and true if it is? 

 

I am aware that there is some duplicated code, trying to get it working properly first. Still didn't figure out how to get multiple key presses at once :(

 

Thanks it was very good feedback :)



#4 Servant of the Lord   Crossbones+   -  Reputation: 17272

Like
3Likes
Like

Posted 29 June 2013 - 10:15 PM

if(x) triggers a branch of code if 'x' is true. It takes a bool.

 

A == B is a function that returns a bool.

&&, ||, !, >, <, <=, >=, also return bools.

 

This means you can go:

bool result = (a > 4) && (a < 6);
if(result)
{
    //Do something.
}

Which can be really useful when dealing with alot of conditionals. smile.png

 

About polling the current state with SDL, SDL already provides functions for that:

SDL_GetKeyState (renamed 'SDL_GetKeyboardState' in SDL 2)

SDL_GetMouseState

 

As for why #defines are generally considered bad practice, consider this:

#define FIVE 3 + 2
#define FOUR 4

std::cout << "The wrong result: " << FIVE * FOUR << std::endl;

Edited by Servant of the Lord, 29 June 2013 - 10:15 PM.

It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.

[Fly with me on Twitter] [Google+] [My broken website]

All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.                                                                                                                                                       [Need free cloud storage? I personally like DropBox]

Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal


#5 diventurer   Members   -  Reputation: 776

Like
1Likes
Like

Posted 30 June 2013 - 07:38 AM

 

 

As for why #defines are generally considered bad practice, consider this:

#define FIVE 3 + 2
#define FOUR 4

std::cout << "The wrong result: " << FIVE * FOUR << std::endl;

 

Which is why it's recommended to do this:

#define FIVE (3 + 2)
#define FOUR (4)
std::cout << "The correct result: " << FIVE * FOUR << std::endl;

I do agree that constants or an enum is a better choice though.

 

Just thought I'd defend #define a bit. rolleyes.gif

It's very useful at times. Just needs to be used when it should be used.


Edited by diventurer, 30 June 2013 - 07:39 AM.


#6 Servant of the Lord   Crossbones+   -  Reputation: 17272

Like
0Likes
Like

Posted 30 June 2013 - 10:11 AM

Which is why it's recommended to do this:

#define FIVE (3 + 2)
#define FOUR (4)
std::cout << "The correct result: " << FIVE * FOUR << std::endl;

Yep, but it's the simplest illustration of why it can cause problems - it just does a copy+paste of the define with what it was defined to.

I also use defines in my project; not frequently, but they are useful in a good number of circumstances. They just should be avoided for things that there are superior options for, like when using them for constant values.

Edited by Servant of the Lord, 30 June 2013 - 10:13 AM.

It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.

[Fly with me on Twitter] [Google+] [My broken website]

All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.                                                                                                                                                       [Need free cloud storage? I personally like DropBox]

Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal


#7 Konrad Jablonski   Members   -  Reputation: 482

Like
0Likes
Like

Posted 30 June 2013 - 11:11 AM

 

if(x) triggers a branch of code if 'x' is true. It takes a bool.

 

A == B is a function that returns a bool.

&&, ||, !, >, <, <=, >=, also return bools.

 

This means you can go:

bool result = (a > 4) && (a < 6);
if(result)
{
    //Do something.
}

 

Oh wow, thank you for this information. I always thought that the if statements are doing the job of returning the value. Not that I can use this, this way. I can see how it could be useful! Everyday learning something new :D

 

I've replaced #define with enum :)

 

I am using the SDL-1.2.15 unfortunately, I guess it's time to upgrade to SDL2 :)

 

Maybe upgrading will fix my problem with toggling full screen and losing  loaded textures in OpenGL. But that fix is for another day :)

 

Once I get controls class working I'll post it here, so anyone else can use it. If not I'll keep asking questions :)



#8 Servant of the Lord   Crossbones+   -  Reputation: 17272

Like
1Likes
Like

Posted 30 June 2013 - 12:03 PM

The two functions I linked to are for SDL 1.2, and do exactly what I think you are asking for. smile.png


It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.

[Fly with me on Twitter] [Google+] [My broken website]

All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.                                                                                                                                                       [Need free cloud storage? I personally like DropBox]

Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal


#9 Konrad Jablonski   Members   -  Reputation: 482

Like
0Likes
Like

Posted 02 July 2013 - 07:36 PM

Hehe yeah I've miss read it my bad. I still managed to upgrade my engine with SDL2.0 and everything seems to work besides the controls :P. Just want to let you guys know I haven't forgotten about this post and I will post this class here once its absolutely glitches :) So maybe it could help someone in the future. Hopefully I will find some time tomorrow to finish it up.



#10 BeerNutts   Crossbones+   -  Reputation: 2576

Like
0Likes
Like

Posted 03 July 2013 - 12:10 PM

One other thing to consider (although this could be done at a higher layer).  Typically, users want to map their keys/buttons to do different things.  So, in my Input manager, I give it enumerations like KEY_MOVE_LEFT, KEY_MAIN_FIRE, KEY_SPECIAL_FIRE, KEY_JUMP, etc to test for key presses.  And the InputManager reads some configuration file to determine which actual keys (or buttons) map to what ability.  So, shooting a gun might be Left Mouse Button for one user, but Right Ctrl for another.

 

Good luck!


My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)

#11 Konrad Jablonski   Members   -  Reputation: 482

Like
0Likes
Like

Posted 03 July 2013 - 04:59 PM

Oh wow, that's really good idea BeerNuts! Thanks :) Will definitely look into doing that :)






Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS