Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Создание ролевой компьютерной игры 12) Программируем предметы


Информационный Канал Subscribe.Ru

Разработка ролевой игры

12) Программируем предметы

Вопросы.

Здраствуйте уже какой ваш выпуск читаю,а так и не могу понять какой (или какими)программами надо пользоваться для создания RPG,что только не пробывал не получается.Если не трудно пожалуйста напишите какими программами надо пользоваться,уж очень хочется програмированием всерьёз заняться.Зарание огромное спасибо.

Для сборки и запуска текущей версии кода (все исходники можно скачать по ссылке внизу выпуска) нужен Turbo/Borland Pascal - древняя версия компилятора Паскаля, "предшественник" Дельфи. О нем говорилось в 64-м выпуске, а найти его можно в свободном доступе вот тут например: http://borlpasc.narod.ru/turbo_pacs.htm

Кроме того, в выпусках обсуждалась возможность эмуляции турбопаскакаля в дельфи :) Но далее мы полностью перенесем нынешний код в Дельфи уже в графическом варианте.

Здравствуйте, Алексей!!!
Я недавно подписался на вашу рассылку а предыдущие статьи скачать возможности не имею немогли бы вы выслать их мне в файле с расширением RTF(Если конечно можно???)

Если кто-нибудь из подписчиков переведет рассылку в RTF-формат, то я ее в таком виде с удовольствием выложу. Пока текущий срез всей рассылки можно взять в одном архиве в html-виде:

http://russianenterprisesolutions.com/sbo/download/sbo.zip 1,1 mb


Предметы.

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

Подготовим модуль GameItem:

  unit GameItem;

  interface

  implementation

  end.

Чем будет характеризоваться предмет? Уникальным идентификатором конкретного объекта, координатами местоположения на карте, типом предмета (броня, оружие), названием и набором дополнительных свойств. Заранее определить все мыслимые свойства очевидно, не удастся, так как мы пока не знаем, какие конкретно типы предметов будут появляться в нашей игре. В то же время желательно описать концепцию предмета так, чтобы в дальнейшем она могла быть свободно расширена под любые объекты, вводимые разработчиком. Поступим так - подготовим два достаточно больших массива целых и вещественных чисел, и в зависимости от типа предмета будем по разному трактовать содержимое этих массивов. Размеры массивов определятся следующими константами:

  const MaxItemInt = 20;
         MaxRealInt = 20;

Тип предмета (оружие или броня) зададим отдельным типом TGameItemType:

  type TGameItemType = (itemHandWeapon, itemArmor);

Даее нам потребуется тип, описывающий конкретный игровой предмет:

  type TGameItem = record
        ID: Integer;
        x,y: Integer;
        IType: TGameItemType;
        Name : String[20];
        Ints : array[1..MaxItemInt] of Integer;
        Reals: array[1..MaxRealInt] of Real;
        end;

Как хранить доступные в локации предметы? Напрашивающийся способ - сделать это по аналогии с хранением монстров. То есть прежде всего нам потребуется массив ItemTypes шаблонов игровых предметов, идеологически схожий с массивом шаблонов монстров MonsterTypes. Первоначально в нем будет два вида оружия и два вида брони:

  const MaxItemTypes = 4;
         ItemTypes: array[1..MaxItemTypes] of TGameItem =
         (
         (ID:1;
          x:0; y:0;
          IType:itemHandWeapon;
          Name:STR_AXE;
          Ints: (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
          Reals: (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)),
         (ID:2;
          x:0; y:0;
          IType:itemHandWeapon;
          Name:STR_SWORD;
          Ints: (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
          Reals: (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)),
         (ID:3;
          x:0; y:0;
          IType:itemArmor;
          Name:STR_HELM;
          Ints: (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
          Reals: (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)),
         (ID:4;
          x:0; y:0;
          IType:itemArmor;
          Name:STR_BODYARMOR;
          Ints: (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
          Reals: (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
         );

Названия предметов поместим как обычно в модуль Texts, не забыв сослаться на него в разделе Interface модуля GameItem:

  const STR_AXE = ' ' Топор ' ;
          STR_SWORD = ' Меч ' ;

          STR_BODYARMOR = ' Бронежилет ' ;
          STR_HELM = ' Шлем ' ;

Сами предметы поместим в массив констант Items.

  const MaxItems = 10;
  var Items: array[1..MaxItems] of TGameItem;

Предметы не всегда будут существовать в игре. Они могут по различным причинам пропадать, портиться, продаваться, и так далее. Как отличать элементы массива Items, хранящие используемые в игре предметы, от "пустых" предметов? Договоримся, что значение координаты x, равное нулю, будет означать незадействованный, свободный элемент в Items.

Теперь определимся со свойствами разных типов предметов. Оружие будет, очевидно, характеризоваться величиной наносимого поражения и вероятностью попадания. Броня ограничится одним параметром - "толщиной", или величиной поглощения удара противника (как у шкуры монстров). Опишем константы, соответствующие этим свойствам:

  const intAttack_d1 = 1;
         intAttack_d2 = 2;
         intAttackHit = 3;
         intArmorDefence = 1;

Эти значения определяют индексы массива Ints, в которых хранятся соответствующие значения. Так, параметры виртуального броска кубиков (число кубиков и число граней каждого кубика) будут записываться в первый (intAttack_d1) и второй (intAttack_d2) элементы, вероятность попадания - в третий (intAttackHit). Если же тип элемента - броня, то уровень ее защиты будет размещаться в первом (intArmorDefence) элементе. Возможные значения всех этих параметров занесем в наш массив ItemTypes:

         ItemTypes: array[1..MaxItemTypes] of TGameItem =
         (
         (ID:1;
          x:0; y:0;
          IType:itemHandWeapon;
          Name:STR_AXE;
          Ints: (1,6,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
          Reals: (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)),
         (ID:2;
          x:0; y:0;
          IType:itemHandWeapon;
          Name:STR_SWORD;
          Ints: (2,4,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
          Reals: (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)),
         (ID:3;
          x:0; y:0;
          IType:itemArmor;
          Name:STR_HELM;
          Ints: (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
          Reals: (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)),
         (ID:4;
          x:0; y:0;
          IType:itemArmor;
          Name:STR_BODYARMOR;
          Ints: (5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
          Reals: (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
         );

То есть меч будет наносить поражение, равное значению, выпавшему на двух четырехгранных кубиках, а вероятность попадания составит 80%. А бронежилет сможет погасить пять пунктов удара противника.

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

Разбрасываем предметы по локации

Этот процесс схож с распределением по локации ловушек. В силу условной ограниченности числа предметов в игре (десять) продемонстрируем несложный прием расчета вероятности размещения предмета на тайле в процессе генерации карты. Общее число доступных тайлов в игре - 32*32 = 1024 (для ДОС-версии). Разделив эту величину на число предметов, получим 102,4 - это и есть среднее количество тайлов, на которых желательно разместить один предмет. Отсюда получим вероятность размещения предмета на конкретном тайле - она будет примерно равна одной сотой.

Следом за оператором, изредка устанавливающим на свободный тайл карты ловушку или источник (процедура MapGeneration), добавим группу операторов, добавляющую в массив Items случайный элемент из массива ItemTypes. Переменная n нужна, чтобы следить за постепенным заполнением массива Items. Правда, наш алгоритм не гарантирует, что все элементы Items обязательно получат значения. Поэтому оставшиеся не у дел "предметы" сделаем невидимыми - занесем в поле x значение ноль, что, как уже говорилось выше, означает свободный элемент в Items.

  procedure MapGeneration(MapLevel: Integer);
  var n,i,x,y: Integer;
  begin
  CurMap := MapLevel;
  n := 0;
  for x := 1 to MAP_WIDTH do
  for y := 1 to MAP_HEIGHT do
    begin
    if (x <= LOCAL_MAP_WIDTH) or (x >=
  MAP_WIDTH-LOCAL_MAP_WIDTH) or
       (y <= LOCAL_MAP_HEIGHT) or (y >=
  MAP_HEIGHT-LOCAL_MAP_HEIGHT) then
       GameMap[CurMap].Cells[x,y].Tile := tileStone else
       begin
       if random(100) < 35
          then GameMap[CurMap].Cells[x,y].Tile := tileTree else
          if random(2) = 0
             then GameMap[CurMap].Cells[x,y].Tile := tileGrass
             else GameMap[CurMap].Cells[x,y].Tile := tileGround;

       if random(100) = 0 then
          if random(2) = 0
             then GameMap[CurMap].Cells[x,y].Tile := tileTrap
             else GameMap[CurMap].Cells[x,y].Tile := tileLive;

       if FreeTile(GameMap[CurMap].Cells[x,y].Tile) then
        if random(100) = 0 then
          begin
          inc(n);
          if n <= MaxItems then
             begin
             Items[n] := ItemTypes[random(MaxItemTypes)+1];
             Items[n].x := x;
             Items[n].y := y;
             end;
          end;
       end;
    GameMap[CurMap].Cells[x,y].IsVisible := false;
    end;

  for i := n+1 to MaxItems do
      Items[i].x := 0;

  ...

Перед попыткой размещения предмета вставлено условие, согласно которому предмет может размещаться только на проходимом для персонажа тайле (что естественно).

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

  const ItemRecords: array[TGameItemType] of TTileRecord =
     (
     (C: ' + ' ; Clr:LightCyan),
     (C: ' [ ' ; Clr:LightGreen)
     );

Размер этого массива определяется структурой типа TGameItemType.

Подготовим для отображения предметов процедуру ShowItem в модуле LowLevel:

  procedure ShowItem(itm: TGameItem);
  begin
  GoToXY( WINDOW_LEFT+itm.x-GameMap[CurMap].LocalMapLeft,
          WINDOW_TOP+itm.y-GameMap[CurMap].LocalMapTop );
  TextColor( ItemRecords[itm.IType].Clr );
  Write( ItemRecords[itm.IType].C )
  end;

Она будет вызываться из процедуры ShowItems, которую разместим в модуле Game, так как в ней не будет кода, зависимого от операционной системы:

  procedure ShowItems;
  var i: Integer;
  begin
  for i := 1 to MaxItems do
    if VisiblePoint(Items[i].x, Items[i].y) then
       ShowItem(Items[i]);
  end;

Показывать предметы станем сразу следом за выводом тайлов карты:

  procedure ShowGame;
  begin
  ShowMap;
  ShowItems;
  ShowMonsters;
  ShowHero(CurHero);
  ShowHeroInfo(CurHero);
  end;

Возможен еще один, более эффективный и простой - но и более ресурсоемкий - способ хранения предметов в игре. Он подходит для случаев, когда у разработчика заведомо отсутствуют жесткие ограничения на доступность компьютерных ресурсов. Так, при создании игр для ДОС-а (и соответственно старых компьютеров) приходится учитывать множество нюансов, связанных с низким быстродействием процессора (чтоне позволяет реализовывать достаточно сложные алгоритмы искусственного интеллекта или в крайнем случае требует значительных дополнительных усилий по их оптимизации и использовании ассемблерных вставок), ограниченными объемами оперативной памяти (что накладывает ограничения на количество и объемы хранимых в программе массивов и констант) и жесткого диска (что не позволяет создавать качественное анимационное и музыкальное сопровождение).

При создании Windows-приложений для современных персональных компьютеров подобные проблемы как правило не возникают. Программист может резервировать в программе практически неограниченные массивы данных - до двух гигабайтов. На практике, конечно приходится исходить из более реалистичных посылок и ориентироваться на доступные объемы ОЗУ порядка 100-200 мегабайтов, что, впрочем, само по себе уже очень и очень много.

Как можно было бы реализовать эффективное хранение предметов на карте? Игровых предметов на карте может быть довольно много - сотни, а то и тысячи, поэтому процесс перерисовки всех тайлов, связанный с многократным выполнением длинного цикла проверки всех предметов (не находится ли какой-то из них на конкретном месте, как мы сделали в процедуре ShowItems), может вызывать серьезную нагрузку на процессор и заметно тормозить работу программы. Простым решением этой незадачи выглядит расширение структуры элемента карты TMapCell новым полем, имеющим тип TGameItem. Таким образом мы получаем возможность хранить один предмет на каждом тайле, получать к нему мгновенный доступ и проводить другие манипуляции, не связанные с обработкой массива Items. Однако такой подход накладывает определенные требования к оперативной памяти - не очень высокие, но весьма сложно реализуемые в ДОС-компиляторе. Опытные разработчики в таких ситуациях прибегают к средствам динамического размещения больших массивов в памяти путем запроса соответствующих объемов уже в ходе работы программы. Это возможный путь, но он не снимает ограничений, связанных с невозможностью получения в 16-разрядной операционной системе области памяти, превышающей 64 килобайта. Имеются средства обхода и этого недостатка (запуск программы в защищенном режиме с помощью дополнительных драйверов, предоставляющих виртуальный доступ ко всей оперативной памяти), однако их использование обычно вызывает трудности в настройке у конечных пользователей. ДОС-платформа была нами выбрана в демонстрационных целях, чтобы показать идею и способы создания программ, способных "собираться" и работать в разных операционных системах на основе одного исходного кода. Обход конкретных ограничений, накладываемые ДОС-ом, мы не будем рассматривать, так как это устаревшая система.

Исходный код текущей версии (всегда проверен и работоспособен, главный файл- main.pas):

http://russianenterprisesolutions.com/sbo/download/11105.zip 6250 байтов

Далее - тренировка навыков, инвентарь.


(c) 2004-2005 Сергей Бобровский bobrovsky@russianenterprisesolutions.com

Все предыдущие выпуски базового курса тут:
http://russianenterprisesolutions.com/sbo/

Дизайн рассылки: Алексей Голубев - Web-дизайн и web-программирование


Subscribe.Ru
Поддержка подписчиков
Другие рассылки этой тематики
Другие рассылки этого автора
Подписан адрес:
Код этой рассылки: comp.soft.prog.prognull.game
Архив рассылки
Отписаться
Вспомнить пароль

В избранное