Форум: "WinAPI";
Текущий архив: 2004.11.21;
Скачать: [xml.tar.bz2];
ВнизТаймер Найти похожие ветки
← →
Sphinx (2004-10-09 23:09) [0]Вопрос собственно, как сделать таймер с гарантийе срабатывания через заданный промежуток времени, плюс НЕБОЛЬШОЕ отставание.
TTimer не подходит, по некоторым данным дискредность его выполнения в Win9x - 55 ms Win2k/XP - 10 ms, то есть разная для разных систем.
Мультимедийный таймер, вычитано в книге, из библиотеки winmm.dll то же не подходит, во-первых под Win2k на работе такой библиотеки не оказалось (?!!!) а во-вторых встретился с проблемой, о которой чуть ниже.
SetTimer, KillTimer - нашел в Windows SDK - работают лучше, но та же проблема. События таймера не всегда выполняются!
Приложение написано на WinAPI (правда играет ли это роль не уверен) и получается что при, скажем, движениях мышью, таймер, то ли блокируется, то ли просто не выполняется.
Пытался сделать еще и отдельным потоком, примерно так:TTimerThread.Execute;
while
begin
while ThrdRun do
begin
if GetTicCount-LastTicCount>Delay then
begin
PostMessage(TimerWinHandle, WM_USEr_TIMER);
LastTicCount:=GetTicCount;
end;
Sleep(Descrite;);
end;
end;
писал тут, возможны ошибки, но суть такая была. Работало все хорошо, если бы не одно НО. Тормозило до жути.
Подскажите как ПРАВИЛЬНО организовать таймер, события которого не пропускались бы при прочих сообщениях окну.
← →
DiamondShark © (2004-10-10 12:20) [1]Попробуйте рассказать, зачем понадобился такой таймер.
Может найдём решение не "в лоб".
← →
Nick Denry © (2004-10-10 12:44) [2]DiamondShark © (10.10.04 12:20) [1]
- Хотите об этом поговорить? (с) реклама
:)))
← →
Sphinx (2004-10-10 17:08) [3]Все очень просто - игрушка :)
нужно пересчитывать движение юнитов через равные промежутки времени, а так как при интенсивный движениях мышью события таймера не выполняются - то получается что юнит двигается рывками. Если мышь не трогать - все плавно.
Или я что-то непонял...или мне нужен какой-то хитрый таймер :)
← →
Nick Denry © (2004-10-10 19:41) [4]ДЛя начала, насколько я понял, надо использовать DirectInput, - тогда ни какие движения мышью не будут столь "интенсивными".
Далее, самый быстрый таймер в виндовс - мультимедийный татаймер...uses ...,MMsystem
....
var
TimerID: uint;
....
///Процедура обрабатывающая тик таймера:
procedure TimerProc(uTimerId, uMessage, UNIT; dwUser,dw1,dw2, : DWORD); stdcall;
begin
//Здесь ты осуществляешь все что тебе нужно...
end;
//Запуск таймера:
TimerID := timeSetEvent(2,0,@TimeProc,0,TIME_PERIODIC);
// здесь перввый параметр не стоит приравнивать к 0...
//Завенршение таймера...
timeKillEvent(TimerID);
...
а вообще, я посоветовал бы тебе использовать Application.Idle или он же цикл обработки сообщений:
вот пример цикла обработки:
...
var
TEst_RUN : Boolean = false;
....
While TEst_RUN do
begin
if PeekMessage(Mesg,MAinWnd,0,0,PM_REMOVE) then begin
TranslateMessage(Mesg);
DispatchMessage(Mesg);
end;
if FActive then
begin
Render; //В этой процедуре должен находится весь код воспрооизведения и рассчетов...
ReadImmediateData; //Это напрямую относится к DirectInput
{Inc(Frames);
ThisTickCount := GetTickCount;
if ThisTickCount - LastTickCount > 1 then
begin
FPSS := "FPS = " + IntToStr(trunc(Frames*1000/(ThisTickCount - LastTickCount)));
SetWindowText(MainWnd,PChar(FPSS));
Frames := 0;
LastTickCount := GetTickCount;
End;}
end;
end;
Код конечно не "оптимайз", но хоть что - то, если используешь VCL, то вышеприведенный цикл тебе, как я уже сказал, заменит Application.Idleprocedure TfrmD3D.ApplicationEvents1Idle(Sender: TObject;
var Done: Boolean);
var
hRet : HRESULT;
begin
if FActive then begin
hRet := Render;
if FAILED(hRet) then begin
FActive := False;
ErrorOut ("Render", hRet);
Exit;
end;
ThisTickCount := GetTickCount;
if ThisTickCount - LastTickCount > 30 then begin
Angle := Angle + 0.05;
if Angle > 2 * Pi then Angle := Angle - 2 * Pi;
LastTickCount := GetTickCount;
end;
end;
Done := False;
end;
Рекомендуемая литература:
1. М. Краснов. OpenGL графика в проектах Delphi, BHV, 2004
2. М. Краснов. DirectX графика в проектах Delphi, BHV, 2004 - оптимайзена под игры...
Если еще чего - спрашивай..
← →
VMcL © (2004-10-10 20:14) [5]>>Sphinx (10.10.04 17:08) [3]
Еще не прочитав [3], я уже догадался. В играх такие вещи делаются не через таймер.
В, так сказать, "нормальных" играх кадры отрисовываются постоянно (например, в Application.OnIdle), а позиции объектов рассчитываются исходя из промежутка времени, который прошел с момента начала игры или отрисовки предыдущего кадра. Этот промежуток определяется посредством функции QueryPerformanceCounter() или какой-либо другой, дающей достаточно точное значение.
← →
Sphinx (2004-10-10 20:50) [6]> ДЛя начала, насколько я понял, надо использовать DirectInput,
Он и используется.
> Далее, самый быстрый таймер в виндовс - мультимедийный татаймер...
прочтите внимательно первый пост, там я про него писал.
> 2. М. Краснов. DirectX графика в проектах Delphi, BHV, 2004
с ней и разбираюсь :) только издание 2002 года
> если используешь VCL
используется WinAPI.
> В, так сказать, "нормальных" играх кадры отрисовываются
> постоянно
читайте внимательно! цитирую самого себя:
> нужно пересчитывать движение юнитов через равные промежутки
> времени
перерисовка экрана и так происходит в реальном времени.
← →
Nick Denry © (2004-10-10 20:50) [7]2>VMcL © (10.10.04 20:14) [5]
В, так сказать, "нормальных" играх
ИМХО, точнее сказать: в сложных и больших играх. Опять таки ИМХО, для мальнькой игры без разницы.
Для большой еще можно сказать, должно быть исключено использование VCL, применяться асеемблерные вставки оптимизация и др. А новичкам, для начала надо следить за использованием того же DirectInput"a....
← →
Nick Denry © (2004-10-10 21:06) [8]2Sphinx (10.10.04 20:50) [6]
Сорри, что читал невнимательно, но тогда тебе дествительно надо делать как говорит VMcL © (10.10.04 20:14) [5]. Т.е. код воспроизведения должен быть размещен в цикле обработки сообщений, как показанно у меня, и рассчет и отрисовка должны проводиться в функции render (предлагается не конкретное решение, а сам принцип).
нужно пересчитывать движение юнитов через равные промежутки времени
Что ты под э
тим подразумеваешь? Если смену кадров спрайтов, то тебе на стр. 173, там все подробно написанно, если движение кадров, то см. дальше...
← →
Sphinx (2004-10-10 21:08) [9]Видно без кода не обойтись.
за коментарии не пинать, писал для себя :)
основной цыкл:
while appRun do
begin
GetMessage(aMSG, 0, 0, 0);
TranslateMessage(aMsg);
DispatchMessage(aMsg);
DrawScreen;
end;
процедура перерисовки:
procedure DrawScreen;
begin
case ScreenCount of
1: begin
if appActive then
begin
DrawTitleSelect;
if ReDrawTitle=DD_OK then
FlipSurf;
end;
end;
2: begin
if appActive then
begin
(* скроллировани экрана *)
if mouseY<5 then
begin
if GroundY>0 then
GroundY:=GroundY-2;
end;
if mouseY>(SCREEN_HEIGHT-40) then
begin
if GroundY<(GameFlur.Height-screen_height) then
GroundY:=GroundY+2;
end;
if mouseX<5 then
begin
if GroundX>0 then
GroundX:=GroundX-2;
end;
if mouseX>(SCREEN_WIDTH-40) then
begin
if GroundX<(GameFlur.Width-screen_width) then
GroundX:=GroundX+2;
end;
if ReDrawGameScreen=DD_OK then
FlipSurf;
end;
end;
3: begin
if appActive then
begin
if ReDrawInfoScreen=DD_OK then
FlipSurf;
end;
end;
end;
KeyboardReadData; // DInput
MouseReadData; // DInput
Inc(FPSCount);
ThisTickCount:=GetTickCount;
if ThisTickCount-LastTickCount > 600 then
begin
FPS:=FPSCount;
FPSCount:=0;
LastTickCount:=GetTickCount;
end;
end;
инициализация таймера:uTimerID:=SetTimer(mainHandle, uEventID+1, AniSpeed, @ProcTime);
(* Процедура обратного вызова таймера.
*** Параметры ***
*)
procedure ProcTime(WHandle: HWND; wMesg, TimerID: UINT; dwTime: DWORD); stdcall;
begin
if appActive then
if TimerID=uTimerID then
if ScreenCount=2 then
Hero.DoMove;
end;(* Обработка сообщений, поступивших приложению
так как используется WinAPI то обработка выполняется
самостоятельно.
******** Входные величины ********
iHandle: указатель на окно приложения
aMSG: собственно сообщение
wParam: первый параметр сообщения
lParam: второй параметр сообщения *)
function WindowProc(iHandle: THandle; aMSG: Cardinal;
wParam: Cardinal; lParam: Integer): Integer; stdcall;
begin
(* определение посленного сообщения *)
case aMSG of
// активация\деактивация окна приложения
WM_ACTIVATE: begin
// приложение не активно
if LOWORD(wParam)=WA_INACTIVE
then
Activate(False)
// иначе приложение активно
else
Activate(True);
end;
// скрыть курсор мыши в полноэкранном режиме
WM_SETCURSOR: begin
SetCursor(0);
Result:=1;
Exit;
end;
// закрываем приложение
WM_QUIT: begin
appRun:=False;
Result:=0;
Exit;
end;
// приложение уничтожается
WM_DESTROY: begin
KillTimer(mainHandle, uEventID);
KillTimer(mainHandle, uEventID+1);
CloseFile(LogFile);
DDFreeObj;
IniFile.Free;
PostQuitMessage(0);
Result:=0;
Exit;
end;
end;
// передаем сообщение следующему приложению
Result := DefWindowProc(iHandle, aMSG, wParam, lParam);
end;
так вот...если таймер не задавать, карта не скроллируется если не двигать мышкой! Если задан таймер, то карта скролируется, когда мышка подведена к краю, но если ей при этом еще и подвигать - герой замирает.
← →
DiamondShark © (2004-10-10 21:15) [10]
> Sphinx (10.10.04 20:50) [6]
> > Далее, самый быстрый таймер в виндовс - мультимедийный
> татаймер...
> прочтите внимательно первый пост, там я про него писал.
Значит у тебя что-то нехорошее с виндой. Эта библиотека должна быть. Что-то её некорректно снесло.
> читайте внимательно! цитирую самого себя:
> > нужно пересчитывать движение юнитов через равные промежутки
> > времени
Ну так это не правильная постановка. В такой постановке задача в рамках не реал-тайм ОС нерешаема. Расчитывать надо не через равные промежутки времени, а как функцию от прошедшего времени.
Другое дело, что брать в качестве источника времени: мультимедиа таймер, пефоманс каунтер или системный таймер.
В твоём случае подойдёт даже GetTickCount, вполне достаточная для игрушки точность.
← →
Sphinx (2004-10-10 21:21) [11]> DiamondShark © (10.10.04 21:15) [10]
А я и не требую абсолютной точности мне бы от "замораживания" избавиться.
> Значит у тебя что-то нехорошее с виндой. Эта библиотека
> должна быть. Что-то её некорректно снесло.
На компьютере много народу бывает, он не мой лично, поэтому может быть, не спорю :)
← →
Erazzer (2004-10-10 21:44) [12]Забудь про сообщения.
В новом потоке сделай следующее
//в методе execute
while no terminated do
begin
sleep(10);
synchronize(твоя процедура);
end;
← →
DiamondShark © (2004-10-10 22:14) [13]
> так вот...если таймер не задавать, карта не скроллируется
> если не двигать мышкой!
Потому что у тебя GetMessage, которая не возвращается, пока нет сообщений в очереди.
← →
Nick Denry © (2004-10-10 22:54) [14]2DiamondShark © (10.10.04 22:14) [13]
Таким образом код возвращается к PeekMessage, о котором я говорил ранее..
Кстати, в нем же о GetTickCount, здесь рассчитывается колчество FPS за 1 секунду. Аналогично можно рассчитывать что угодно, за любой промежуток времени.
Я прав?...
var
TEst_RUN : Boolean = false;
....
While TEst_RUN do
begin
if PeekMessage(Mesg,MAinWnd,0,0,PM_REMOVE) then begin
TranslateMessage(Mesg);
DispatchMessage(Mesg);
end;
if FActive then
begin
Render; //В этой процедуре должен находится весь код воспрооизведения и рассчетов...
ReadImmediateData; //Это напрямую относится к DirectInput
{Inc(Frames);
ThisTickCount := GetTickCount;
if ThisTickCount - LastTickCount > 1 then
begin
FPSS := "FPS = " + IntToStr(trunc(Frames*1000/(ThisTickCount - LastTickCount)));
SetWindowText(MainWnd,PChar(FPSS));
Frames := 0;
LastTickCount := GetTickCount;
End;}
end;
end;
Далее по коду появилось несколько вопросов.
1. Зачем нужен exit в каждом сообщении WM_ ???? Сколько писал, писал только там, где явно необходимо было возвращать значения.. Это не правильно?
2. В "процедуре перерисвоки", если не ошибаюсь можно сначала проверить условие if appActive, а затем весь остальной код, тем самым уменьшить время, отводимое на эти проерки...
3. Объясните мне плиз, что хотел сказать [12]...
← →
Sphinx (2004-10-11 00:14) [15]> Я прав?
Да :) Просто я хотел сделать изменяющуюся скорость игры, ну и
еще пару вещей, а у того же Краснова, к примеру, говориться что это лучше делать это через таймер.
> Зачем нужен exit в каждом сообщении WM_ ????
Не в каждом, может я не прав, но в WM_EXIT и WM_DESTROY должно вернуться "0". Из хелпа: If an application processes this message, it should return zero.
> В "процедуре перерисвоки", если не ошибаюсь можно сначала
> проверить условие if appActive,
Будет сделано :) просто оптимизацией еще не занимался, дописывая отдельные ветви, по мере из появления.
> 3. Объясните мне плиз, что хотел сказать [12]...
Не знаю...все равно результат один будет...
← →
Nick Denry © (2004-10-11 00:46) [16]Да :) Просто я хотел сделать изменяющуюся скорость игры
ИМХО, лучше это сделать используя счетчик шагов/движений персоонажа или спрайтов. Т.е. воспроизводится все непрерывно, а меняется значение переменной, отвечающей за сдвиг объектов. Ты наверное Direct3D не смотрел еще, краснов там так поступает в большинстве случаев...
Не в каждом, может я не прав, но в WM_EXIT и WM_DESTROY должно вернуться "0". Из хелпа: If an application processes this message, it should return zero.
Про справку -то оно верно, но в по опыту погу сказать дельфи сама подставляет в откомпилированный код необходимые значения. Использовать result:=Что-то и exit; мне приходилось только в сообщениях WM_CTLCOLORSTATIC и им подобных, т.е. там где
Return Values
(для WM_CTLCOLORSTATIC: If an application processes this message, the return value is the handle of a brush that Windows uses to paint the background of the static control. ) как раз отличны от нуля. Т.е. 0 можно не подставлять, по крайней мере проблем с этим не было...
Еще про подстановку кода дельфей в исполняемый код: поверить в это трудно :), но это факт. Если помнишь Краснов пишет о "неизвестной ошибке", появляющейся при попытке вызова метода _Release любого интерфейса. объяснить это он отказывается, ссылаясь на неоотлаженность заголовков DirectX для Delphi. Так вот, дело в том, что при инициализации каждого конкретного интерфейса Дельфи сама добавляет код его освобождения при завершении программы.
> В "процедуре перерисвоки", если не ошибаюсь можно сначала
> проверить условие if appActive,
Будет сделано :) просто оптимизацией еще не занимался, дописывая отдельные ветви, по мере из появления.
Ты знаешь, одним из главных советов по разработке больших программ на любом языке является рекомендация оптимизировать все после написания основных функции программы и ее отладки. Так что советую тебе просто пока запомнить/записать это, потом доработаешь. Иначе можно очень крепко запутаться....
Вот, советую почитать, http://www.wasm.ru/article.php?article=biprjasm , мне в свое время очень помогло...
← →
XProger © (2004-10-11 01:48) [17]
function GetTimer: integer;
var
T, F : LARGE_INTEGER;
begin
QueryPerformanceFrequency(int64(F));
QueryPerformanceCounter(int64(T));
Result := trunc(1000 * T.QuadPart/F.QuadPart);
end;
...
Time_Old := GetTimer;
while not Engine_isQuit do
begin
while PeekMessage(msg, 0, 0, 0, PM_REMOVE) do
begin
TranslateMessage(msg);
DispatchMessage(msg);
end;
Time := GetTimer;
Time_Delta := Time - Time_Old;
flag := false;
// Engine_GetUPS - кол-во обновлений игры в секунду
for i := 1 to Time_Delta div (1000 div Engine_GetUPS) do
begin
Engine_Update; //Обновление
flag := true;
end;
if flag = true then
Time_Old := Time - (Time_Delta mod (1000 div Engine_GetUPS));
Engine_Draw;
end;
← →
SammIk © (2004-10-11 04:48) [18]Я тут где-то писал уже, про получение ко-ва тиков процессора
со времени включения компа.
Точность опупительная, и зависит лишь от частоты процессора.
Чем выше, тем более короткии промежуток можно отследить.
Работать будет всегда, но реализовывать придется самому.
Я лишь написал как можно реализовать его, с кодом на MASM.
Там реализован только счетчик тиков, а таимер сам делаи и в секунды сам переводи.
← →
Nick Denry © (2004-10-11 10:47) [19]2XProger © (11.10.04 01:48) [17]
Отлично, надо взять на заметку...
Страницы: 1 вся ветка
Форум: "WinAPI";
Текущий архив: 2004.11.21;
Скачать: [xml.tar.bz2];
Память: 0.55 MB
Время: 0.041 c