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

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


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

 

4) Программируем карту

Всем вновь присоединившимся надо прочитать последние выпуски:

С Единицы

Общие принципы работы игровой программы

Перед программированием


Программируем карту

Сначала создадим, как обычно, главный файл программы main.pas:

  program LearningRPG;

  begin

  end.

Затем добавим к проекту модуль map.pas:

  unit Map;

  interface

  implementation

  end.

Мы договорились, что будем работать с записями, избегая объектной технологии. Поэтом опишем новые типы - ячейку карты, и саму карту, как обычные записи:

  type TMapCell = record
       Tile: Integer;
       IsVisible: Boolean;
       end;

       TMap = record
       Cells: array[1..MAP_WIDTH,1..MAP_HEIGHT] of TMapCell;
       LocalMapLeft, LocalMapTop: Integer;
       end;

Здесь поле Tile хранит значение тайла ячейки карты, IsVisible определяет, виден ли тайл. Все ячейки хранятся в массиве Cells (первая размерность - координата x, вторая - координата y), текущие координаты видимой части карты записываются в поля LocalMapLeft и LocalMapTop (значение левой и верхней координат).

К этому тексту необходимо добавить некоторые важнейшие константы. MAP_WIDTH и MAX_HEIGHT задают размеры глобальной карты (счет начинается с нуля), LOCAL_MAP_WIDTH и LOCAL_MAP_HEIGHT - размеры видимой части.

  const LOCAL_MAP_WIDTH = 8;
         LOCAL_MAP_HEIGHT = 8;

         MAP_WIDTH = 32 + LOCAL_MAP_WIDTH*2;
         MAP_HEIGHT = 32 + LOCAL_MAP_HEIGHT*2;

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

Размеры пещеры 32*32 тайла и видимой части 8*8 тайлов конечно невелики, но в нашем примере они физически ограничены размером сегмента данных ДОС-а (64 килобайта). Для Windows-версии эти размеры можно увеличить практически неограниченно. Здесь видно, что размеры этих констант существенно зависят от операционной системы, поэтому сразу выделим их директивами условной компиляции, не забыв добавить в самое начало файла вызов {$I DEFINES.INC} (об этом вызове - в следующем выпуске):

  const

  {$IFDEF DOS_GAME}
         LOCAL_MAP_WIDTH = 8;
         LOCAL_MAP_HEIGHT = 8;

         MAP_WIDTH = 32 + LOCAL_MAP_WIDTH*2;
         MAP_HEIGHT = 32 + LOCAL_MAP_HEIGHT*2;
  {$ENDIF}

Далее определим, какие значения поля Tile каким тайлам будут равны. Условимся о двух типах свободной местности (например, земля и трава) и двух типах непроходимых зон (например, камни и дерево). Кроме того, подготовим такие тайлы, как "ступеньки вверх" (переход на уровень выше) и "ступеньки вниз" (переход на уровень ниже).

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

  const
         tileGrass = 1;
         tileGround = 2;
         tileStairsUp = 3;
         tileStairsDown = 4;

         tileFirstStopTile = 5;
         tileTree = tileFirstStopTile;
         tileStone = tileFirstStopTile+1;
         tileLast = tileFirstStopTile+1;

Такой подход удобен тем, что позволяет добавлять новые тайлы в этот список, не корректирую значения всех других тайлов группы. Достаточно лишь изменить значения констант tileFirstStopTile или tileLast.

Как определить, проходИм ли некоторый тайл? Вынесем такую проверку в отдельную функцию. Содержимое ее будет тривиальным:

  function FreeTile(Tile: Integer): Boolean;
  begin
  FreeTile := Tile < tileFirstStopTile;
  end;

Игровая пещера (последовательность локаций)

Глубину пещеры (семь уровней) зададим в константе

const MaxDungeonLevel = 7;

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

  type TGameMap = array[1..MaxDungeonLevel] of TMap;

  var GameMap: TGameMap;
      CurMap: Integer;

Переменная CurMap будет хранить номер текущей локации (элемента массива GameMap), используемой в игре.

Следующий шаг - генерация карты. Для этого существует немало хороших алгоритмов, причем все они отличаются достаточно высокой сложностью. Мы возьмем простой вариант и будем расставлять непроходимые тайлы на пустой карте случайно. Практика показывает, что если на карте около 35% непроходимых, случайно "набросанных" тайлов, то перемещаться по ней становится ничуть не легче, чем по лабиринту.

Запишется процедура генерации игровой карты так:

  procedure MapGeneration(MapLevel: Integer);
  var x,y: Integer;
  begin
  CurMap := MapLevel;
  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;
       end;
    GameMap[CurMap].Cells[x,y].IsVisible := false;
    end;

  GameMap[CurMap].LocalMapLeft := MAP_WIDTH div 2;
  GameMap[CurMap].LocalMapTop := MAP_HEIGHT div 2;

  end;

В качестве параметра указывается уровень в пещере для генерируемой локации.

Первая проверка выясняет, находится ли точка карты (x,y) вблизи границ, которые мы договорились заполнять непроходимыми тайлами. Далее в зависимости от результата датчика случайных чисел выбирается тайл - он может быть либо непроходимым (tileTree) либо одним из двух проходимых. В заключение данный тайл помечается как исходно невидимый.

Начало видимой части сделаем примерно расположенной в центре глобальной карты.

Далее - добавляем средства визуализации карты.


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


Есть вопросы по самому жанру roguelike-игр, которые мы делаем. Почитать замечательную статью можно тут: http://rlgclub.ru/articles.php?index=base_Rogue.html

Есть также адаптированная (мной :) и переведенная (мной :) версия легендарной "рогульки" "Мория". Архив 270 kb брать тут: http://russianenterprisesolutions.com/download/um.zip.

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


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

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

 

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

В избранное