главная страница статьи файлы о сайте ссылки |
Георгий Мошкин Часть 1 - Часть 2 - Часть 3 - Часть 4 - Часть 5 - Часть 6 - Часть 7 Итак, приступим к написанию ядра RTS-стратегии. Для начала займёмся разработкой описания юнитов. Для описания юнитов будем использовать обычные текстовые файлы. В дальнейшем вы можете написать удобный редактор этих файлов. Но на начальном этапе разработки и отладки движка удобно описывать юниты именно с помощью текстовых файлов. Попбробуем описать юнит "крестьянин". Нам необходимо описать следующие элементы:
Как видите, этого списка вполне достаточно для реализации огромного разнообразия юнитов. Но помимо описания сущности юнита необходимо задать данные о спрайтах, звуках для юнита:
Создание неподвижных зданий при такой структуре описания юнитов делается очень просто: для этого нужно сделать пустой запись "скорость передвижения". Итак, мы сформировали облик файлов с данными, описывающих юниты. Перед тем, как перейти к написанию исходного кода, попробуем набросать примерное содержание текстового файла с описанием юнита "крестьянин". Разделы будем начинать со знака ")", а подразделы - со знака "]":
Файл описания золотника будет выглядеть следующим образом:
Это означает, что золотник имеет уровень энергии 100. Скорость самовосстановления - 100/1=100 единиц энергии в секунду. Ресурс золота составляет 100000 единиц. Для повышения гибкости формата данных будем использовать массивы переменной длины. Это означает, что вместо массива:
мы будем использовать конструкцию вида:
Раздел USES модуля rtsmain.pas дополним библиотеками "_strman, sysutils":
Для хранения данных об энергии зададим тип TEnergia:
Названия записей выбраны таким образом, чтобы было легко догадаться об их назначении: nazv - название энергии; max - максимальный уровень энергии; vosst - время восстановления от 0 до макимального уровня. Скорость восстановления определяется следующим образом: V=max/vosst. Далее задаём тип описания ударов:
В начале идёт nazv - название удара; chto - в этой строке записывается название уменьшаемой в результате удара энергии; sila - сколько единиц отнимает удар; treb_energ - название требуемой энергии для удара; rash_energ - расход требуемой энергии для удара. Теперь определяем тип для добычи:
Здесь nazv - название добываемого ресурса; kolvo - количество одной порции; vrem - время добычи одной порции ресурса.
Здесь nazv - название расходуемого при покупке ресурса; kolvo - цена, измеряемая в единицах ресурса; vrem - время расходования ресурса. Для описания количества объектов задан следующий тип:
С помощью этого типа мы можем задавать количество kolvo какого-нибудь объекта с названием nazv. Используя типы для задания цены и количества, мы можем описать требования к апгрейду следующим образом:
Используя в качестве базы описанные выше типы, запишем описание юнита:
Здесь nazv - это название юнита для движка (внутреннее название); nazvdisp - это название, которое будет показываться на дисплее компьютера во время игры; cena и neobh - это списки того, что требуется для покупки (создания) данного юнита; resurs - признак того, что юнит является ресурсом (золото, дерево и т.п.); radius - радиус юнита для алгоритма проверки столкновений. Опишем какую-нибудь рассу. Для этого достаточно задать тип, в котором содержатся описания всех юнитов, которые есть в данной рассе:
В игре будут разные рассы, поэтому понадобится ещё один массив:
Теперь, когда у нас сформированы все необходимые основные типы, мы можем сделать загрузчик данных из текстовых файлов. Для этой цели удобно использовать библиотеку STRMAN. Эта библиотека предназначена для работы со строками: выбор слова из предложения, поиск определённых символов и т.п.. Выше я уже писал, что разделы и подразделы текстового файла описания юнитов обозначаются круглой и квадратной скобкой. Вы можете в этом убедиться, если посмотрите описание юнита "крестьянин", которое было дано выше. Например, название раздела определяется следующим образом:
Тогда логично, что подраздел и его названия можно определить так:
В дальнейшем занесение данных в массивы осуществляется за счёт разбора названий разделов и подразделов:
Чтение из файла мы осуществляем простым readln(f,s), а сама переменная f имеет тип text (f:text). Данные организованы следующим образом: есть несколько файлов с описанием юнитов данной рассы и один главный "индексный" файл, в котором перечислены имена этих файлов:
Поэтому помимо процедуры загрузки описания юнита (ZagruzOpisan) необходимо сделать процедуру загрузки рассы (ZagruzRassa):
Эта процедура последовательно загружает описания различных юнитов, расширяя массив для данной рассы. Данные о спрайтах для каждого юнита мы добавим немного позже. А сейчас ещё раз посмотрим на структуру хранения описаний юнитов. Файл с описанием - это обычный текстовый файл, в котором данные разделены на разделы: ")название", ")энергия", ")скорость", ")удар" и т.д.. Разделы и подразделы мы обозначаем скобкой (разделы - круглой, подразделы - квадратной). Если в файле отсутствует какой-либо раздел, то соответствующий этому разделу массив будет иметь нулевую длину (пустой). Загрузчик описаний сделан таким образом, что последовательность разделов в файле не имеет значения. Если в описании присутствует раздел ")ресурс", то юниту делается соответствующая пометка (fRassa.Opisan[i].resurs:=true). Такая структура файлов очень удобна. Например, мы можем задать базу следующим образом: база это неподвижный объект, поэтому раздел ")скорость" отстутствует (кстати, никто не мешает сделать подвижную базу, или водоплавающую). Дальше: база может создавать крестьян; это умение мы записываем в раздел "стройка". Именно так. Причём здсь будет всё то же, что и в Warcraft 2: при строительстве крестьянина сработает соответствующая проверка удовлетворения требований раздела ")цена" по количеству имеющихся ресурсов и прочих объектов (ферм). Считывание строковых и числовых данных осуществляется с помощью стандартных функций и библиотеки STRMAN. Например, загрузка данных из подраздела ")ресурсы" раздела ")цена":
Здесь дана только часть исходного кода. Но если вы посмотрите на полный объём написанного мною исходного кода по загрузке текстовых файлов описания юнитов, то сразу заметите, что там всё понятно. Мы загружаем данные из текстового файла с простой структурой и размещаем их в записи элементов наших массивов. Причём сами массивы тоже имеют простую и понятную структуру данных. Загрузка и парсинг (parsing, "разбор содержимого") файлов с описанием юнитов начинается с того, что мы открываем для чтения файл fname:
Далее идёт идёт цикл вида:
В этом цикле и происходит разбор содержимого файла (парсинг файла). Внутри цикла мы при каждом проходе осуществляем считывание текстовой строки из фала. Считывание продолжается до тех пор, пока не будет достигнут конец файла (функция eof). Так как идентификатор файла f имеет тип text (f:text), то считывание строк осуществляется простой процедурой readln:
Затем мы проверяем, есть ли что-нибудь в этой строке. Это необходимо, так как строка может оказаться пустой. Если в считанной строке s есть какие-либо символы, то начинается блок "begin-end" по соответствующему "if"-у:
В считанной строке может находится идентификатор раздела, подраздела или какой-нибудь параметр. Разделы и подразделы начинаются со знака "закрывающаяся скобка". Если скобка не обнаружена, то значит в строке содержится параметр:
В блоке "begin-end", который начинается после второго "else", идёт анализ параметров. Например, если текущий раздел имеет наименование "удар", то разбор параметров удара идёт следующим образом:
В данном случае мы увеличиваем массив данных на единицу и производим запись различных характеристик удара. При этом разбор строк осуществляется с помощью функции StringWordGet из библиотеки STRMAN. Приведём пример, показывающий как действует эта функиця:
Первый параметр - это исходная строка, второй параметр - это разделитель, третий параметр - это номер слова в строке (отсчитывается с учётом выбранного разделителя). С помощью этой функции очень удобно проводить разбор текстовых строк по словам. Более подробное описание этой и других полезных функций для работы с текстовыми строками вы можете найти в исходном коде библиотеки STRMAN (читайте комментарии). Когда цикл по считыванию строк из файла отработает (функция eof(f)=true), то необходимо закрыть файл:
Стоит отметить, что на данном этапе разработки есть вещи, которые ещё не определены до конца. Это и вопрос о спрайтах, и о проверках наличия достаточного количества ферм. Но вы уже успели заметить, что структура данных позволяет дополнять её в будущем новыми элементами. К настоящему моменту у нас имеются следующие элементы:
И уже есть возможность попробовать вывести анимированный спрайт:
В блоке firsttime мы загружаем спрайт, а потом каждый кадр выводим на экран различные кадры спомощью процедуры Kvadrat. Как видите, уже готова хорошая база для начала разработки главного игрового кода. А как вы понимаете, это сделать очень просто. Вы можете убедиться в этом, если посмотрите на проект "Самодельный WarCraft". Выше я уже писал, что для каждой рассы существует свой список описаний юнитов:
Был задан массив расс:
В описанных выше массивах задаются "базовые" типы. Для экономии памяти целесообразно создать общий массив юнитов, в котором будут храниться изменяющиеся параметры (энергия, ресурсы, положение, угол поворота). Для установления взаимосвязи между массивом Opisan и массивом Obyekti
Файлы к статье: dev002src.rar - исходники (144kb), dev002exe.rar - исходники + exe (329kb). Часть 1 - Часть 2 - Часть 3 - Часть 4 - Часть 5 - Часть 6 - Часть 7 |