トップへ(mam-mam.net/)

Retrieving Keyboard, Mouse, and Joystick Input

Japanese

Retrieving Keyboard, Mouse, and Joystick Input

In shooting games and other game‑related programming, you often need to check timing using a TTimer or TApplicationEvents.OnIdle with functions such as (Winapi.MMSystem.)timeGetTime, timeBeginPeriod(1), timeEndPeriod(1), or QueryPerformanceFrequency(qpf) / QueryPerformanceCounter(qpc).
In real time, you must detect the current state of the keyboard (which keys are pressed), the mouse (cursor position and button states), and the joystick controller (stick position and button states).

(Reference) My free FMX software that retrieves joystick (gamepad) input:
Shooting Game NoTitle2

Create a New Delphi Project

Launch Delphi and select File → New → Windows VCL Application to create a new project.
Drag and drop one TTimer and three TMemo components onto the form.
Click Save All to create the project folder and save the project and unit files.

Writing the Source Code

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls
  ,Winapi.MMSystem;

type
  TForm1 = class(TForm)
    Timer1: TTimer;
    Memo1: TMemo;
    Memo2: TMemo;
    Memo3: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  Timer1.Interval:=33;
  Timer1.Enabled:=True;
  Memo1.DoubleBuffered:=True;
  Memo2.DoubleBuffered:=True;
  Memo3.DoubleBuffered:=True;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var mp:TPoint;  //Mouse position
    mb:SmallInt;//Mouse button state
    sb:Integer; //Whether mouse buttons are swapped in OS settings
    k:SmallInt; //Keyboard key state
    ji: TJoyInfo;
begin
  memo1.Lines.BeginUpdate;
  memo2.Lines.BeginUpdate;
  memo3.Lines.BeginUpdate;

  memo1.Lines.Clear;
  memo2.Lines.Clear;
  memo3.Lines.Clear;
  //Current mouse position
    //Screen coordinates
    GetCursorPos(mp);
    Memo1.Lines.Add(
      'GetCursorPos (screen coordinates):'+Format('x=%4d,y=%4d',[mp.X,mp.Y])
    );
    //Convert to form coordinates
    mp:=self.ScreenToClient(mp);
    Memo1.Lines.Add(
      'Converted to form coordinates:'+Format('x=%4d,y=%4d',[mp.X,mp.Y])
    );

  //Physical mouse button states (ignores OS left/right swap)
    mb:=GetAsyncKeyState(VK_LBUTTON);
    if mb<0 then
      memo1.Lines.Add('GetAsyncKeyState(VK_LBUTTON):pressed')
    else
      memo1.Lines.Add('GetAsyncKeyState(VK_LBUTTON):not pressed');

    sb:=GetSystemMetrics(SM_SWAPBUTTON);
    if sb<>0 then
      memo1.Lines.Add('GetSystemMetrics(SM_SWAPBUTTON):mouse buttons swapped')
    else
      memo1.Lines.Add('GetSystemMetrics(SM_SWAPBUTTON):mouse buttons not swapped');


  //Keyboard key states
    k:=GetKeyState(ord('A'));
    if k<0 then
      Memo2.Lines.Add('A key:pressed)
    else
      Memo2.Lines.Add('A key:not pressed');
    k:=GetKeyState(VK_SHIFT);
    if k<0 then
      Memo2.Lines.Add('SHIFT key:pressed')
    else
      Memo2.Lines.Add('SHIFT key:not pressed');

    mb:=GetAsyncKeyState(VK_LSHIFT);
    if mb<0 then
      memo2.Lines.Add('GetAsyncKeyState(VK_LSHIFT):pressed')
    else
      memo2.Lines.Add('GetAsyncKeyState(VK_LSHIFT):not pressed');

    mb:=GetAsyncKeyState(VK_RSHIFT);
    if mb<0 then
      memo2.Lines.Add('GetAsyncKeyState(VK_RSHIFT):pressed')
    else
      memo2.Lines.Add('GetAsyncKeyState(VK_RSHIFT):not pressed');


  //■Joystick controller state
    if joyGetPos(JOYSTICKID1,@ji)=JOYERR_NOERROR then
    begin
      memo3.Lines.Add('JOYSTICKID1:connected');
      memo3.Lines.Add(
        'JOYSTICKID1:X='+Format('%5d',[ji.wXpos])
      );
      memo3.Lines.Add(
        'JOYSTICKID1:Y='+Format('%5d',[ji.wYpos])
      );
      if (ji.wButtons and JOY_BUTTON1)=JOY_BUTTON1 then
        memo3.Lines.Add('JOYSTICKID1:JOY_BUTTON1 pressed')
      else
        memo3.Lines.Add('JOYSTICKID1:JOY_BUTTON1 not pressed');

      if (ji.wButtons and JOY_BUTTON2)=JOY_BUTTON2 then
        memo3.Lines.Add('JOYSTICKID1:JOY_BUTTON2 pressed')
      else
        memo3.Lines.Add('JOYSTICKID1:JOY_BUTTON2 not pressed');
    end
    else
      memo3.Lines.Add('JOYSTICKID1:not connected');

    //ジョイスティック2
    if joyGetPos(JOYSTICKID2,@ji)=JOYERR_NOERROR then
    begin
      memo3.Lines.Add('JOYSTICKID2:接続されている');
      memo3.Lines.Add(
        'JOYSTICKID2:X='+Format('%5d',[ji.wXpos])
      );
      memo3.Lines.Add(
        'JOYSTICKID2:Y='+Format('%5d',[ji.wYpos])
      );
      if (ji.wButtons and JOY_BUTTON1)=JOY_BUTTON1 then
        memo3.Lines.Add('JOYSTICKID2:JOY_BUTTON1 pressed')
      else
        memo3.Lines.Add('JOYSTICKID2:JOY_BUTTON1 not pressed');

      if (ji.wButtons and JOY_BUTTON2)=JOY_BUTTON2 then
        memo3.Lines.Add('JOYSTICKID2:JOY_BUTTON2 pressed')
      else
        memo3.Lines.Add('JOYSTICKID2:JOY_BUTTON2 not pressed');
    end
    else
      memo3.Lines.Add('JOYSTICKID2:not connected');

  memo1.Lines.EndUpdate;
  memo2.Lines.EndUpdate;
  memo3.Lines.EndUpdate;
end;

end.

Run the Application

Execution screen: