Даже если движок у нас — трехмерный, без плоской графики, как правило, обойтись не удается. В первую очередь — для интерфейса: до сих пор во всех или почти всех играх он остается плоским, и так, скорее всего, и будет еще достаточно долго. Эта статья — о двумерных механизмах внутри ЛКИ-Creator 3D, а также об организации ввода с клавиатуры и мыши.
Те, кто следит за развитием проекта ЛКИ-Creator c cамого начала, почерпнут из этой статьи немногое, потому что двумерная часть ЛКИ-Creator 3D почти не отличается от ЛКИ-Creator 2D. И все же она необходима для дальнейшей работы с пакетом.
Кнопки и текстовые строки
Большинство новых функций, о которых мы сегодня будем говорить, заимствовано из ЛКИ-Creator 2D. Несколько отличается только механизм их вывода на экран.
Четыре интерфейсных элемента ЛКИ-Creator 2D — TLKIStatic, TLKIButton, TLKIText, TLKIEdit — обладают трехмерными аналогами (TLKITextSurface, который мы какое-то время поддерживали в целях совместимости с предыдущими версиями, не включен в трехмерный пакет).
Это важно: использовать двумерные элементы в трехмерном движке напрямую нельзя. Хотя функции TLKI3dButton, например, строго идентичны функциям TLKIButton, механизм внутренней работы с ними различается. Трехмерные элементы отличаются буквами 3d в названии: TLKI3dText, TLKI3dStatic и так далее. Это означает, что они предназначены для использования в трехмерном движке, и ничего более: никакой «внутренней» трехмерности в них нет.
Как и раньше, мы можем расставлять эти интерфейсные элементы на форме средствами Delphi, и это будет работать даже в случае полноэкранного режима. Параметр IsVisible отвечает за то, чтобы элемент прорисовывался.
Это важно: если у вас есть хоть один интерфейсный элемент с текстом — не забудьте проинициализировать его шрифт. Как это делается — смотрите в занятии втором (на нашем диске).
Напомним вкратце, что делает каждый из элементов и для чего они предназначены.
TLKI3dStatic —значок на экране, который никак не реагирует на нажатия; впрочем, его можно, при необходимости, двигать, менять картинку (процедура СhangePic) и убирать с экрана (свойство IsVisible). Его можно использовать, например, для неактивных частей интерфейса (панелей, логотипов и так далее), значков жизней, состояний персонажа и так далее.
На заметку: если у вас поверх него стоят активные элементы, вроде кнопок — подложка должна быть описана раньше активных элементов. Элементы отображаются в том порядке, в каком описаны, то есть более поздние накладываются на более ранние.
TLKI3dButton —кнопка. От TLKI3dStatic отличается тем, что реагирует на нажатие (обработчиком события OnPressed). Кроме того, у кнопки может быть «нажатая» фаза, которая отрисовывается, пока кнопка вдавлена — за нее отвечает свойство Alternate.
TLKI3dText —статический текст, он же — метка. Сама отображаемая строка хранится в свойстве Text. Менять то, что в ней написано, можно, но после изменения свойства Text нужно запустить метод Update. Годится для любых подписей на экране, кроме тех, в которые сам пользователь будет вводить буквы.
Это важно: менять шрифт текстовых элементов в процессе работы программы крайне не рекомендуется.
TLKI3dEdit —строка ввода. Поле CanSwitchLanguage определяет, можно ли переключать языки ввода. Не делайте этот элемент активным «по умолчанию», не то он будет перехватывать все нажимаемые клавиши. Как и прежде, в окне редактуры работают буквенно-цифровые клавиши, Shift, пробел, Backspace, Delete, стрелки влево и вправо, Home, End. Кнопки Enter и Tab заканчивают ввод и деактивируют компонент.
На заметку: если вы захотите запрограммировать еще какие-нибудь хитрые двумерные компоненты, имейте в виду, что двумерные координаты мышиного указателя всегда доступны в переменных MouseX и MouseY.
Другие компоненты Delphi
Поскольку под отрисовку трехмерности у нас уходит вся площадь экрана либо формы, никакие из стандартных визуальных компонентов Delphi поверх трехмерного экрана отображаться не могут. Они не предназначены для отрисовки на DirectX-поверхности. Но вот невизуальные компоненты — таймеры, диалоги, компоненты работы с сетью и так далее — вполне могут ставиться на эту форму безо всяких ограничений.
Заметим, что диалоговые компоненты, которые создают новое окно и в нем производят какие-то действия, вполне допустимы в оконном режиме. Точно так же вы вольны создавать сами новые формы, в которые уже можно поместить все, что заблагорассудится.
Это важно: на данный момент ЛКИ-Creator не поддерживает возможности сделать несколько потомков TLKI3dForm в одном приложении. Совмещение его с окном, в котором содержится двумерный TLKI2dEngine, возможно, но при этом могут возникнуть ошибки.
То же самое касается и компонентов, разработанных другими пользователями. Если они не предполагают отображения на этой форме — их использовать можно, если нет — их применение вызовет ошибку.
Но что же все-таки делать, если хочется поместить стандартные компоненты Delphi на трехмерную форму? Есть один-единственный выход: на время их работы отключать трехмерное отображение. Такие средства в ЛКИ-Creator есть.
Метод формы AllHide прервет показ трехмерного экрана и сделает видимыми все ранее невидимые визуальные компоненты (а все компоненты ЛКИ-Creator — скроет). Если вам нужны не все компоненты — лишние придется отключать вручную, исходный параметр Visible во внимание не принимается. Время формы при этом останавливается, жизнь игрового мира замирает.
Соответственно, метод AllShow возвращает все на круги своя: стандартные визуальные компоненты убираются, потомки TLKI3dStatic и трехмерные объекты — появляются, время пускается снова.
На заметку: не все стандартные компоненты Delphi «хорошо себя чувствуют» в полноэкранном режиме. Рекомендуется все же ими пользоваться только в режиме окна.
Если вам нужно только остановить время — скажем, при работе какого-то интерфейсного элемента — для этого существуют процедура TimeStop, действие которой прекращается процедурой TimeGo.
Клавиатура
Для считывания нажатий клавиш (кроме случая, когда активен элемент TLKI3dEdit — там все происходит автоматически) можно использовать процедуру GetKey(var Key : integer; var Pressed : boolean). Если в Pressed оказывается false — значит, ни одна кнопка не нажата, а если true — в Key будет код нажатой клавиши.
Ввод здесь буферизованный, поэтому GetKey стоит вызывать до тех пор, пока Pressed не станет равно false — только в этом случае вы считаете все нажатые клавиши.
Поясним механизм небольшим примером (см. «Движение дельфина»).
Это — слегка видоизмененная процедура обработки игрового мира из нашего «дельфиньего» примера (см. прошлый выпуск).
Отличие — в том, что перед перемещением дельфина считываются нажатия стрелок и в зависимости от них дельфин сдвигается вверх или вниз. При каждом зарегистрированном нажатии кнопки со стрелкой меняется значение переменной Hgt, которая затем прибавляется к высоте парения дельфина над морским дном.
Для того чтобы этот пример заработал, нужно еще создать переменную Hgt типа single (приравняв ее изначально нулю) и константу Step, от которой будет зависеть скорость перемещения при нажатии на кнопку.
Не забудьте: если у вас включен TLKI3dEdit, он перехватит все клавиши, и до GetKey они просто не дойдут.
Это первый метод. Но его удобно применять в статических играх, а в динамических чаще используется второй. В нем ключевая функция — ReadImmediateKBD, а результат помещается в массив Keys.
Рассмотрим еще один пример, в котором мы оставили только ключевую часть процедуры («Небуферизованный ввод»).
Здесь проверяется, нажата ли в данный момент стрелка, и если да, то дельфин сдвигается — а на какую величину, зависит от шага (Step) и от прошедшего со времени предыдущего вызова этой процедуры времени. Так скорость движения дельфина оказывается машиннонезависимой.
(Если нажаты обе стрелки — программа в данном случае выбирает верхнюю. Можно поступить с этой ситуацией как-нибудь по-другому.)
Попробуйте подобрать параметр Step сами — так легче «почувствовать» процесс.
Это важно: никогда не используйте в одной программе оба метода ввода — во избежание непредсказуемых последствий. Также не следует в рамках одной программы вызывать GetKey из разных мест — правильнее считать все нажатые клавиши разом и потом рассортировать, какая к чему относится.