Технические аспекты сотворения мира. Часть 3
Использование клавиатуры, мыши и джойстика
Рассматривая решения различных проблем, возникающих в процессе разработки компьютерных игр, обязательно следует уделить внимание реализации управления. Не секрет, что неудобное управление способно загубить любую игру, какой бы интересной и красивой она ни была. Стандартные модули Turbo Pascal 7.0 предоставляют очень скудные возможности реализации управления в играх. В этой части статьи будут рассмотрены подпрограммы, позволяющие не только использовать мышь и джойстик, поддержка которых в Turbo Pascal 7.0 отсутствует вообще, но также значительно расширить возможности использования клавиатуры.
Обратимся сперва к клавиатуре. Рассмотрим процедуры, позволяющие определять нажатые клавиши даже в тех случаях, когда одновременно их нажато несколько.
uses Dos; var pInterrupt09hVector: Pointer; bKeys: array[1..127] of Byte; p rocedure KeyboardDriver; interrupt; assembler; asm mov ah,1 in al,60h test al,80h jz @1 xor ax,180h @1: mov bl,al mov bh,0 mov [(offset bKeys)-1+bx],ah mov dx,61h in al,dx mov ah,al or al,80h out dx,al xchg al,ah out dx,al mov al,20h < tab> out 20h,al end; procedure InitializeKeyboardDriver; var b1: Byte;< r> begin for b1:=1 to 127 do bKeys[b1]:=0; GetIntVec(9, pInterr upt09hVector); SetIntVec(9, @KeyboardDriver) end; procedure Uninitiali zeKeyboardDriver; begin SetIntVec(9, pInterrupt09hVector) end;
Процедура KeyboardDriver является "драйвером", который вызывается всякий раз, когда на клавиатуре нажимается или отжимается какая-нибудь клавиша. Эта процедура определяет скан-код клавиши и отображает полученную информацию в массиве bKeys. Для запуска "драйвера" необходимо вызвать процедуру InitializeKeyboard-Driver.
Для завершения работы "драйвера" предназначена процедура UninitializeKey-boardDriver. Методика использования рассмотренных подпрограмм такова: сначала вызывается InitializeKeyboardDriver; после этого в любой момент времени можно определять, какие клавиши нажаты, а какие нет; когда необходимость в определении нажатых клавиш отпадет или же программа должна будет завершить свою работу, вызывается UninitializeKeyboardDriver. Статус клавиш (нажата/отжата) определяется следующим образом: если значение bKeys[bScanCode] равно единице, то клавиша со скан-кодом bScanCode нажата, если нулю - то отжата.
Каждая клавиша имеет свой собственный скан-код. Для того чтобы определить, какой код соответствует некоторой клавише, можно воспользоваться справочниками или же написать небольшую программу, отображающую скан-коды нажатых клавиш. Следует отметить одну особенность использования приведенных выше подпрограмм.
Если в процессе работы программы необходимо вызвать такие функции и процедуры, как KeyPressed, ReadKey, Read и Readln, то следует либо разработать их аналоги, использующие возможности, предоставляемые "драйвером", либо отключить "драйвер" процедурой UninitializeKeyboard-Driver, а затем, когда отработают стандартные подпрограммы Pascal'я, опять вызвать InitializeKeyboardDriver. Завершая обсуждение использования клавиатуры, рассмотрим еще несколько небольших процедур, которые могут пригодиться при разработке игр или другого программного обеспечения.
procedure LockKeyboard; assembler; asm in al,21h or al,2 < n> out 21h,al end; procedure UnlockKeyboard; assembler; asm < tab> in al,21h and al,253 out 21h,al end; procedure ClearKeyboardBuffer; assembler; asm mov ax,0C00h int 21h end; procedure SwitchWindowsKeysOn; assembler; asm mov ax,1681 h int 2Fh end; procedure SwitchWindowsKeysOff; assembler; as m mov ax,1682h int 2Fh end;
Процедура LockKeyboard блокирует клавиатуру, не позволяя фиксировать нажатия/отжатия клавиш. UnlockKeyboard выполняет противоположное действие. Процедура ClearKeyboardBuffer очищает буфер клавиатуры, в котором сохраняются необработанные нажатия клавиш. Она предназначена для очистки буфера перед запросом ввода данных с клавиатуры.
Процедуры SwitchWindowsKeysOn и SwitchWindows-KeysOff соответственно разрешают или запрещают прерывание выполнения программы, выполняемой в эмуляции DOS, посредством нажатия специальных Windows-клавиш. Вообще-то, назначение этих двух подпрограмм несколько иное, но они подойдут и для использования так, как было сказано.
Перейдем теперь к обсуждению использования мыши. Здесь будут даны только необходимые подпрограммы, тогда как стандартные драйверы мыши предоставляют гораздо большую функциональность. Тем, кому потребуются более широкие возможности, следует обратиться к справочной информации по прерыванию 33h.
const LeftButton=1; RightButton=2; CenterButton=4; function MouseSupportPresence: Boolean; asse mbler; asm xor ax,ax int 33h cmp ax,0FFFFh mov al,0 jne @1 inc ax @1: end; procedure ShowMousePointer; assembler; asm mov ax,1 int 33h< r> end; procedure HideMousePointer; assembler; asm mov ax,2 < n> int 33h end; function MouseButtonsStatus: Word; assembler; asm< r> mov ax,3 int 33h mov ax,bx end; procedur e GetMousePointerPositions(var wX, wY: Word); assembler; asm mov ax,3 int 33h push ds lds si,wX mov [si],cx lds si,wY mov [si],dx pop ds end; procedure Set MousePointerPosition(wX, wY: Word); assembler; asm mov ax,4 mov cx,wX mov dx,wY int 33h end;
Первым шагом, который следует сделать перед тем, как работать с мышью, является проверка присутствия поддержки этого устройства. Функция MouseSupportPresence кроме определения наличия поддержки проводит инициализацию мыши, что вызывает перемещение указателя мыши в центр экрана, причем указатель делается невидимым.
Процедуры ShowMousePointer и HideMousePointer соответственно показывают и прячут указатель мыши. После инициализации следует вызвать первую процедуру. Если необходимо осуществить вывод на экран какой-либо информации, то указатель следует предварительно спрятать, а затем показать вновь. Эта последовательность действий необходима для того, чтобы, если вывод осуществляется в ту область, где в данный момент находится указатель мыши, не происходили коллизии выводимого изображения и изображения указателя. Для определения того, какие клавиши мыши нажаты, предназначена функция MouseButtonsStatus.
В возвращаемом ею значении нулевой бит показывает, нажата ли левая клавиша (1 - нажата, 0 - не нажата). Первый бит закреплен за правой, а второй - за центральной клавишами мыши. Если, к примеру, необходимо определить, нажата ли правая клавиша, то для этого следует проанализировать значение выражения (MouseButtonsStatus and RightButton). Если оно равно нулю, то клавиша не нажата, если оно больше нуля, то клавиша нажата.
Аналогично проверяются другие клавиши. Последние две процедуры определяют (GetMousePointerPosition) или устанавливают (SetMousePointerPosition) текущее положение указателя мыши, которое сохраняется в переменных wX и wY. В графическом режиме положение указателя задается с шагом равном 1, в текстовом - равном 8. В завершение этой части статьи рассмотрим использование джойстика. Для работы с этим устройством вполне достаточно двух подпрограмм, которые позволяют получать данные от двух джойстиков сразу.
const A1Button=16; A2Button=32; B1Button=64; B2Button=128; function JoysticksButtonsStatus: Byt e; assembler; asm mov ah,84h xor dx,dx int 15 h end; procedure GetJoysticksCoordinates(var wAX, wAY, wBX, wBY: Word); assembler; asm mov ah,84h mov dx,1 int 15h push ds lds si,wAX mov [si],ax lds si,wAY mov [si],bx lds si,wBX mov [si],cx lds si,wBY mov [si],dx pop ds end;
Первая подпрограмма - JoysticksButtonsStatus - определяет, какие клавиши нажаты.
Для этого анализируется выражение (JoysticksButtonsStatus and xyButton), где "x" задает джойстик ("A" или "B" ), а "y" - клавишу выбранного джойстика ("1" или "2"). В отличие от того, как это производилось с определением статуса клавиш мыши, для клавиш джойстика нулевое значение выражения означает, что клавиша нажата. Другая подпрограмма - GetJoysticksCoordinates - возвращает координаты джойстиков. Переменные wAX и wAY сохраняют координаты джойстика "A", а wBX и wBY - джойстика "B".
Несколько слов по поводу того, как определить наличие джойстиков. Для этой цели следует вызвать процедуру GetJoysticksCoordinates, и если все переменные (wAX, wAY, wBX и wBY) равны нулю, значит джойстики не подключены. Итак, в этой части были рассмотрены вопросы использования различных устройств управления компьютерными играми. Информации, представленной здесь, а также в предыдущих частях статьи, достаточно для того, чтобы приступить к программной реализации игры.
Когда игра будет практически готова, в нее можно будет добавить воспроизведение звуков через Sound Blaster. Как это сделать - тема следующей части статьи. Продолжение следует
Сергей Иванчегло