PDA

View Full Version : One way to do programmable control inputs in games :)



paul_nicholls
19-10-2009, 03:51 AM
If anyone is interested in how I have done my configurable inputs for my PGDAnnual entry, Day Of Destruction!, this is how:

First, I have these types/constants:


{...}Const
cLeftButton = 1;
cMiddleButton = 2;
cRightButton = 3;

cButton1 = 1;
cButton2 = 2;
cButton3 = 3;
cButton4 = 4;
cButton5 = 5;
cButton6 = 6;
cButton7 = 7;
cButton8 = 8;
cButton9 = 9;
cButton10 = 10;
{...}
Type
{................................................. .............................}
TControl = (
eControl_InsertCredit,
eControl_StartGame,
eControl_PrimaryWeapon,
eControl_SecondaryWeapon,
eControl_HighScoreClick,
eControl_TakeScreenshot
);
{................................................. .............................}

{................................................. .............................}
TInputType = (
eInputType_Mouse,
eInputType_Joystick,
eInputType_Keyboard
);
{................................................. .............................}

{................................................. .............................}
Const
cControlStr : Array[TControl] Of AnsiString = (
'insert_credit',
'start_game',
'primary_weapon',
'secondary_weapon',
'highscore_click',
'take_screenshot'
);


I have this array in my game which uses the control and input types from above:

FControls : Array[TControl,TInputType] Of Integer;

I have these routines in my game to get/set the FControls array:

Function TGame.GetControl(Const AControl : TControl;
Const AInputType : TInputType) : Integer;
Begin
Result := FControls[AControl,AInputType];
End;
{................................................. .............................}

{................................................. .............................}
Procedure TGame.SetControl(Const AControl : TControl;
Const AInputType : TInputType;
Const AValue : Integer);

Begin
FControls[AControl,AInputType] := AValue;
End;


I have these routines that do the actual parsing of my control input type strings and call SetControl():


Function SDLKeyFromStr(Const Str : AnsiString) : Integer;
Var
i : Integer;
Name : AnsiString;
Begin
Result := -1;
For i := 0 To High(Word) Do
Begin
Name := StringReplace(LowerCase(SDL_GetKeyName(i)),'sdlk_' ,'',[]);
If Name = LowerCase(Str) Then
Begin
Result := i;
Exit;
End;
End;
End;
{................................................. .............................}

{................................................. .............................}
Procedure TGame.ParseControlInput(Const AControl : TControl; AValueStr : AnsiString);
Begin
If Pos('mb_',AValueStr) = 1 Then
Begin
Delete(AValueStr,1,Length('mb_'));
If AValueStr = 'left' Then
SetControl(AControl,eInputType_Mouse,cLeftButton)
Else
If AValueStr = 'middle' Then
SetControl(AControl,eInputType_Mouse,cMiddleButton )
Else
If AValueStr = 'right' Then
SetControl(AControl,eInputType_Mouse,cRightButton)
End
Else
If Pos('jb_',AValueStr) = 1 Then
Begin
Delete(AValueStr,1,Length('jb_'));
Try
SetControl(AControl,eInputType_Joystick,cButton1 + (StrToInt(AValueStr) - cButton1));
Except
SetControl(AControl,eInputType_Joystick,cButton1);
End;
End
Else
If Pos('kb_',AValueStr) = 1 Then
Begin
Delete(AValueStr,1,Length('kb_'));
SetControl(AControl,eInputType_Keyboard,SDLKeyFrom Str(AValueStr));
End;
End;

I have this code below that reads in the .ini file control inputs and uses the above routines:

Type
{................................................. .............................}
TParseControlInput = Procedure(Const AControl : TControl; AValueStr : AnsiString) Of Object;
{................................................. .............................}

{................................................. .............................}

{................................................. .............................}
Procedure GetControlInputs(Const AIniFile : TIniFile;
Const AControl : TControl;
Const ADefault : AnsiString;
Const AParseInput : TParseControlInput);
Var
InputTypeStr : AnsiString;
sl : TStringList;
i : Integer;
Begin
sl := TStringList.Create;
Try
sl.CommaText := LowerCase(AIniFile.ReadString('controls',cControlS tr[AControl],ADefault));
For i := 0 To sl.Count - 1 Do
Begin
InputTypeStr := sl.Strings[i];
AParseInput(AControl,InputTypeStr);
End;
Finally
sl.Free;
End;
End;

Here is the final bits of code to use the GetControlInput() routine:


ConfigFile := TIniFile.Create(ModulePath + ChangeFileExt(ModuleName,'.ini'));
Try
SDL_Init(SDL_INIT_VIDEO Or SDL_INIT_AUDIO Or SDL_INIT_JOYSTICK);

FullScreen := ConfigFile.ReadBool('settings','fullscreen' ,False);
Game.MusicEnabled := ConfigFile.ReadBool('settings','music_enabled',Tru e);
Game.SoundEnabled := ConfigFile.ReadBool('settings','sound_enabled',Tru e);
Game.FPSEnabled := ConfigFile.ReadBool('settings','fps_enabled' ,True);

GetControlInputs(ConfigFile,eControl_HighscoreClic k ,'kb_lctrl,kb_rctrl,left_jb_,jb_1' ,Game.ParseControlInput);

GetControlInputs(ConfigFile,eControl_PrimaryWeapon ,'mb_left,jb_1' ,Game.ParseControlInput);
GetControlInputs(ConfigFile,eControl_SecondaryWeap on,'mb_right,jb_2' ,Game.ParseControlInput);

GetControlInputs(ConfigFile,eControl_InsertCredit ,'kb_c,mb_right,jb_9' ,Game.ParseControlInput);
GetControlInputs(ConfigFile,eControl_StartGame ,'kb_1,mb_left,jb_10' ,Game.ParseControlInput);
GetControlInputs(ConfigFile,eControl_TakeScreensho t ,'kb_s,jb_5' ,Game.ParseControlInput);

SDL_Quit;
Finally
ConfigFile.Free;
End;

Here is an example of me checking the Controls array to see if the input(s) match to do different actions:

Procedure TState_PlayGame.OnJoystickUp(Which: UInt8; Button: UInt8; State: SInt16);
Const
cInputType = eInputType_Joystick;
Var
Game : TGame;
x,y : Single;
Begin
Game := TGame(FOwner);
x := FCrossHairs.x;
y := FCrossHairs.y;

If Button = Game.Control[eControl_PrimaryWeapon,cInputType] Then
FirePrimaryWeapon(x,y,Button,Ord(cInputType))
Else
If Button = Game.Control[eControl_SecondaryWeapon,cInputType] Then
FireSecondaryWeapon(x,y,Button,Ord(cInputType));
End;

Maybe I will encapsulate some of that code into a TControl (or similar) class to make it a bit neater, but for now it works just fine :)

I hope this helps someone else :)

cheers,
Paul

paul_nicholls
20-10-2009, 01:43 AM
30 views and no comments? ;)

chronozphere
20-10-2009, 05:38 AM
I am using a similar system, which supports both buttons and axises (what is the english plural for axis ??? ?). Axises can be used to handle mouse coordinates or analog sticks on a gamepad. You can Bind and Unbind certain keycodes (or axiscodes) like N3DKEY_ESCAPE or N3DKEY_SPACE to a slot with a number between 0..31.
In consequence, you can call KeyState(Slot) or AxisState(Slot) to retrieve a keystate (Press, Pressed, Release, Released) for the key's or a Float value between 0..1 for the axises. Finally, the module can also handle text input. :)

My system is based on DirectInput, but I might make a similar system with SDL in the future.

You're code looks good, but I don't need it ATM. 8)

paul_nicholls
20-10-2009, 05:45 AM
I am using a similar system, which supports both buttons and axises (what is the english plural for axis ??? ?). Axises can be used to handle mouse coordinates or analog sticks on a gamepad. You can Bind and Unbind certain keycodes (or axiscodes) like N3DKEY_ESCAPE or N3DKEY_SPACE to a slot with a number between 0..31.
In consequence, you can call KeyState(Slot) or AxisState(Slot) to retrieve a keystate (Press, Pressed, Release, Released) for the key's or a Float value between 0..1 for the axises. Finally, the module can also handle text input. :)

My system is based on DirectInput, but I might make a similar system with SDL in the future.

You're code looks good, but I don't need it ATM. 8)



That's fine, I wasn't expecting anyone to suddenly use it, LOL!

Yours sounds neat...any code snippets you could share?

BTW, I believe the plural for axis is axii ;D

cheers,
Paul

NecroDOME
20-10-2009, 07:39 AM
I have a kind of similar approach, but I bind my keys at runtime to 'actions'.