![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
И опять о плавности...
HPR © (2006-02-05 21:31:00)В общем решил я забить на таймеры и сделал бесконечный цикл (координаты объектов рассчитываются с плавным шагом dt) в надежде, что рывки прекратятся. Но рывки остались :( К примеру, при запущенном и играющем WinAmp-е Zuma работает плавно, а в моей есть рывки. FPS высокий = 85 при включенном vsync. С таймерами такая же фигня...
Что делаете, чтобы добиться плавности?
HPR (2006-02-08 13:45:00) [1]Кстати, камрады по цеху, я ведь забыл вас поблагодарить! БОЛЬШОЕ спасибо ;)
HPR (2006-02-07 21:42:00) [2]В общем поставил QueryPerformanceCounter и Sleep. Вроде бы помогло :) Скорее всего дело в Sleep. Видимо система начинала понижать приоритет моей игре, съедавшей 100% проц. времени.. Хотя это все только теория
VolanD666 (2006-02-07 13:43:00) [3]Использование QPC- самый оптимальный вариант. После того, как я поменял у себя GetTickCount() на QPC, движения стали значительно "плавнее" :)
Don Nikola (2006-02-07 11:43:00) [4]2HPR: QueryPerformanceCounter это обертка для rdtsc, и предназначен для замера сверхмалых отрезков времени. Использование QPF для замера-регулирования FPS, все равно что использование лазерного гироскопа для замера крена детского велосипеда.
Плюс ко всему, чисто теоретически, QPF может работать не на всех машинах, но это скорее всего относиться к машинам before-Pentium.
HPR (2006-02-06 21:43:00) [5]to XProger: Я вызываю timeBeginPeriod(1), так что период= 1 мс. Но QueryPerformanceCounter говорят действительно точнее, попробую использовать...
VolanD666 (2006-02-06 20:02:00) [6]Спасибо! :)
XProger (2006-02-06 19:49:00) [7]* 1) Трассировка отрезка %)
А по-второму методу у меня есть примерный код:
function ENG_time: Integer;
var
T : LARGE_INTEGER;
F : LARGE_INTEGER;
begin
QueryPerformanceFrequency(Int64(F));
QueryPerformanceCounter(Int64(T));
Result := Trunc(1000 * T.QuadPart / F.QuadPart);
end;
begin
ups_delta := 10; // 100 обновлений в секунду (в среднем)
ups_time_old := ENG_time - ups_delta;
ups_time := ENG_time;
if ENG_Init then // Инициализация
while not eng_isquit do
begin
while PeekMessage(msg, 0, 0, 0, PM_REMOVE) do
begin
TranslateMessage(msg);
DispatchMessage(msg);
end;
if eng_isquit then
break;
Time := ENG_time;
Time_Delta := Time - ups_time_old;
while Time_Delta >= ups_delta do
begin
ENG_update; // обновление игры
dec(Time_Delta, ups_delta);
end;
ups_time_old := Time - Time_Delta;
ENG_render; // отрисовка
end;
ENG_Free; // высвобождение ресурсов - завершение работы движка
end;
Также в WndProc
WM_CLOSE :
begin
ENG_Free;
eng_isquit := True;
end;
Что-то в этом духе...
XProger (2006-02-06 19:35:00) [8]К тому моменту как программа "отойдёт" от цикла - dt будет иметь настолько большое значение, что Pos + Speed * dt - окажется далеко за стеной :)
Решение:
1) Трассировка вектора до пересечения со стеной, а там уже со сферой
2) Использовать фиксированный UPS (Updates Per Second) безо всяких dt :)
VolanD666 (2006-02-06 19:06:00) [9]Хм, а почему так? Ведь при нажатии отрисовка не происходит, а значит и положение камеры не обновляется?
VolanD666 (2006-02-06 19:04:00) [10]Руки оказались кривые... :(
XProger (2006-02-06 16:58:00) [11]VolanD666, кстати, хочешь краштест для своей игрушки?
if Key = ord('E') then for i := 1 to 1000000000 do;
Затем, во время игры, беги на стену и единожды нажми клавишу 'E', если руки прямые - сквозь стену не пролетишь :)
VolanD666 (2006-02-06 16:19:00) [12]Недавно возился с этой проблемой, хорошо попалась статья хорошая и пример к ней. Во-первых, в качестве таймера я использую QueryPerformanceCounter. Во-вторых, для плавности движения при любом приличном FPS необходимо рассчитывать коэффициент, и потом все перемещения на него умножать. Ну а рендерится это все с помощью WM.
Don Nikola (2006-02-06 15:27:00) [13]2XProgrer:
Точность timeGetTime регулируется с помощью timeBeginPeriod/timerEndPeriod
2HPR:
Я такой loop использовал:
---
procedure UpdateTime;
var
CurrentTime, Delta: dword;
begin
CurrentTime:=timeGetTime;
if not ApplicationActive then
begin
LastTickTime:=CurrentTime;
Exit;
end;
if CurrentTime>=LastTickTime then
Delta:=CurrentTime-LastTickTime else
Delta:=CurrentTime+($FFFFFFFF-LastTickTime);
LastTickTime:=CurrentTime;
Time:=Time+Delta;
if (Time-LastFPSTime)>=1000 then
begin
FPS:=FramesAtLastSecond;
FramesAtLastSecond:=0;
LastFPSTime:=Time;
end;
if (not LimitFPS)or((Time-LastFrameTime)>=DesiredFrameTime) then
begin
if LastFrameProcessed then
begin
LastFrameProcessed:=false;
LastFrameTime:=Time;
PostMessage(MainWindow,WM_DOFRAME,0,0);
end;
end;
end;
procedure MainLoop;
var
Msg: TMsg;
begin
PeekMessage(Msg,0,0,0,PM_NOREMOVE);
while Msg.Message<>WM_QUIT do
begin
if PeekMessage(Msg,0,0,0,PM_REMOVE) then
DispatchMessage(Msg) else
begin
UpdateTime;
Sleep(SleepValue); //Do not use all possible CPU time
end;
end;
end;
---
Фрейм отрабатывается в обработчике WM_DOFRAME, который может быть равен WM_APP+1, например.
При SleepValue>=1, программа всеьма скромно пожирает ресурсы CPU.
Единственная проблема - этот код не предполагает наличие VCL, так что использовать этот код просто копипастом нельзя, но представление о методе дает.
Nic (2006-02-06 11:54:00) [14]Я делаю так. Считаю сколько секунд прошло с рендеринга прошедшего кадра до нынешнего. Из этого числа путём преобразований получаю переменную GLOBAL_K. Потом всё что передвигается перемножаю на этот глобальный коэффициент. Получаю FPS независимый рендеринг. Но при резких скачках FPS некоторая грубость перемещений заметна. С WinAmp'ом не экспериментировал.
XProger (2006-02-06 11:35:00) [15]
delta := (TimeNew-TimeOld)/1000;
TimeOld := TimeNew;
+ ко всему, точность timeGetTime может быть не менее 20 мс (насколько я помню) Попробуй вместо неё использовать следующий код:
function ENG_time: Integer;
var
T : LARGE_INTEGER;
F : LARGE_INTEGER;
begin
QueryPerformanceFrequency(Int64(F));
QueryPerformanceCounter(Int64(T));
Result := Trunc(1000 * T.QuadPart / F.QuadPart);
end;
HPR (2006-02-06 11:16:00) [16]Дело-то всегда в руках, поэтому хочется их натренировать с вашей помощью
Вот привожу главный кусок:
while not NeedToExitGame do
begin
TimeNew := timeGetTime;
delta := (TimeNew-TimeOld)/1000;
Application.ProcessMessages; // обработка сообщений
DXInput.Update; // обновление данных ввода
DXDraw.BeginScene;
Common.UpdateObjects(delta); // обновление объектов
Common.RenderObjects; // рендер объектов в главную поверхность
DXDraw.EndScene;
DXDraw.Flip;
TimeOld := TimeNew;
end;
Что не так?
P.S. Вообще довольно плавно все движется, но опять же при включенном WinAmp рывки...
P.P.S. Использую компонент UnDelphiX с включенным 3D-ускорением
Кефир87 (2006-02-05 22:40:00) [17]Может дело в руках? 8)