Категории
Самые читаемые
onlinekniga.com » Компьютеры и Интернет » Программирование » Графика DirectX в Delphi - Михаил Краснов

Графика DirectX в Delphi - Михаил Краснов

Читать онлайн Графика DirectX в Delphi - Михаил Краснов

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 31 32 33 34 35 36 37 38 39 ... 69
Перейти на страницу:

Создание консоли

Консоль вы часто видели и использовали в профессиональных играх и, наверняка, захотите создать и в своей игре. Пример данного раздела - проект каталога Ех09 - поможет вам в этом. Он является развитием нашей пробной игры: теперь по нажатии клавиши <Таb> на экране появляется консоль, предназначенная для ввода команд (рис. 5.9).

Рис. 5.9. Наша игра обзавелась консолью

Я предусмотрел реакцию только на одну команду, после ввода Exit приложение завершает работу, все остальные вводимые строки просто вызывают эхо в консоли.

Моя консоль вмещает три строки, инициализируемые многозначительными фразами:

rcRectConsole : TRECT; // Вспомогательный прямоугольник

ConsoleHeight : Integer =0; // Текущий размер консоли

ConsoleLive : BOOL = False; // Флаг, связанный с присутствием

TextConsolel : String = '> Initialization....OK'; // Строки вывода

TextConsole2 : String = '> Loading .......OK';

TextConsole3 : String = '>_';

Для функционирования консоли я завел отдельную поверхность, закрашиваемую при инициализации белым цветом:

ZeroMemory(@ddsd, SizeOf(ddsd));

with ddsd do begin

dwSize := SizeOf(ddsd);

dwFlags := DDSD__CAPS or DDSD_HEIGHT or DDSD_WIDTH;

ddsCaps.dwCaps := DDSCAPS_OFFSCREENPLAIN;

dwWidth := 640;

dwHeight := 100;

end;

hRet := FDD.CreateSurface(ddsd, FDDSConsole, nil);

if Failed (hRet) then ErrorOut(hRet, 'CreateSurface1);

hRet := FDDSConsole.SetPalette(FDDPal) ;

if Failed (hRet) then ErrorOut(hRet, 'SetPalette');

ZeroMemory(gddbltfx, SizeOf(ddbltfx));

ddbltfx.dwSize := SizeOf(ddbltfx);

ddbltfx.dwFillColor :=RGB (255, 255, 255);

FDDSConsole.Bit(nil, nil, nil, DDBLT COLORFILL or DDBLT WAIT, @ddbltfx);

SetRect (rcRectConsole, 0, 0, 640, 100);

Код воспроизведения монстров и пуль немного подправил, чтобы они не появлялись в области консоли. Сама же консоль воспроизводится в последнюю очередь, непосредственно перед переключением буферов:

if ConsoleLive then begin // Надо ли рисовать консоль

if (GlobalThisTickCount - GlobalLastTickCount > DelayConsole) then

begin // Плавное появление консоли

Inc (ConsoleHeight, 5);

if ConsoleHeight > 100 then ConsoleHeight := 100;

SetRect (rcRectConsole, 0, 0, 640, ConsoleHeight);

end;

// Собственно воспроизведение консоли

FDDSBack.BltFast(0, 0, FDDSConsole, @rcRectConsole, DDBLTFAST__WAIT);

end;

Текст в консоли выводится с помощью функций GDI:

procedure OutText (const X, Y : Integer; const TextCon : String);

var

DC : HOC;

begin

FDDSConsole.GetDC (DC) ;

SetBkColor(DC, RGB (255, 255, 255)); // Цвета фона и букв необходимо

SetTextColor (DC, 0); // задавать обязательно

TextOut (DC, X, Y, PChar(TextCon), length (TextCon));

FDDSConsole.ReleaseDC (DC);

end;

Немало хлопот принесла обработка нажатия клавиши <Backspace>: чтобы стереть старый текст, приходится воспроизводить ряд пробелов:

if diks [DIK_TAB] and $80 <> 0 then begin // Клавиша <Tab>

if not ConsoleLive then begin // Включение консоли

ConsoleHeight := 0; ConsoleLive := True;

end

else ConsoleLive := False; // Выключить консоль

Sleep(lOO); // Небольшая пауза

end;

if ConsoleLive then begin // Обработка клавиш для консоли

OutText (5, 10, TextConsolel); // Вывод трех строк в консоли

OutText (5, 30, TextConsole2); OutText (5, 50, TextConsole3);

if diks [DIK_RETURN] and $80 <> 0 then begin // Ввод команды

// Введена команда "Exit"; выход из программы

if (TextConsole3 = '>EXIT_') or (TextConsole3 = '> EXIT_') then Close;

// Введена другая команда, строки стираются и поднимаются наверх

TextConsolel := ' ';

OutText (5, 10, TextConsolel); // Затираем пробелами

TextConsolel := TextConsole2; // Строка сдвигается вверх

TextConsole2 := ' ' ;

OutText (5, 30, TextConsole2);

TextConsole2 := '> Command : ' + Copy (TextConsole3, 2,

length (TextConsoleS) - 2); // Реакция на все остальные команды -

// вывод эха

TextConsoleS := ' ';

OutText (5, 50, TextConsoleS);

TextConsoleS := '>_'; // Последняя строка превратилась в приглашение

Sleep(100);

end;

if diks [DIK_BACKSPACE] and $80 <> 0 then begin // Нажата клавиша

// <Backspace>

TextConsole3 := ' ';

OutText (5, 50, TextConsoleS); // Стираем последнюю строку

TextConsoleS := '>_';

OutText (5, 50, TextConsoleS);

end;

for i := DIK_Q to DIK_M do // Просматриваем буквенные клавиши

if diks [i] and $80 <> 0 then begin // Нажата какая-то клавиша с буквой

if length (TextConsoleS) < 20 then begin // Ограничение длины строки

// Перед символом подчеркивания вставляем букву нажатой клавиши

TextConsoleS := Copy (TextConsoleS, I, length (TextConsoleS) - 1) +

ScanToChar (i) +'_';

OutText (5, 50, TextConsoleS); // Вывод получившейся строки

Sleep(100);

end;

end;

end;

Поскольку обработка клавиатуры происходит необычайно быстро, с помощью процедуры sleep создаем искусственную паузу, чтобы не получить эффекта залипания клавиши. Эта пауза дает время пользователю отпустить нажатую клавишу.

Диалоговые окна

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

Установка и запрос пароля хранителя экрана являются системными действиями, но вызов их не ограничивается одной строкой. Это означает, что диалоги установки и ввода пароля не должны реализовываться программистом, пароль назначается для всех хранителей экранов. Мы не можем самостоятельно запросить пароль, хранить его в реестре, в определенном разделе, и самостоятельно организовывать ввод пароля при нажатии клавиши или движении курсора мыши.

Следующая процедура предназначена для вызова системного диалога задания нового пароля:

procedure TfrmDD.RunSetPassword;

type // Специальный тип функции, используется только в этой ситуации

TPCPAFunc = function(A : PChar; Parent : hWnd; В, С : Integer) :

Integer; stdcall;

var

Lib : THandle; // Ссылка на DLL

PCPAFunc : TPCPAFunc; // Загружаемая функция

begin

Lib := .LoadLibrary('MPR.DLL1); // Динамическая загрузка DLL

if Lib > 32 then begin // Проверка успешности загрузки

// Получаем адрес точки входа нужной функции

@PCPAFunc := GetProcAddress(Lib, 'PwdChangePasswordA');

// Задаем пароль хранителей экрана

if @PCPAFunc о nil then PCPAFunc('SCRSAVE', StrToInt(ParamStr(2)),

0, 0);

FreeLibrary(Lib); // Выгружаем библиотеку

end;

end;

В нашей программе эта процедура вызывается, если приложение запущено с параметром /а, т. е. в ситуации, когда пользователь нажал кнопку Изменить на вкладке Заставка (см. рис. 4.3).

При нажатии клавиши или движении курсора программа должна сама определить, установлен ли пароль для хранителя экрана, и запустить системный диалог ввода пароля:

function TfrmDD.TestPassword : BOOL;

type

// Специальный тип, тоже используется только в этом, особом случае

TVSSPFunc = function(Parent : hWnd) : BOOL; stdcall;

var

Key : hKey;

D1,D2 : Integer;

Value : Integer;

Lib : THandle;

VSSPFunc : TVSSPFunc;

begin

Result := True;

// Загружаем информацию из реестра, используя функции API

if RegOpenKeyEx(hKey_Current_User, 'Control PaneiDesktop', 0,

Key_Read, Key) = Error_Success then begin

D2 := SizeOf(Value);

// Определяем, установлен ли пароль

if RegQueryValueEx(Key, 'ScreenSaveUsePassword', nil, @D1,

@Value,@D2) = Error_Success then begin if Value <> 0 then begin

// Динамически загружаем библиотеку ввода пароля

Lib := LoadLibraryf'PASSWORD.CPL');

if Lib > 32 then begin

// Получаем адрес точки входа

SVSSPFunc := GetProcAddress(Lib, 'VerifyScreenSavePwd');

// На время работы диалога включаем курсор

ShowCursor (True) ;

// Запускаем системный диалог

if @VSSPFunc <> nil then Result := VSSPFunc(Handle);

ShowCursor(False); // Это можно, в принципе, не делать

FreeLibrary(Lib); // Освобождаем память

end;

end;

end;

RegCloseKey(Key);

end;

end;

И теперь самое главное: диалоговое окно должно работать "поверх" первичной поверхности (рис. 5.10).

Чтобы пользователь увидел его, перед вызовом нашей пользовательской функции TestPassword нужно переключиться на воспроизведение в режиме GDI:

FDD.FlipTcGDISurface;

To есть в такой ситуации обязан вызываться метод главного объекта FlipToGDisurface, а перерисовка экрана не должна осуществляться. К сожалению, мне встречались хранители экрана, написанные профессионалами, авторы которых не позаботились о корректной работе системного диалога: пароль приходится вводить "вслепую", не видя окно ввода пароля, закрытое картинкой первичной поверхности. Памятуя об этом, я многократно проверял работу своего хранителя на самых разных видеокартах, и могу сказать, что не встретил ничего подобного.

Чтобы отключить клавиатуру, точнее, запретить работу комбинаций клавиш <Alt>+<Tab> и <CtrI>+<Alt>+<Del>, на время работы приложения информируем систему о том, что работает хранитель экрана, при запуске приложения выполняется следующая строка кода:

SystemParametersInfo(SPI SCREENSAVERRUNNING, 1, nil, 0);

По окончании работы надо не забыть восстановить нормальную работу комбинаций этих клавиш, для чего вызывается та же команда, но второй аргумент задается нулевым значением.

Переключение на GDI-воспроизведение, надеюсь, сделает видимым диалоговое окно у каждого читателя книги, но полноценным такое решение назвать нельзя. При перемещении окна по экрану оно оставляет следы, расчищая первичную поверхность и обнажая окно приложения (поэтому экран становится серым).

Такой способ общения с пользователем ущербен еще по той причине, что он не работает в палитровом режиме. Из-за этого я не мог рекомендовать его для применения в функции вывода сообщения об ошибке, используемой нами в предыдущих примерах.

1 ... 31 32 33 34 35 36 37 38 39 ... 69
Перейти на страницу:
На этой странице вы можете бесплатно читать книгу Графика DirectX в Delphi - Михаил Краснов.
Комментарии