Рассылка закрыта
При закрытии подписчики были переданы в рассылку "Особенности национального бизнеса" на которую и рекомендуем вам подписаться.
Вы можете найти рассылки сходной тематики в Каталоге рассылок.
Создание ролевой компьютерной игры 6) Добавляем средства визуализации карты-2
Информационный Канал Subscribe.Ru |
Создание ролевой компьютерной игры
6) Добавляем средства визуализации карты-2 |
Во-первых, обращаю внимание на изменившийся дизайн. Координаты автора дизайна - в конце рассылки. Во-вторых, для подписчиков данной рассылки напомню, что выросла она из Школы программирования с нуля, поэтому время от времени мы будем затрагивать и старые темы - во всех рассылках выходит один и тот же текст. Вопросы Один из главных. Turbo Pascal. Сейчас мы делаем программу, которая будет работать ТОЛЬКО в среде TurboPascal. Это текстовая среда разработки для DOS, и скачать ее официальную версию (она небольшая, мегабайта три) можно свободно например отсюда: http://community.borland.com/article/0,1410,20803,00.html Возможно, будет работоспособна программа и в схожей среде FreePascal: В Дельфи ПОКА этот код работать не будет. Ни в консольной, ни в другой версии - потому что для вывода на экран нам потребуются примитивные команды работы с текстовой видеопамятью TurboPascal, которые в Дельфи отсутствуют. Или вроде есть для Дельфи библиотека, которая позволяет эмулировать работу модулей CRT, DOS? Какие-то типа WinCtr, WinDos? Очень прошу, кто знает, подскажите плиз для тех, кто по каким-то причинам не может разобраться или задействовать TurboPascal. Есть для Дельфи библиотеки эмуляции текстовой dos-графики? * Наибольшие трудности вызвали директивы условной компиляции. Естественно, так как ранее мы этого в Школе не проходили :) Смысл директив заключается в том, что если компилятор берет исходный текст и транслирует его в машинный, исполнимый код (фактически - создает exe-файл), то директивы условной компиляции обрабатываются так называемым препроцессором. Он берет исходный текст программы для анализа и на выходе тоже формирует исходный текст этой же программы. А зачем тогда он нужен? Вот в нашем примере мы хотим, чтобы один и тот же исходный текст с минимальными модификациями работал бы (компилировался) в разных системах. Для этого в исходном тексте будут части, не имеющие отношения к конкретной операционной системе, а будут и зависимые от нее. Последние надо включать в исходный текст только с учетом выбранной целевой платформы исполнения (в зависимости от некоторых условий). Сами эти условия "включает" и "выключает" программист - надо сделать программу для Windows, он активизирует настройки для Windows; надо для Linux - активизирует для Linux. Сначала о настройках. Команда TP/Delphi {$DEFINE имя-константы} "включает" в программе константу с указанным именем. То есть считается, что такая константа определена. {$DEFINE DOS_GAME} Препроцессор теперь будет знать, что определена в проекте константа DOS_GAME. Сама по себе она ничего не значит, может быть либо определенной, либо не быть вообще. Далее препроцессор (он всегда работает ДО компилятора - компилятолр получает исходный текст, обработанный препроцессором) ищет в исходном тексте знакомые команды :) - все, что НЕ начинается с {$стандартная-команда-препроцессора ... } он считает единым текстом, не нуждающемся в обработке. То есть препроцессору все равно, на Паскале, Си или еще чем-то написан исходный код, он понимает лишь команды своего препроцессорного языка. Это как бы дополнительный мета-язык обработки исходных текстов. Понимает препроцессор и условные операторы :) Это - пожалуй, его самая главная заслуга. Основные нужные нам формы условных операторов препроцессора такие: {$IFDEF имя-константы} {$ENDIF} и {$IFNDEF имя-константы} {$ENDIF} Препроцессор выполняет их так. Встречая "IFDEF константа", он проверяет, была ли ранее (выше по тексту) определена указанная константа, и если да, то включает в результирующий исходный текст часть текста, располагающуюся далее до команды ENDIF (по умолчанию любой текст программы, не охваченный командами препроцессора, целиком и без изменений сразу выдается в результат). Команда IFNDEF работает наоборот - включает следующую часть текста до ENDIF, если константа НЕ была определена. Вот есть такой текст.
implementation uses Если ранее мы определили константу препроцессора DOS_GAME командой DEFINE, то данный условный оператор IFDEF включит строку с упоминанием модуля CRT в результирующий исходный текст. А если бы она не была определена (или если бы мы использовали команду IFNDEF), то результирующий текст получился бы таким (с ошибкой, так как без окночания):
implementation uses
Во всех модулях программы удобно использовать единый набор констант препроцессора, чтобы, переопределив константу в одном месте, сразу получить нужную версию кода для компилятора. Мы собрали эти константы в файле Defines.Inc. Почему такое название? Defines - определения. Inc - от include, включать. Обратите внимание на расширение! Это не Defines.Inc.Txt или Defines.Inc.Pas, а именно Defines.Inc - расширение .inc, скорее всего, в Windows не будет ни с чем ассоциировано. Впрочем, ничто не мешает взять например название файла Defines.Pas и любое другое. Тогда текст примеров просто придется немного подправлять. Включение содержимого стороннего файла осуществляется в текущий файл командой препроцессора {$I имя-файла} Сторонний файл должен находиться в одном каталоге с pas-файлами проекта. Команду включения размещаем в самой первой строчке каждого файла, чтобы единый набор препроцессорных констант автоматически прописывался в начало каждого модуля. * Насчет модулей. Сейчас у нас три модуля - главная программа; модуль (файл) map.pas; модуль (файл) lowlevel.pas. Когда я пишу "добавляем процедуру А в модуль map.pas", это значит, что в файл map.pas мы заносим код процедуры А. При этом занесение такое разделяется на две части: 1. Весь код процедуры мы размещаем в разделе implementation; 2. Заголовок процедуры мы размещаем в разделе interface. Так, добавление процедуры ShowCell в модуль LowLevel означает: 1. Добавили полное описание процедуры:
unit LowLevel;
...
implementation
procedure ShowCell(t: TMapCell; x,y: Integer);
end;
...
2. Добавили заголовок в интерфейсный раздел:
unit LowLevel;
interface
procedure ShowCell(t: TMapCell; x,y: Integer);
* По старой памяти "В опубликованнщм письме одного ученика (№10), он пишет, что "многие задачи решаются только с привлечением Асамблера" . Я так понял, что Асамблер - это что-то очень трудное и ужаное. Немогли бы Вы в крадце рассказать об этом языке: его возможностях, особенностях, и за что его так многие не любят". Ассемблер (от англ. assembly) - это язык программирования, в котором используются команды конкретного процессора (Intel, если типовая персоналка). Эти команды очень просты, поэтому сделать из них более-менее сложную программу сложно. Команды - уровня "взять число из ячейки 123, сложить с числом из ячейки 345 и результат поместить в ячейку 678. если результат больше нуля, то перейти к команде с номером 162534". итд. Прогрмма на Паскале фактически и переводится компилятором в набор таких команд. То, что записывавется на Паскале пятью операторам, на ассемблере потребует сотен команд. Основной плюс ассемблера - что программы на нем получаются очень быстрыми. Ведь транслятор переводит Паскаль на ассемблер (точнее, в двоичный код - каждой команде ассемблера соответствует своя кодировка) "автоматически" :) , не думая, поэтому то, что записано на Паскале и потом переведено в ассемблер, человек, хорошо знающий ассемблер, запрограммировал бы гораздо эффективнее. Но это трудоемко. На ассемблере пишутся разные системные утилиты, драйверы, программы для встроенных систем, где быстродействие - главное. Хакеры любят ассемблер - ведь чтобы взломать программу - exe-код, представляющий двоичные команды процессора, надо хорошо в них разбираться и понимать, что и как и на что заменить. Язык Си был придуман именно как удобная высокоуровневая замена ассемблеру, и не зависящая от конкретного процессора. Но и неудобства его определенные также связаны со стремлением поддержать такой, немного ассемблерно-подобный стиль программирования.
Добавляем средства визуализации карты-2 Теперь перейдем к реализации процесса вывода тайла карты на экран. Для этого, очевидно, нам потребуется выполнить некоторую предварительную инициализацию текстового/графического режима - чтобы например очистить экран, загрузить изображения и т. д. Создадим в модуле LowLevel процедуру VideoInitialize. Все, что она будет делать - это очищать экран. Данная деятельность потребует подключения стандартного в Turbo Pascal модуля CRT, ответственного за отрисовку в псевдографике, который нужно указать в заголовке реализации с учетом текущих настроек условной компиляции:
implementation uses Сама процедура будет содержать только один вызов стандартной функции очистки экрана:
procedure VideoInitialize; Кроме того, в заголовок главной программы потребуется добавить ссылку на модуль LowLevel. В итоге она примет следующий вид:
program LearningRPG;
begin Отметим, что в данной главной программе никакие директивы условной компиляции не потребуются, так как для Delphi и Windows вся главная часть программы будет иметь совсем другой вид (только она одна будет сильно различаться). Соответствующая настройка проекта должна выполняться с помощью, например, утилиты-менеджера проекта. Мы до этого в свое время доберемся. Как проще и красивее всего представить тайл в текстовом режиме? Выбрать для него подходящий символ и цвет (фоновый цвет всегда будем считать черным). Поэтому поставим в соответствие каждому тайлу игры запись, состоящую из двух полей - символа для отображения и цвета этого символа (из доступных стандартных цветов модуля CRT):
type TTileRecord = record
const TileRecords: array[1..tileLast] of TTileRecord = Объявление способа представления тайлов в виде константы имеет еще одно преимущество. Когда мы захотим добавить в игру новый тайл, компилятор при разборе модуля LowLevel выдаст ошибку, сообщив, что длина данного массива образов тайлов не соответствует реальному числу тайлов (указанному в константе tileLast). Таким образом нам будет автоматически выдаваться напоминание о необходимости внести изменения в код программы. В какой части разместить это определение? Так как оно привязано к ДОС-у и не используется ни в каких других модулях, кроме LowLevel, его правильнее всего ввести сразу за директивой Implementation - точнее, после следующей за ней директивы условной компиляции {$IFDEF DOS_GAME}. Чтобы программа компилировалась успешно, укажем в заголовке реализации ссылку на модуль Map:
implementation uses Map, Эта ссылка на Map расположена в "независимой" от платформы части кода, потому что модуль Map не привязан к конкретной операционной системе. Все, что нам остается сделать для отображения карты - это подготовить реализацию функции ShowCell. В текстовом режиме данная задача решается элементарно. Договоримся о начале окна вывода видимой части карты - допустим, это будет точка экрана с координатами (10,3), для чего опишем в начале ДОС-раздела реализации модуля LowLevel две константы:
const WINDOW_LEFT = 10; К этим начальным координатам будем прибавлять координаты конкретного тайла, предварительно задав его цвет, и затем выводить в нужную позицию окна с помощью стандартной функции GoToXY - с учетом сдвига видимой части окна, задаваемой переменными LocalMapLeft/LocalMapTop. Цвет элемента сформируем вызовом стандартной процедуры TextColor.
procedure ShowCell(t: TMapCell; x,y: Integer); Запустим программу - на экране появится изображение части карты! Каждый раз после нового запуска оно должно быть неповторимым. Нажмем Enter, чтобы закрыть нашу программу. С выводом карты мы почти разобрались. Почему почти? Потому что пока мы не учитываем видимость тайлов, которая определяется значением флажка IsVisible. Добавим соответствующую проверку - если тайл невидим, то вместо символа тайла выведем пробел:
procedure ShowCell(t: TMapCell; x,y: Integer); Если теперь запустить программу, то, естественно, изображение будет пустым, так как изначально все тайлы карты невидимы - не исследованы. Какой аспект игры будем реализовывать дальше? Можно, конечно, заняться расстановкой монстров, предметов или ловушек, однако, во-первых, они все равно не будут видны на неисследованной карте, а во-вторых, желательно все же поскорее ввести в игру главное действующее лицо - героя, и начать им управлять. Поэтому следующим этапом совершенствования игры будет создание героя. (c) 2004-2005 Сергей Бобровский bobrovsky@russianenterprisesolutions.com
Школа программирования с нуля
Дизайн рассылки: Алексей Голубев - Web-дизайн и web-программирование |
Subscribe.Ru
Поддержка подписчиков Другие рассылки этой тематики Другие рассылки этого автора |
Подписан адрес:
Код этой рассылки: comp.soft.prog.prognull.game |
Отписаться
Вспомнить пароль |
В избранное | ||