PDA

View Full Version : DirectInput Limiting Movement



Memphis
25-03-2008, 09:58 PM
hi there, i've been trying to find something on limiting the users movement on my game.. i use directinput in which it detects keypress and moves character, but i have no idea how todo this and its really aggrivating me now...


procedure TFury.Render;
begin
if F_DXDevice = nil then exit;

// clear the backbuffer to black
F_DXDevice.Clear(0, nil, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.0, 0);

// Rendering of scene objects can happen here
F_DXDevice.BeginScene;
begin
//get user input.
ProcessKBInput;

F_MapBuilder.DrawBricks;
end;
F_DXDevice.EndScene;

// Present the backbuffer contents to the display
F_DXDevice.Present(nil, nil, 0, nil);
end;


the processkbinput is as follows:


procedure TFury.ProcessKBInput;
var
buffer: array[0..255] of byte;
hr: HResult;
begin
hr := F_DIDevice.GetDeviceState(SizeOf(buffer), @buffer);
if FAILED(hr) then begin
WriteLN('FAIL');
exit; //**** knows.... probably try to reaquire, todo...
end;

if (buffer[DIK_LEFT] and $80) <> 0 then //... need to move character smoothly not 1000mph...
F_MapBuilder.Player.PosX := F_MapBuilder.Player.PosX - 1
else if (buffer[DIK_RIGHT] and $80) <> 0 then
F_MapBuilder.Player.PosX := F_MapBuilder.Player.PosX + 1; //way to fast :/
end;


so basically when i press left key, the character moves farrrrrrr too quick and vice versa with right, i considering only checking input after x amount of seconds, but then it will miss keypress...

any help would be greatly appreciated


*EDIT*

i slowed down the movement by using single for posx and y, however problem is that if the framerate increases or drops it also changes the movement rate


if (buffer[DIK_LEFT] and $80) <> 0 then //... need to move character smoothly not 1000mph...
F_MapBuilder.Player.PosX := F_MapBuilder.Player.PosX - 0.05
else if (buffer[DIK_RIGHT] and $80) <> 0 then
F_MapBuilder.Player.PosX := F_MapBuilder.Player.PosX + 0.05;


-MM

cronodragon
25-03-2008, 10:49 PM
You should multiply the step (0.05) by the number of seconds or milliseconds elapsed since the last frame. That way you would be moving the character N units per second/millisecond.

Memphis
25-03-2008, 10:58 PM
You should multiply the step (0.05) by the number of seconds or milliseconds elapsed since the last frame. That way you would be moving the character N units per second/millisecond.

yep i jus sorted it, thanks anyways for replying ^ that was spot on answer, thanks alot


if (buffer[DIK_LEFT] and $80) <> 0 then
F_MapBuilder.Player.PosX := F_MapBuilder.Player.PosX - (((tick - F_LastCheck) / 60) * 5)
else if (buffer[DIK_RIGHT] and $80) <> 0 then
F_MapBuilder.Player.PosX := F_MapBuilder.Player.PosX + (((tick - F_LastCheck) / 60) * 5);

Memphis
26-03-2008, 08:16 PM
hi i've done a bit more testing and im still doing it wrong by looks of it, if i set max 60fps then moving left or right drops to a snails pace... i've tried alsorts of way, and my math is terrible in compare to what it used to be... lol any sample of what you meant? maybe i misunderstood...

thanks in advance

cronodragon
26-03-2008, 08:47 PM
You really don't need to divide by 60, and removing that will increase the speed 60 times:

if (buffer[DIK_LEFT] and $80) <> 0 then
F_MapBuilder.Player.PosX := F_MapBuilder.Player.PosX - ((tick - F_LastCheck) * 5)
else if (buffer[DIK_RIGHT] and $80) <> 0 then
F_MapBuilder.Player.PosX := F_MapBuilder.Player.PosX + ((tick - F_LastCheck) * 5);

It all depends on the size of your world... :)

Memphis
26-03-2008, 09:29 PM
hi, thanks for responding...

problem is i need it to move the character at the same speed no matter what framerate is drawn, sometime people may get 60fps for me i get 5000fps, but i want them both to move at the same speed no matter of the fps.

thanks

cronodragon
26-03-2008, 10:18 PM
Yes, that's the idea behind multiplying by the time elapsed:

(tick - F_LastCheck) * 5

If the time elapsed is large (meaning a low frame rate), you will get a large value, making the step larger. If the time elapsed is small (meaning a fast frame rate), you will get a small value, making the step smaller. If the time elapsed is being calculated properly, you should get a constant speed, no matter if the frame rate is going up and down while animating. :)

Memphis
26-03-2008, 11:19 PM
hi, thank you very much, i understand now... but it seems to jitter

if i set vsync mode on, limiting at 60fps and i add code as:


d := 5;

if (buffer[DIK_A] and $80) <> 0 then
F_MapBuilder.Player.PosX := F_MapBuilder.Player.PosX + d
else if (buffer[DIK_D] and $80) <> 0 then
F_MapBuilder.Player.PosX := F_MapBuilder.Player.PosX - d;

if (buffer[DIK_W] and $80) <> 0 then
F_MapBuilder.Player.PosY := F_MapBuilder.Player.PosY + d
else if (buffer[DIK_S] and $80) <> 0 then
F_MapBuilder.Player.PosY := F_MapBuilder.Player.PosY - d;


then it moves perfectly, no jitter or anything. if i then change to:


d := (F_ThisTick - F_LastTick) * 0.5;

if (buffer[DIK_A] and $80) <> 0 then
F_MapBuilder.Player.PosX := F_MapBuilder.Player.PosX + d
else if (buffer[DIK_D] and $80) <> 0 then
F_MapBuilder.Player.PosX := F_MapBuilder.Player.PosX - d;

if (buffer[DIK_W] and $80) <> 0 then
F_MapBuilder.Player.PosY := F_MapBuilder.Player.PosY + d
else if (buffer[DIK_S] and $80) <> 0 then
F_MapBuilder.Player.PosY := F_MapBuilder.Player.PosY - d;


if you'd like to check what i mean >

http://www.meka-meka.com/meka/FuryEngine.rar

thanks alot for help

chronozphere
27-03-2008, 10:02 AM
Why not use QueryPerformanceCounter and QueryPerformanceFrequency?? They will be a lot more accurate than GetTickCount (which i assume you use now). I alway's calculate the "FrameTime", which is the time it took to render the last frame. I make this value globally available and i multiply it when moving/scaling etc...


Obj.x := Obj.x + 100*FrameTime;


Will move the object 100 pixels to the right in one second. When running @ 60FPS, your FrameTime should be 1/60.

Hope it helps. ;)

Memphis
27-03-2008, 07:16 PM
hi, thannkssssssssssssssss, i have changed to this and it works perfect, doing as you said thanks very much :DDDDD

for anyone else it may help to see, i have changed to >

private's >

F_Freq : Int64;
F_DeltaTime : Single;
F_LastTime : Single;
F_ThisTime : Single;



procedure TFury.ProcessKBInput;
const
iSpeed = 60;
var
buffer: array[0..255] of byte;
hr: HResult;

d: Double;
begin
hr := F_DIDevice.GetDeviceState(SizeOf(buffer), @buffer);
if FAILED(hr) then begin
F_DIDevice.Acquire;
exit;
end;

d := iSpeed * F_DeltaTime;

if (buffer[DIK_A] and $80) <> 0 then
F_MapBuilder.Player.PosX := F_MapBuilder.Player.PosX + d
else if (buffer[DIK_D] and $80) <> 0 then
F_MapBuilder.Player.PosX := F_MapBuilder.Player.PosX - d;

if (buffer[DIK_W] and $80) <> 0 then
F_MapBuilder.Player.PosY := F_MapBuilder.Player.PosY + d
else if (buffer[DIK_S] and $80) <> 0 then
F_MapBuilder.Player.PosY := F_MapBuilder.Player.PosY - d;
end;

procedure TFury.Render;
var
iTime: Int64;
begin
if F_DXDevice = nil then exit;

// clear the backbuffer to black
F_DXDevice.Clear(0, nil, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.0, 0);

// Rendering of scene objects can happen here
F_DXDevice.BeginScene;
begin
//get user input.
ProcessKBInput;

F_MapBuilder.DrawBricks;
end;
F_DXDevice.EndScene;

// Present the backbuffer contents to the display
F_DXDevice.Present(nil, nil, 0, nil);

QueryPerformanceCounter(iTime);
F_ThisTime := (iTime / F_Freq);
F_DeltaTime := F_ThisTime - F_LastTime;

F_LastTime := F_ThisTime;
end;


thanks again :)

-MM

chronozphere
27-03-2008, 08:19 PM
Glad i could help ;)

lordzero
30-03-2008, 05:48 AM
can you show your code about frequency?

using this without interval dont will use 100% of CPU?

Memphis
08-03-2009, 02:52 PM
bringing back a dead post to save creating a new thread. again similar problem only i was working on a new 3d engine.

i've been working on it for a long time, but only noticed this problem due to the fps changing.

vars:



iMoveSpeed: Single = 1;
iFrameRate: Cardinal; //obviously the fps




if (buffer[DIK_A] and $80) <> 0 then begin
Cam.x := Cam.x + cos(Cam.RotX + PI/2) * (iMoveSpeed / iFrameRate);
Cam.z := Cam.z + sin(Cam.RotX + PI/2) * (iMoveSpeed / iFrameRate);
end else if (buffer[DIK_D] and $80) <> 0 then begin
Cam.x := Cam.x + cos(Cam.RotX - PI/2) * (iMoveSpeed / iFrameRate);
Cam.z := Cam.z + sin(Cam.RotX - PI/2) * (iMoveSpeed / iFrameRate);
end;

if (buffer[DIK_W] and $80) <> 0 then begin
Cam.x := Cam.x + cos(Cam.RotX) * (iMoveSpeed / iFrameRate);
Cam.y := Cam.y - cos(Cam.RotY + PI/2) * (iMoveSpeed / iFrameRate);
Cam.z := Cam.z + sin(Cam.RotX) * (iMoveSpeed / iFrameRate);
end else if (buffer[DIK_S] and $80) <> 0 then begin
Cam.x := Cam.x - cos(Cam.RotX) * (iMoveSpeed / iFrameRate);
Cam.y := Cam.y + cos(Cam.RotY + PI/2) * (iMoveSpeed / iFrameRate);
Cam.z := Cam.z - sin(Cam.RotX) * (iMoveSpeed / iFrameRate);
end;


this works for moving around, however depending on framerate, it goes faster or slower.. i cant figure out how to keep it steady, note i use queryperformance for my counter/fps calculation.

thanks in advance.

;MM;

JSoftware
08-03-2009, 04:41 PM
My guess is that you only update iFrameRate every second or so :) I would do it like this:


iMoveSpeed: single = 1; //Units/second
fDeltaTime: single; //fraction of second

LastTick, Tick: Cardinal; //Just use GetTickCount. It's precise enough and you don't get problems with changing frequencies


Every frame before calculating positions do:

Tick := GetTickCount;
fDeltaTime := (Tick-LastTick)/1000;
LastTick := Tick;



if (buffer[DIK_A] and $80) <> 0 then begin
Cam.x := Cam.x + cos(Cam.RotX + PI/2) * (iMoveSpeed * fDeltaTime);
Cam.z := Cam.z + sin(Cam.RotX + PI/2) * (iMoveSpeed * fDeltaTime);
end else if (buffer[DIK_D] and $80) <> 0 then begin
Cam.x := Cam.x + cos(Cam.RotX - PI/2) * (iMoveSpeed * fDeltaTime);
Cam.z := Cam.z + sin(Cam.RotX - PI/2) * (iMoveSpeed * fDeltaTime);
end;

if (buffer[DIK_W] and $80) <> 0 then begin
Cam.x := Cam.x + cos(Cam.RotX) * (iMoveSpeed * fDeltaTime);
Cam.y := Cam.y - cos(Cam.RotY + PI/2) * (iMoveSpeed * fDeltaTime);
Cam.z := Cam.z + sin(Cam.RotX) * (iMoveSpeed * fDeltaTime);
end else if (buffer[DIK_S] and $80) <> 0 then begin
Cam.x := Cam.x - cos(Cam.RotX) * (iMoveSpeed * fDeltaTime);
Cam.y := Cam.y + cos(Cam.RotY + PI/2) * (iMoveSpeed * fDeltaTime);
Cam.z := Cam.z - sin(Cam.RotX) * (iMoveSpeed * fDeltaTime);
end;

Memphis
08-03-2009, 07:00 PM
yes you were right my problem was my performance counter. thanks.

i will keep gettickcount in mind, i however am still using query counter, the problem was how i was doing it, adding the delta time was the solution

F_DeltaTime := (F_ThisTime - F_LastTime) / 1000;


thanks again jsoftware