Page 1 of 1

Handling keyboard input.

Posted: Tue Dec 09, 2008 7:22 am
by ismetteren
I have wondered about what would be the best way to handle keyboard input. I am using allegro, so all the basic stuff is already done.

I have thougt about two method's. One is to make all actors that can't be controlled in the actor vector, and then make the player object(s) outside that vector, and control them directly from the main loop. This just dosent seem very dynamic.

The other way i have thougt about is to give all actors a proccesInput() function and then only put some thing in it in the player class(or other classes that took keyboard inout)(player extends actor). The problem here is that all players then would have same controls, unless you could give the player object a set of controls, in this way you could also change controls in the game.

I have tried to do this, but i am getting a runtime error.

this is the declaration in player.h:

Code: Select all

bool *controls[4];
and this is from the constructor in player.cpp(i know that i will have to pass an array as argument):

Code: Select all

*controls[0] = key[KEY_UP];
*controls[1] = key[KEY_LEFT];
*controls[2] = key[KEY_RIGHT];
*controls[3] = key[KEY_P];

...
//the way a am using the control array:
void Player::processInput()
{
    if(*controls[0] == true)
    {
        setMoveing(true);
    }
    else
    {
        setMoveing(false);
    }
    
    if(*controls[1] == true)
    {
        addDirection(5);
    }
    if(*controls[2] == true)
    {
        addDirection(-5);
    }
}
as i said, this gives a runtime error(i am pretty sure it is this, because the runtime error started to occur just after i made this).

So, can you see what is wrong whit that code, and how does you procces input?

Sorry for my bad englsih.

Re: Handling keyboard input.

Posted: Tue Dec 09, 2008 8:35 am
by bugmenot
For the code: Why are you using pointers for the boolean array? Are you trying to dynamically map a set of keys from Allegro's input data?

As for the design, I would think about implementing a design where each actor would have a interface pointer to a controller type (player input or AI) and the actor queries the controller interface about the state of the input in its update/process function.

This design also allows for flexibility for player/actor switching either for gameplay or debug purposes as switching control between actors is a simple case of switching controller interfaces.

Re: Handling keyboard input.

Posted: Tue Dec 09, 2008 10:02 am
by ismetteren
bugmenot wrote:For the code: Why are you using pointers for the boolean array? Are you trying to dynamically map a set of keys from Allegro's input data?
...uhmmm... yes...
bugmenot wrote: As for the design, I would think about implementing a design where each actor would have a interface pointer to a controller type (player input or AI) and the actor queries the controller interface about the state of the input in its update/process function.

This design also allows for flexibility for player/actor switching either for gameplay or debug purposes as switching control between actors is a simple case of switching controller interfaces.
I'm not sure about what you mean. Are there interfaces in c++(when i think of a interface, a think of a java style interface)? I tried google, and it seems there isent, so what do you mean about interface.

And could you give an example of what a controller object would be like?

Re: Handling keyboard input.

Posted: Tue Dec 09, 2008 10:10 am
by bugmenot
ismetteren wrote:...uhmmm... yes...
In which case (although I don't recommend it), the array is set of 4 bool pointers. It is crashing because I assume they are either initialised to NULL or not initialised at all. Either way trying to dereference an element in the controls array will (EDITcause) crash it. What you intended to do is point to the address of an element in key array and not copy the value:

Code: Select all

controls[0] = &key[KEY_UP];
Also note that key is a char array, not a bool array: http://alleg.sourceforge.net/stabledocs ... 6.html#key
ismetteren wrote:I'm not sure about what you mean. Are there interfaces in c++(when i think of a interface, a think of a java style interface)? I tried google, and it seems there isent, so what do you mean about interface.

And could you give an example of what a controller object would be like?
I write up an example later as I am at work at the moment.

Re: Handling keyboard input.

Posted: Tue Dec 09, 2008 12:31 pm
by avansc
have you figured out why it crashing at run time. im not sure but i think it probably happens when you ary and assing a value to one of your pointers.
i think you have to allocate memory for it first.

Re: Handling keyboard input.

Posted: Tue Dec 09, 2008 1:16 pm
by MarauderIIC
I have now sig'd your typo. :)
avansc wrote:im not sure but i think it probably happens when you ary and assing a value to one of your pointers.

Re: Handling keyboard input.

Posted: Tue Dec 09, 2008 1:23 pm
by avansc
MarauderIIC wrote:I have now sig'd your typo. :)
avansc wrote:im not sure but i think it probably happens when you ary and assing a value to one of your pointers.
haha, thats hilarious. i was typing to a friend in south africa. and was talking/typing in my native tongue, i guess somehow that made me mess up.

(thanks my excuse and im sticking to it.)

Re: Handling keyboard input.

Posted: Tue Dec 09, 2008 3:20 pm
by bugmenot
For simplicity, this is done in one source file. I assume you know enough C++ to split this into separate files:
(WARNING: massive code dump)

Code: Select all

// Forward declarations
class IController;

//--------------------------------------------------------------
class Actor
{
public:
	Actor();
	virtual ~Actor();
	
	virtual void Process() = 0;
	void AttachController( const IController * pController );
	void DetachController();

protected:
	const IController * mpController;
};

Actor::Actor() : mpController(NULL)
{
}

Actor::~Actor()
{
}

void Actor::AttachController( const IController * pController )
{
	// NOTE: You may want to some checks/assets here to check for NULL pointers
		// or if you are removing control from another controller
	mpController = pController;
}

void Actor::DetachController()
{
	mpController = NULL;
}

//--------------------------------------------------------------
namespace ButtonActions
{
	enum eButtonActions
	{
		UP = 0,
		LEFT,
		RIGHT,
		ACTION,
		MAX_LIMIT
	};
};

class InputData
{
public:
	InputData();
	bool mButtons[ButtonActions::MAX_LIMIT];
};

InputData::InputData()
{
	for( int i = 0; i < ButtonActions::MAX_LIMIT; i+=1 )
	{
		mButtons[i] = false;
	}
}

//--------------------------------------------------------------
class IController
{
public:
	virtual ~IController();
	virtual InputData GetInputData( const Actor * pActor ) const = 0;
};

IController::~IController()
{
}

//--------------------------------------------------------------
class Car : public Actor
{
public:
	void Process();
};

void Car::Process()
{
	if( mpController != NULL )
	{
		InputData inputData = mpController->GetInputData( this );
		
		if( inputData.mButtons[ButtonActions::LEFT] )
		{
		
		}
		
		if( inputData.mButtons[ButtonActions::RIGHT] )
		{
		
		}
		
		if( inputData.mButtons[ButtonActions::UP] ) 
		{
		
		}
		
		if( inputData.mButtons[ButtonActions::ACTION] )
		{
		
		}
	}
}

//--------------------------------------------------------------
class PlayerController : public IController
{
public:
	InputData GetInputData( const Actor * pActor ) const;
};

InputData PlayerController::GetInputData( const Actor * pActor ) const
{
	InputData inputData;
	if( key[KEY_UP] )
	{
		inputData.mButtons[ButtonActions::UP] = true;
	}
	
	if( key[KEY_RIGHT] )
	{
		inputData.mButtons[ButtonActions::RIGHT] = true;
	}
	
	if( key[KEY_LEFT] )
	{
		inputData.mButtons[ButtonActions::LEFT] = true;
	}
	
	if( key[KEY_P] )
	{
		inputData.mButtons[ButtonActions::ACTION] = true;
	}
	
	return inputData;
}

//--------------------------------------------------------------
class AICarController : public IController
{
public:
	InputData GetInputData( const Actor * pActor ) const;
};

InputData AICarController::GetInputData( const Actor * pActor ) const
{
	InputData inputData;
	
	// NOTE: This is a bad assumption, look at using RTTI or writing
		// out your own to see if the Actor object is the type we
		// are assuming it is before doing the cast
	if( pActor != NULL )
	{
		const Car * pCar = static_cast<const Car *>( pActor );
		// Get some information from the Car to figure out what 
			// to populate the inputData with. E.g. If we need to 
			// turn left, then make mLeftPressed = true;
	}
	
	return inputData;
}


//--------------------------------------------------------------
int main()
{
	Car carA;
	Car carB;
	
	PlayerController playerController;
	AICarController aiCarController;
	
	// Attach the player controller to car A
	carA.AttachController( &playerController );
	
	// Attach the AI controller to the other car
	carB.AttachController( &aiCarController );
	
	// I assume you have a vector of Actor pointers that you iterate
		// through and call Process on
}
That is the general backbone of what I was suggesting above but doesn't yet solve the issue of multiple players on the same keyboard. For this we need to iterate over PlayerController class to accept key mappings.

Code: Select all

//--------------------------------------------------------------
class PlayerController : public IController
{
public:
	PlayerController();
	InputData GetInputData( const Actor * pActor ) const;
	void MapActionToKey( ButtonActions::eButtonActions buttonAction, int key );
	
private:
	int mKeyMap[ButtonActions::MAX_LIMIT];
};

PlayerController::PlayerController()
{
	for( int i = 0; i < ButtonActions::MAX_LIMIT; i+=1 )
	{
		mKeyMap[i] = -1;
	}
}

InputData PlayerController::GetInputData( const Actor * pActor ) const
{
	InputData inputData;
	
	for( int i = 0; i < ButtonActions::MAX_LIMIT; i+=1 )
	{
		inputData.mButtons[i] = mKeyMap[i] != -1 && key[mKeyMap[i]];
	}
	
	return inputData;
}

void PlayerController::MapActionToKey( ButtonActions::eButtonActions buttonAction, int key )
{
	mKeyMap[buttonAction] = key;
}
Now we can have a Controller instance per player with their own bindings:

Code: Select all

//--------------------------------------------------------------
int main()
{
	Car carA;
	Car carB;
	
	PlayerController playerController1;
	PlayerController playerController2;
	
	playerController1.MapActionToKey( ButtonActions::UP, KEY_UP );
	playerController1.MapActionToKey( ButtonActions::LEFT, KEY_LEFT );
	playerController1.MapActionToKey( ButtonActions::RIGHT, KEY_RIGHT );
	playerController1.MapActionToKey( ButtonActions::ACTION, KEY_P );
	
	playerController2.MapActionToKey( ButtonActions::UP, KEY_W );
	playerController2.MapActionToKey( ButtonActions::LEFT, KEY_A );
	playerController2.MapActionToKey( ButtonActions::RIGHT, KEY_D );
	playerController2.MapActionToKey( ButtonActions::ACTION, KEY_G );
	
	carA.AttachController( playerController1 );
	carB.AttachController( playerController2 );
}
IMPORTANT
Now there are some drawbacks with this method but it is the pattern I would use in my own projects, it may be overkill for your needs. If you are only worried about multiple players on the same keyboard, then the only part you really need to look at is the Button Mapping functionality and incorporate it directly to your object.

EDIT: I forgot to say, this is by no means the best method. Different problems require different solutions, there is rarely no one size fit all.
EDIT2: Realised I missed a huge chunk of code out.

Re: Handling keyboard input.

Posted: Wed Dec 10, 2008 10:15 am
by ismetteren
Thanks! :worship:

This really helped me, i got the main point, to use it i think, i have to read it throug a time or two more :D