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.