Question

I have a 3d object in a delphi program and I am trying to program to control his movement in 3d space with arrow keys.

First problem: For single arrow keys the movement works fine - the object turns around or goes forward according to the key pressed, but it doesn't seem to work with multiple key press. In case of the UP key and Left or Right key press, it rotates but does not go forward.

Second problem: The UP key movement is somehow delayed. Only 0.5 seconds after the UP key is pressed, the object starts moving forward. Why is that?

Here's the code:

function KeyPressed(nKey: Integer): Boolean;
begin
Result := (GetAsyncKeyState(nKey) and $8000) <> 0;
end;

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
if keypressed(VK_LEFT) then
     if heroMove.angle < 360 then inc(heroMove.angle,5) else heroMove.angle:=0
else if keypressed(VK_RIGHT) then
     if heroMove.angle < 360 then inc(heroMove.angle,-5) else heroMove.angle:=0 else
if keypressed(VK_UP) then
begin
  heroMove.x:=heroMove.x+0.2*Sin(heroMove.angle*3.1415/180);
  heroMove.y:=heroMove.y+0.2*Cos(heroMove.angle*3.1415/180);
  pose:=1;
end;

if keypressed(VK_LEFT) and keypressed(VK_UP) then
begin
if heroMove.angle < 360 then inc(heroMove.angle,5) else heroMove.angle:=0;
  heroMove.x:=heroMove.x+0.2*Sin(heroMove.angle*3.1415/180);
  heroMove.y:=heroMove.y+0.2*Cos(heroMove.angle*3.1415/180);
  pose:=1;
end else
if keypressed(VK_RIGHT) and keypressed(VK_UP) then
begin
  if heroMove.angle < 360 then inc(heroMove.angle,-5) else heroMove.angle:=0;
  heroMove.x:=heroMove.x+0.2*Sin(heroMove.angle*3.1415/180);
  heroMove.y:=heroMove.y+0.2*Cos(heroMove.angle*3.1415/180);
  pose:=1;
end;

end;

procedure TForm1.FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
if Key=VK_UP then
pose:=0;
end;

P.S. The parameter "pose" just controls the animation type of the object.

Was it helpful?

Solution

What you need here is to stop using event driven programming methods. You need to switch to a more tradition game programming style.

You want to run a game loop that provides a steady pulse to your program. Each iteration of this loop you check the keyboard state. Use GetAsyncKeyState for that. Check the state of each key that you are interested in. In your game loop you will also be driving any updates to the OpenGL canvas.

I can see from your update that you are already using GetAsyncKeyState. But that really does not make a lot of sense when used in response to an OnKeyDown event. Either you use event driven synchronous I/O, or polling asynchronous I/O. I cannot understand why you are trying to mix the two paradigms.

The way you call GetAsyncKeyState looks wrong. You should write it like this:

function KeyPressed(nKey: Integer): Boolean;
begin
  Result := GetAsyncKeyState(nKey) < 0;
end;

I also suggest that in each iteration of your main loop you call GetAsyncKeyState once and once only for each key. Save the returned value to a local variable. That avoids the scenario where a key is deemed to be down at the start of the iteration, but then the same key is up at some later point in the iteration. That's an issue you need to be alive to with asynchronous I/O.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top