{{notification.text}}

MirGames

adskiy_sotona
23.11.07 21:30
0
Разбираюсь в директх через дельфи по соответствующей книге Фленова, и никак не могу вкурить, как правильно организовать обработку окна, чтобы оно обновлялось и перерисовывалось, как надо...
Использую приложенные к книге шаблоны пока, там идёт, ну, стандартная, насколько я понимаю (я просто винапи не знаю нифига, щас вот как зароюсь...), "подмена" класса окна своим классом с нужными обработчиками сообщений:
Код

function WindowProc(wnd: HWND; Msg: Integer; wParam: wParam; lParam: lParam): Lresult; stdcall;
begin
Result:=0;
case msg of
  WM_CREATE:
   begin
   end;
  WM_DESTROY:
   begin
    backsurf :=nil;
    primsurf :=nil;
    ppiDD :=nil;
    PostQuitMessage(0);
    exit;
   end;
  WM_PAINT:
   begin
     processing;
     drawscene;
   end;
else
  Result:=DefWindowProc(wnd,msg,wparam,lparam);
end;
end;
....
begin
wc.cbSize       := sizeof(wc);
wc.lpfnWndProc  := @WindowProc;
wc.cbClsExtra   := 0;
wc.cbWndExtra   := 0;
wc.hInstance    := HInstance;
wc.hCursor      := LoadCursor(0, IDC_ARROW);
wc.hbrBackground:= COLOR_BTNFACE+1;
wc.lpszMenuName := nil;
wc.lpszClassName:= 'delphi_directdraw';
RegisterClassEx(wc);

pWnd := CreateWindowEx(WS_EX_APPWINDOW, 'delphi_directdraw', 'Delphi DirectX Demo',
    WS_OVERLAPPED or WS_SYSMENU, 0, 0, 250, 130, 0, 0, Hinstance, nil);

ShowWindow(pWnd, SW_SHOW);
while GetMessage(pMsg, 0, 0, 0) do
  begin
    TranslateMessage(pMsg);
    DispatchMessage(pMsg);
  end;
end.


Суть конкретной проблемы в том, что программа (простое приложение использующее директдрав и выводящее на экран спрайтики) прорисовывается один раз (причём видно, что рисует всё правильно, корректно выполняя обе прописанные функции processing и drawscene), и далее наотрез отказывается обновляться сама. Подозреваю, что нужно как-то простимулировать появление события WM_PAINT. Но как?

В винапи ничего не понимаю, сплагиатить тоже не получилось (смотрел, например, исходники DGLEngine, нашёл подобную же структуру, но пока не понял, как она так ловко крутится, предоставляя программисту уже готовые к употреблению места для процессинга и рисования, и избавляя от рутинной организации всякого основного...).

В общем, если не трудно, подскажите, как без затей программе можно говорить, что WM_PAINT надо обрабатывать раз, да ещё раз, да ещё много-много раз.

з.ы. поиском не воспользовался, потому что не понимаю толком, что искать .
з.ы.ы. побёг русскую справку по винапи читать.
Отредактировано: 23.11.07 21:32
#1
аксакал
23.11.07 23:10
0
После DispatchMessage(pMsg) попробуй вызывать invalidaterect(pWnd, nil, False)
#2
24.11.07 04:22
0
adskiy_sotona
В конце WM_PAINT после drawscene вызови invalidaterect(pWnd, nil, False)
А вообще, не помешало бы винапи подучить. Я учил по книжке П.В. Румянцева "Азбука программирования в Win32 API".
Отредактировано: 24.11.07 04:23
#3
adskiy_sotona
24.11.07 17:37
0
К сожалению, способ не помог, но чуток потупив, поплевав в потолок, посмотрев чужие коды и почитав книжку я вдруг понял, что это вообще неправильный путь и всё надо делать совсем по другому. Чем сейчас и занимаюсь.

Эх, понапридумывали блин функций. <_<
#4
mov, просто mov
24.11.07 19:07
0
я бы сделал вот так, шустрее чем WM_PAINT
Код

    if PeekMessage(msg, 0, 0, 0, PM_REMOVE) then
    begin
      TranslateMessage(msg);
      DispatchMessage(msg);
    end else if Active then
    begin
       Render;
    end;

#5
adskiy_sotona
24.11.07 19:13
0
во-во, я примерно это и разглядел.

теперь блин парюсь, как мне проверять и возвращать всякие нажатия кнопок. о_О
вроде элементарная вещь, а блин чорти-чо...
#6
adskiy_sotona
24.11.07 19:30
0
Блин, чё-то я никак...
В результате творческого плагиата родил такую петлю:

Код
while not ExitProgram do
begin
  if PeekMessage(pMsg, 0, 0, 0, PM_REMOVE)
  then
    begin
      if (pMsg.message=WM_QUIT)
        then
          ExitProgram:=True
        else
          begin
            TranslateMessage(pMsg);
            DispatchMessage(pMsg);
          end;
    end
  else
    begin
      CurrentTime:=GetTimer;
      TimeDelta:=CurrentTime-OldTime;

      flag:=false;

      for i:=1 to TimeDelta div PROCESS_INTERVAL do
        begin
          Processing;
          flag:=true;
        end;

      if flag then
        OldTime:=CurrentTime-(TimeDelta mod PROCESS_INTERVAL);

      ReDraw;
    end;
end;


Так петля ходит по трансляйт-месседж/диспатч-месседж, а в ту часть, где что-то должно рисоваться и просчитываться, ни ногой. о_О Это теперь типа проблема наоборот - то сообщения не проходили, а теперь прут, что не остановишь. :D Чего я опять забыл проге сказать, чтобы все наконец заработало?
#7
adskiy_sotona
24.11.07 19:35
0
.....о! :blink:
я сделал в петле безусловное выполнение процессинга и отрисовки, и всё заработало. Ну слава тебе хоспади.
Но ведь так, поди, неправильно делать? Не зря же люди процессинг и отрисовку делают только если else...
Отредактировано: 24.11.07 19:39
#8
adskiy_sotona
24.11.07 20:14
0
блин, достало!
чё-то я уже расфлудился, но вроде как оказывается, что проблема совсем в другом была и осталась.
Сейчас, к новой, упомянутой выше, петле, где безусловная отрисовка и обсчёт, я прикрутил возможность работать в двух режимах - фуллскрин и оконный. В оконном режиме всё работает как надо - экран очищается, прорисовывается спрайт, бегает по окошку вслед за нажатиями клавиш, и программа нормально выходит по нажатию Esc и ненормально - по Alt+F4 ("программа выполнила недопустимую операцию и будет тово...").
Если же запускать в полноэкранном режиме, то
1) нажатия прописаных клавиш (т.е. стрелки и Esc) почти игнорируются. Почти, потому что вроде как что-то проходит и потом надолго залипает, выйти из проги Escом то ли нельзя, то ли я слишком мало ждал.
2) альт-ф4 не работает нафиг, поэтому программу можно вырубить только аварийно.

Я ничё не пойму, почему моя программа себя вот так ведёт, а пример из книжки, где код принципиально такой же (если сравнивать с моим вариантом, где вызов обработки и отрисовки происходит в WM_PAINT) и работает на основе точно таких же функций инициализации директдрава - работает нормально. :blink:

Есть у кого-нибудь какие мысли? :(
Отредактировано: 24.11.07 20:21
#9
24.11.07 23:50
0
Первая мысль - полный код в студию
#10
adskiy_sotona
25.11.07 10:56
0
Значится, вот:
Код
program volki;

uses
  windows,
  messages,
  sysutils,
  Classes,
  Controls,
  DirectDraw,
  ddfunc in 'ddfunc.pas';

var
wc   : TWndClassEx;
pWnd : HWND;
pMsg : TMsg;
ppiDD : IDirectDraw7=nil;
primsurf : IDirectDrawSurface7=nil;
backsurf : IDirectDrawSurface7=nil;
sp_zayac : IDirectDrawSurface7=nil;

bWindowed :  boolean;
ExitProgram: boolean;
flag:        boolean;

cData:TDDColorKey;
CurrentTime:integer;
OldTime:integer;
TimeDelta:integer;
X:cardinal=0;
Y:cardinal=0;
PressedKey:integer;
i:integer;

const
  PROCESS_INTERVAL=10;

function GetTimer:integer;
  var
    T,F:LARGE_INTEGER;
  begin
    QueryPerformanceFrequency(int64(F));
    QueryPerformanceCounter(int64(T));
    Result:=trunc(1000*T.QuadPart/F.QuadPart);
  end;

procedure DrawScene;
  begin
    backsurf.BltFast(X,Y,sp_zayac,nil,DDBLTFAST_SRCCOLORKEY or DDBLTFAST_WAIT);
  end;

procedure Processing;
  begin
    Case PressedKey of
      38:y:=y-1;
      40:y:=y+1;
      37:x:=x-1;
      39:x:=x+1;
      27:ExitProgram:=true;
    end;
  end;

procedure ReDraw;
  var
    p: TPoint;
    ClientRect: TRect;
  begin
    ClearSurface(BackSurf,$000000);

    DrawScene;

    if bWindowed
     then
      begin
        p.X:=0;
        p.Y:=0;
        ClientToScreen(pWnd,p);
        GetClientRect(pWnd,clientRect);
        OffsetRect(ClientRect, p.X, p.Y);
        primsurf.Blt(@ClientRect,backsurf,nil,DDBLT_WAIT, 0);
      end
     else
      primsurf.Flip(nil,DDFLIP_WAIT);
  end;

function WindowProc(wnd: HWND; Msg: Integer; wParam: wParam; lParam: lParam): Lresult; stdcall;
begin
Result:=0;
case msg of
  WM_KEYDOWN:
   begin
     PressedKey:=wParam;
   end;
  WM_KEYUP:
   begin
     PressedKey:=0;
   end;
  WM_CREATE:
   begin
   end;
  WM_DESTROY:
   begin
    backsurf :=nil;
    primsurf :=nil;
    ppiDD :=nil;
    PostQuitMessage(0);
    exit;
   end;
else
  Result:=DefWindowProc(wnd,msg,wparam,lparam);
end;
end;

begin
wc.cbSize       := sizeof(wc);
wc.lpfnWndProc  := @WindowProc;
wc.cbClsExtra   := 0;
wc.cbWndExtra   := 0;
wc.hInstance    := HInstance;
wc.hCursor      := LoadCursor(0, IDC_ARROW);
wc.hbrBackground:= COLOR_BTNFACE+1;
wc.lpszMenuName := nil;
wc.lpszClassName:= 'delphi_directdraw';
RegisterClassEx(wc);
pWnd := CreateWindowEx(WS_EX_APPWINDOW, 'delphi_directdraw', 'Delphi DirectX Demo',
    WS_OVERLAPPED or WS_SYSMENU, 0, 0, 250, 130, 0, 0, Hinstance, nil);

DXDDInit(ppiDD, primsurf, backsurf, pWnd, 1024, 768, 16777216, bWindowed);
ShowWindow(pWnd, SW_SHOW);

sp_zayac:=LoadBMPToSurface('res\zayac.bmp',ppiDD);
cData.dwColorSpaceLowValue:=0;
cData.dwColorSpaceHighValue:=0;
sp_zayac.SetColorKey(DDCKEY_SRCBLT, @cData);

ExitProgram:=false;
OldTime:=GetTimer-PROCESS_INTERVAL;

while not ExitProgram do
begin
  if PeekMessage(pMsg, 0, 0, 0, PM_REMOVE)
  then
    begin
      if (pMsg.message=WM_QUIT)
        then
          ExitProgram:=True
        else
          begin
            TranslateMessage(pMsg);
            DispatchMessage(pMsg);
          end;
    end;
  {else}
  { begin}
      CurrentTime:=GetTimer;
      TimeDelta:=CurrentTime-OldTime;

      flag:=false;

      for i:=1 to TimeDelta div PROCESS_INTERVAL do
        begin
          Processing;
          ReDraw;
          flag:=true;
        end;

      if flag then
        OldTime:=CurrentTime-(TimeDelta mod PROCESS_INTERVAL);
    {end;}
end;
end.
#11
аксакал
25.11.07 11:37
0
Код основного цикла из eXgine
Код
  while not eng_isquit do
  begin
  // обработка Windows сообщений
    while PeekMessage(msg, ownd.Handle, 0, 0, PM_REMOVE) do
    begin
      TranslateMessage(msg);
      DispatchMessage(msg);
    end;
  // Тайминг
    while GetTime - ups_time_old >= (1000 div UPS) do
    begin
      Update;
      inc(ups_time_old, 1000 div UPS);
    end;
    Render;
  end;

UPS - желаемое кол-во вызовов Update в секунду.
#12
adskiy_sotona
25.11.07 13:55
0
Ну этот цикл ничего принципиально другого не вносит, работает так же.
Проблема снимается, если я убираю вызов процедуры отрисовки. То есть, программа нормально реагирут на закрытие по алт-ф4, не завешивает комп в полноэкранном режиме и вообще. Непонятно, нафиг мне программа, которая ничего не рисует, но всё же. Но проблема НЕ исчезает, если я в уже вызванной процедуре отрисовки сразу же прописываю exit;, ещё НИЧЕГО не успев сделать.

чзх? :(
#13
аксакал
25.11.07 23:30
0
adskiy_sotona
В приведённом мною коде ты должен забыть о существовании WM_PAINT. Он тут принципиально не нужен. (Это к вопросу, как правильно делать)
#14
adskiy_sotona
25.11.07 23:54
0
XProger
Да про вм-пэинт я забыл, угу - коли всё мимо него рисуется, то чому бы ни...
Там просто, как выяснилось, проблема была совсем другом - в непонятно чём. :blink:

Как-то само собой (я так и не понял, из-за чего именно) трабл разрешился и всё заработало во всех режимах.
Сейчас основная проблема с точки зрения работы программы - что она не хочет по стандартному алт-ф4 нормально закрываться - либо "приложение выполнило недопустимую...", либо окно вроде как закрывается, но сам процесс продолжает сидеть в памяти и жрать процессор. Но тут, я так понимаю, я просто какие-то неправильные действия прописываю (или наоборот недопрописываю) на завершение проги. Ну, вроде ж алт-ф4 - стандартная виндовая команда... По кнопке Esc , которая тупо запрещает выполнение главного цикла, выход вроде корректный.
#15
mov, просто mov
28.11.07 00:10
0
adskiy_sotona
1.
Код

   begin
      if (pMsg.message=WM_QUIT)
        then
          ExitProgram:=True
        else
          begin
            TranslateMessage(pMsg);
            DispatchMessage(pMsg);
          end;

теперь подумай этом )
сообщение о выходе обрабатываеться после того как окну послано WM_DESTROY
вывод - обрабатывать WM_QUIT и WM_DESTROY в WindowProc вместе :)
2. уничтожай класс окна)
Отредактировано: 28.11.07 00:15
#16
adskiy_sotona
28.11.07 17:24
0
класс уничтожать - это UnregisterClass?
Если да, то как им правильно пользоваться, а то что-то он там параметры совсем другого типа требует...
#17
mov, просто mov
28.11.07 18:40
0
adskiy_sotona
всё правильно он требует :)))
Код
UnregisterClass('Имя класса окна', HInstance);
#18
adskiy_sotona
01.12.07 14:26
0
а, в очередной раз всё ясно, у меня там лишнего в uses было прописано - модуль Classes, и он подсовывал свою одноимённую процедуру, требующию параметр типа TPersistentClass.

#19
01.12.07 14:54
0
adskiy_sotona
Явно укажи модуль, в котором нужная тебе функция: Windows.RegisterClass, Windows.UnregisterClass. В Delphi модули одновременно выполняют функцию пространств имен.
#20
07.12.07 17:38
0
Несмотря на то, что проблема с клавиатурой уже решилась, все же внесу свою лепту.
Мoжно обрабатывать нажатия клавиш не по сообщению, а вызовом
Код
if GetAsyncKeyState(X)<0 then
...

Где X - виртуальный код клавиши, определены различные константы вроде VK_ESCAPE, VK_NUMPAD1 и т.п. а для алфавитно-цифровых соответствуют
Код
ord(symbol)
#{{post.Index}}
{{post.Author.Login}}
{{post.CreatedDate | date:'dd.MM.yy HH:mm'}}
{{post.VotesRating}}
Отредактировано: {{post.UpdatedDate | date:'dd.MM.yy HH:mm'}}