До сегодняшнего дня мы писали DirectX приложения на основе созданной Delphi формы. Мы привязывали к ней DirectDraw и рисовали на ней. Сегодня мы откажемся от этого и напишем приложение на основе своего окна, без использования VCL форм.
Рис 1. Пример формы
Когда ты создаёшь новый проект, Delphi создаёт главную форму. Она нам не нужны, поэтому её надо удалить. Для этого выбери View -> Project Manager. Перед тобой откроется окно, как на рисунке 1. Щёлкни правой кнопкой крысы по Unit1 . В появившемся меню выбери Remove from project .
Теперь у нас есть пустой проект. Щёлкни правой кнопкой крысы по Project1 . В появившемся меню выбери View Source . Перед тобой откроется вот такой текст:
program Project1;
uses
Forms;
{$R *.RES}
begin
Application.Initialize;
Application.Run;
end.
Лёгким стуком по клавиатуре, преврати этот текст в такой:
program Project1;
uses
windows,
messages,
graphics,
sysutils,
forms,
directx;
{$R *.RES}
var
Instance: HWnd;//Поток
WindowClass: TWndClass;//Класс окна
Handle: HWnd;//Указатель на окно
msg: TMsg;//Переменная для системных сообщений
//Со следующими переменными мы уже работали
//они нам понадобятся для работы с DirectX
FDrawObject : IDirectDraw;
FBackCaps: TDDSCaps;
FPrimarySurface : IDirectDrawSurface;
FSecondarySurface : IDirectDrawSurface;
FSurfaceDescription: TDDSurfaceDesc;
begin
//Получаем новый поток
instance :=GetModuleHandle(nil);
//Создаём класс окна
with WindowClass do
begin
style:=CS_HRedraw or CS_VRedraw;
//В Lpfnwndproc присваиваем указатель на процедуру
//которая будет откликаться на системные сообщения.
//Эту процедуру я напишу чуть позже.
Lpfnwndproc:=@windowproc;
Hinstance:=Instance;
HbrBackground:= color_btnface;
LpszClassName:='VR_DX';
Hcursor:=LoadCursor(0,IDC_ARROW);
end;
//Регистрируем новый класс
RegisterClass (WindowClass);
//Создаём окно
Handle:=CreateWindowEx (0,'VR_DX','',WS_POPUP, 5,5, GetSystemMetrics
(SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN),0,0,instance, nil);
//Обновляем окно
UpdateWindow(Handle);
//Здесь мы вставим инициализацию DirectX
// Запускаем цикл, в котором будет проходить проверка
// системных сообщений.
while true do
begin
//Получить системное сообщение.
translatemessage(msg);
//Отправляем его окну
dispatchmessage (msg);
//Даём поработать другим
application.processmessages;
end;
end.
С большинством кода можно разобраться по комментариям. Я остановлюсь только на одной функции:
Функиция CreateWindowEx создаёт новое окно. Первый параметр - стиль окна, который нам не нужен, поэтому 0. Второй - класс окна. Он должен быть таким же, как и у WindowClass.LpszClassName. Третий - заголовок, который нам тоже не нужен, всё равно его не будет видно. Четвёртый - снова стиль, но немного другой. Здесь нам достаточно WS_POPUP. Следующие два параметра - левая и правая позиции окна. Потом идут ширина и высота. Девятый параметр - указатель на владельца окна. Наше окно главное, поэтому 0. Десятый - указатель на меню. Следующий - указатель на поток. Последний параметр нас тоже не очень интересует.
Теперь напишем процедуру, которая будет откликаться на системные сообщения:
function windowproc (Hwn,msg,wpr,lpr: longint): longint; stdcall;
begin
//Вызываем обработчик события по умолчанию
result:=defwindowproc(hwn,msg,wpr,lpr);
//Если окно уничтожается, то вызвать процедуру DoExit;
//эту процедуру мы напишем чуть позже
if msg=wm_destroy then
DoExit;
//Если нажата клавиша то ...
if msg=wm_KeyDown then
// Если это клавиша ESC то выход
if wpr=VK_ESCAPE then
DoExit;
end;
Её нужно вписывать после объявлений var и перед первым begin, иначе Delphi её не найдёт.
Теперь напишем функцию DoExit, которая будет уничтожать окно. Её нужно писать между объявлениями var и процедурой windowproc.
procedure DoExit;
begin
FSecondarySurface:=nil;
FPrimarySurface:=nil;
FDrawObject:=nil;
Halt;
end;
Здесь всё ясно. Она уничтожает объекты DirectDraw, которые мы ещё не создали :). Давай же создадим их. Напиши следующий текст после токо как мы вызывали UpdateWindow, и перед циклом проверки системных сообщений. Это там, где мы писали смый первый код, я там ещё поставил комментарий, где нужно написать следующее:
DirectDrawCreate (nil,FDrawObject,nil);
FDrawObject.SetCooperativeLevel (handle, DDSCL_EXCLUSIVE or
DDSCL_FULLSCREEN);
FDrawObject.SetDisplayMode (800,600,16);
with FSurfaceDescription do
begin
dwSize := sizeof(FSurfaceDescription);
dwFlags := DDSD_CAPS or DDSD_BACKBUFFERCOUNT;
ddsCaps.dwCaps :=DDSCAPS_PRIMARYSURFACE or DDSCAPS_FLIP or
DDSCAPS_COMPLEX;
DwBackBufferCount := 1;
end;
FDrawObject.CreateSurface (FSurfaceDescription,FPrimarySurface,nil);
FBackCaps.dwCaps := DDSCAPS_BACKBUFFER;
FPrimarySurface.GetAttachedSurface (FBackCaps,FSecondarySurface);
FPrimarySurface.Flip(nil, DDFLIP_WAIT);
Здесь тебе должно быть уже всё ясно. Со всеми процедурами мы уже знакомы.
Программа готова!!! Она ничего не делает, кроме инициализации DirectDraw и создания поверхностей. Вроде бы ничего особенного, но всё это происходит без использования VCL. Если ты захочишь написать быстродействующее прилоение DirectDraw, то тебе придёться писать его именно так.
Если ты захочешь добавить в программу рисование, то обработай сообщение WM_PAINT и рисуй сколько угодно. Для этого нужно подправть процедуру windowproc вот так:
function windowproc (Hwn,msg,wpr,lpr: longint): longint; stdcall;
begin
result:=defwindowproc(hwn,msg,wpr,lpr);
if msg=wm_destroy then
DoExit;
if msg=wm_paint then
begin
//Здесь ты можешь рисовать, сколько тебе влезет.
end;
if msg=wm_KeyDown then
begin
if wpr=VK_ESCAPE then
DoExit;
end;
end;