Skoro już wiemy, co chcemy możemy poczynić pierwsze kroku ku obranemu przez nas celowi-Stworzeniu profesjonalnej gry pod windowsa z wykorzystaniem Delphi, DirectX no i oczywiście WinApi(hehe:). Aby kontynuować musisz zdobyć przetłumaczone na Delphi nagłówki DirectX.
Możesz je z powodzeniem znaleźć w Internecie. Gdy ów nagłówki zostaną przez ciebie ściągnięte możemy przystąpić do dalszej części kursu. Oki. Teraz tworzysz nowy projekt WinApi. I jak zwykle kasujesz całą zawartość (chamstwo, nie?:). Na początku piszemy coś takiego:
1 |
program ApiWin; |
Tym sposobem informujemy kompilator, że będziemy pisać program(odkrywcze, nie?:), o nazwie wewnętrznej ApiWin. Po tym zabiegu dodajemy plik zasobów do naszej aplikacji. Dzięki temu zasobowi nasz program będzie miał ikonę. Tak tak teraz sami musimy o to zadbać. Najprościej taki plik z zasobami można zrobić w restoratorze, który znajdziesz w Komputer Świat Ekspert nr. 3/2003 (4). Który możesz zamówić archiwalnie pod numerem telefonu (nie będę go podawał, bo jeszcze mnie oskarżą o jakieś sztuczne promowanie tejże gazet, ale wierzcie mi, są tam naprawdę cool rzeczy). Aby wkompilować zasób do pliku *.exe używamy {$R jakiś tam plik zasobów}. Jeśli nie mamy ochoty zdobywać restoratora możemy zrobić to na dwa inne sposoby. Na pierwszy trzeba by poświęcić osobny artykuł a drugi to podstępna sztuczka. Może podebrać plik zasobów z „normalnego” projektu *.dpr(w katalogu ów projektu powinien być plik *.res). Dobra wróćmy do naszego kursu. W naszym przypadku piszemy:
1 |
{$R ikona.res} |
Tym sposobem wkompilowaliśmy zasób. Następnie w sekcji uses używamy Messages(odpowiada za nazwy komunikatów), Windows i DirectDraw (chyba nie muszę tłumaczyć, do czego służą te dwa ostatnie:).
1 2 |
uses Messages, Windows, DirectDraw; |
Deklarujemy stałe i zmienne:
1 2 3 4 |
const AppName = 'Mój pierwszy Dx w WinApi'; var lpDD : IDirectDraw; // Obiekt DirectDraw |
A oto główny punkt programu. Inicjalizacja trybu graficznego (rozdzielczość). W tym arcie tylko tyle, ponieważ postanowiłem skupić się na WinApi. Oto jedyna procedura powiązana, z DirectX w tym arcie(zawsze coś:). Omówimy do końca WinApi a potem będziemy bawili się z narzędziami Bila(co ja pisze… Przecież windows to też jego produkt:).
1 |
procedure InitDirectDraw( h_Wnd : HWND ); |
h_Wnd to uchwyt do naszego okna. teraz kod naszej funkcji tradycyjnie zaczynający się od begin a kończący na end ;). Warto zwrócić uwagę, że w języku c++ mamy NULL a w Pascalu jego odpowiednikiem jest nil. Może to się bardzo przydać, jeśli będziesz coś przenosić z MSSDK microsoftu. Idąc dale dochodzimy do funkcji lpDD.SetDisplayMode, która ustawia rozdzielczość a DirectDrawCreate tworzy obiekt o jakiejś tam nazwie(konkretnie lpDD:). Pierwszy parametr to szerokość, drugi to wysokość a trzeci to ustawienie kolorów. Mamy do wyboru 8, 16, 24, 32 bitowy kolor. Im większy tym oczywiście lepszy:).
1 2 3 4 5 6 |
begin DirectDrawCreate(nil , lpDD, nil); lpDD.SetCooperativeLevel(h_Wnd, DDSCL_EXCLUSIVE or DDSCL_FULLSCREEN); lpDD.SetDisplayMode(320, 200, 32); end; |
Nie jest to takie straszne, nie? Tak wygląda zainicjowanie trybu graficznego z wykorzystaniem DirectDrawa. Jest to równie proste jak w DelphiX czy PowerDrawie. A teraz to co programiści lubią najbardziej czyli WindowProc. Kierujemy tu wszystkimi zdarzeniami tak jak na zakładce Ewents w Vcl. Wystarczy że przeczytasz nazwy zdarzeń i wiesz o co chodzi:).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
function WindowProc(Window: HWnd; AMessage: UINT; WParam : WPARAM; LParam: LPARAM): LRESULT; stdcall; export; var dc : hdc; ps : paintstruct; r : TRect; begin WindowProc := 0; case AMessage of wm_paint: begin dc:=beginPaint(Window,ps); GetClientRect(Window,r); SetTextColor(dc, COLORREF($00FF0000)); //Ustawiamy kolor DrawText(dc,'Zabójcza rozdzielczość 320x200 : )',-1,r, DT_SINGLELINE or DT_CENTER or DT_VCENTER);//Wypisujemy tekst endPaint(Window,ps); Exit; end; wm_Destroy: begin PostQuitMessage(0); Exit; end; wm_create: begin initdirectdraw(window); //INICJUJEMY!!! exit; end; end; WindowProc := DefWindowProc(Window, AMessage, WParam, LParam); end; |
taaa…. Teraz musimy zarejestrować nasze okienko. Nie ma sensu bym to dogłębnie omawiał skoro mamy zamiar programować gry. I tak nie będziemy z tego praktycznie nigdy korzystać. Ważne , że jest i działa :). Tylko jedynym wartym omówienia składnikiem jest WindowClass.hIcon := LoadIcon( hInstance, MakeIntResource( 102 ) ); Nadaje ikonkę z zasobów naszemu programikowi(tą w lewym górnym rogu).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
{ Rejestracja klasy naszego okna } function WinRegister: Boolean; var WindowClass: WndClass; begin WindowClass.Style := cs_hRedraw or cs_vRedraw; WindowClass.lpfnWndProc := @WindowProc; WindowClass.cbClsExtra := 0; WindowClass.cbWndExtra := 0; WindowClass.hInstance := system.MainInstance; WindowClass.hIcon := LoadIcon( hInstance, MakeIntResource( 102 ) ); WindowClass.hCursor := LoadCursor(hInstance, MakeIntResource( 100 )); WindowClass.hbrBackground := GetStockObject(WHITE_BRUSH); WindowClass.lpszMenuName := nil; WindowClass.lpszClassName := AppName; Result := RegisterClass(WindowClass) <> 0; end; |
Teraz tworzymy/kreujemy nasze okno. Parametry dla fullscrena nieco się zmienią, omówię to w następnym artykule(jak dam radę go napisać;).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
{ Robimy nasze okno!!! } function WinCreate: HWnd; var hWindow: HWnd; begin hWindow := CreateWindow(AppName, 'Mój pierwszy Dx w WinApi', ws_OverlappedWindow, cw_UseDefault, cw_UseDefault, cw_UseDefault, cw_UseDefault, 0, 0, system.MainInstance, nil); if hWindow <> 0 then begin ShowWindow(hWindow, CmdShow); ShowWindow(hWindow, SW_SHOW); UpdateWindow(hWindow); end; Result := hWindow; end; |
Uwaga! Uwaga! Coś nowego dla ciebie jeśli ciągle programowałeś w Vcl!!! Akcja naszego programu dzieje się między begin a end a nie w żadnej procedurze/funkcji wywoływanej co jakiś czas. Dzięki głównej pętli programu uzyskujemy maksymalną wydajność.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
var AMessage: Msg; hWindow: HWnd; done : boolean; begin if not WinRegister then begin MessageBox(0, 'Błąd rejestracji', nil, mb_Ok); Exit; end; hWindow := WinCreate; if longint(hWindow) = 0 then begin MessageBox(0, 'Błąd zainicjowania okna', nil, mb_Ok); Exit; end; done := false; |
Doszliśmy do sedna sprawy. PeekMessage pobiera komunikaty wysyłane przez windows, TranslateMessage przetwarza je a DispatchMessage Wysyła. Tak wygląda prawdziwa aplikacja napisana dla windowsa. Nasza pierwsza 32-bitówka(aż łezka kręci się w oku:).
1 2 3 4 5 6 7 |
while (not done) do begin PeekMessage(AMessage, hWindow, 0, 0, PM_REMOVE); TranslateMessage(AMessage); DispatchMessage(AMessage); end; Halt(AMessage.wParam); end. |
Autor: HNB