Меню сайта
Форма входа
Категории раздела
Уроки по созданию игр [38]
Программирование игр разной сложности
Игровые алгоритмы [24]
Алгоритмы, которые уже реализованы для разных жанров игр
Графика [5]
Учимся работать с графикой в Делфи
Мультимедиа [3]
Работа с мультимедийными возможностями Делфи
Другие статьи [18]
Статьи не вошедшие не в один из разделов
Ошибки [4]
Всевозможные ошибки и пути их решения
Вторник, 01.07.2025, 10:40
Приветствую Вас Гость

Статьи по программированию

Главная » Статьи » Уроки по созданию игр

Создание Ролевой игры РПГ на Паскале и Делфи. Урок 8

Урок 8. Программируем главного героя-2

 

Программируем главного героя-2

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

 

procedure   ShowHero(HeroNum:   Integer);

begin

GoToXY(

WINDOW_LEFT+Heroes[HeroNum].x-GameMap[CurMap].LocalMapLeft,

WINDOW_TOP+Heroes[HeroNum].y-GameMap[CurMap].LocalMapTop   );

TextColor (   White   );

Write (    'X'    )

end;

 

Герой выводится белым символом X. Чтобы процедура компилировалась нормально, в список подключаемых модулей раздела реализации надо добавить модуь Hеrо:

 

implementation uses   Hero,

{$IFDEF   DOS_GAME} CRT;

{$ENDIF}

 

В каком месте программы надо выводить образ героя?

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

 

Unit Game;

Interface

procedure  ShowGame;

implementation uses  Map,   LowLevel,   Hero;

procedure  ShowGame;

begin

ShowMap;

Hero (CurHero) ;

End;

 

End.

 

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

 

program   LearningRPG;

uses Map,   LowLevel,   Hero,   Game;

begin

randomize;

Videoinitialize;

MapGeration (1);

InitHeroes;

ShowGame;

Readln;

End.

 

 Запустив эту программу, мы получим на экране уже что-то, напоминающее вид нормальной игры.

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

 

while   true  do

begin

ShowGame;

k    :      ReadKey;

  case   k   of

  # 0 :   begin

            k := ReadKey;

            case k of

            ' К ' : MoveHero( -1,0 ) ;

            ' M ' : MoveHero( +1,0 );

            ' H ' : MoveHero ( 0,-1 );

            ' P ' : MoveHero( 0,+1 );

            end;

   end;

@27 : break;

end;

end.

 

Рассмотрим этот код поподробнее. В начале бесконечного цикла происходит опрос клавиатуры, и в зависимости от нажатой клавиши происходит вызов той или иной функции программы. Значение #0 означает, что нажата специальная клавиша (функциональная или клавиша-стрелка), поэтому необходимо считать дополнительный клавиатурный код. Значение #27 соответствует клавише Escape, значения К, М, N, Р - стрелкам "влево", "вправо", "вверх" и "вниз". Чтобы переместить героя по карте, надо вызвать процедуру MoveHero, которая получает в качестве параметров сдвиг относительно текущего положения героя по горизонтали и вертикали. Так, вызов MoveHero( +1,0 ) должен сдвинуть героя на карте на один таил вправо (координата х увеличится на +1, а координата у не изменится).

Обратите внимание, что в начале цикла стоит вызов процедуры ShowGame - здесь происходит перерисовка экрана с учетом всех внесенных после действий пользователя изменений в игре.

Реализацию процедуры MoveHero разместим в модуле Game, не зависящем от платформы. Нам удастся обойтись уже готовыми функциями отрисовки, подготовленными в модуле LowLevel.

 

procedure MoveHero(   dx,dy:   Integer   );

begin

if not FreeTile{ GameMap[CurMap].Cells[

Heroes[CurHero].x+dx,Heroes[CurHero].y+dy].Tile ) then Exit;

inc(Heroes[CurHero].x,dx);

inc(Heroes[CurHero].y, dy) ;

SetHeroVisible(CurHero);

end;

 

Этот код достаточно тривиален - сначала проверяется, свободен ли таил, в направлении которого выполняется перемещение героя. Если он свободен, происходит изменение координат героя. В новом месте уточняется, какие тайлы вокруг героя стали видимыми. Однако данная процедура несмотря на свою простоту станет одним из ключевых функциональных элементов в программе. Дело в том, что все игровые коллизии мы будем определять прежде всего в MoveHero - основной процедуре, ответственной за перемещение героя и взаимодействие с окружающим миром, в том числе и сражения с монстрами. Уже в таком виде программа будет работоспособна - герой будет перемещаться по карте при нажатии на клавиши-стрелки. Однако он пока будет некорректно выходить за пределы окна видимой части карты, так как пока не выполняется прокрутка этого окна при приближении героя к его границам. Поэтому этот аспект программы нам необходимо доработать в первую очередь.

Исходный код текущей версии: И его замечание:

PS: да, хочу дать небольшой совет. При размещении основных важных объектов(и особенно "порталов" для перехода на другие уровни) нужно ставить их так, чтобы герой смог до них добраться - иначе примерно в 20% случаев он оказывается "запертым" на уровне - проверено на собственном опыте. Я обхожу это так - создаю "карту доступа"(двумерный массив с такими же размерами, как и сама игровая карта; элементы массива имеют тип bool), который показывает доступность/недоступность ячеек игровой карты. И размещаю объекты (монстры, etc) в соответствии с этой информацией. Сама функция создания карты доступа занимает несколько строк - можете посмотреть в исходниках.

Да, конечно, проблема тупика время от времени возникает. Наш код - учебный, и что вы его развиваете - замечательно! А вот комментарий Дмитрия:

Процесс создания игры продолжается, герой уже создан, и отображается на экране. Возникли следующие предложения:

1) характеристику "сила зрения" (поле VisLong) включить в список характеристик героя массив Chars). В этом случае можно будет менять силу зрения героя как другие атрибуты, например, в случае, если его подвергли действию заклинания "Слепота" или он выпил бутылочку усилителя зрения или инфравидения.

2) при задании списка констант их молено описывать следующим образом: значение каждой следующей = значение предыдущей + 1. Т.е.:

 

statMin=1;

statSTR=statMin;

statDEX=statStr+1;

statCon=statDex+1;

statInt= statCon+1;

statSignStr= statInt+1;

statHealth=satSignStr+1

statMax = slatHeallh;

 

Плюсы - всегда известно минимальное и максимальное значение константы, легко Фильтровать последовательность значений, например, при добавлении после statCon новой константы statWill (воля), добавлется одна строка statWill = statCon+1, и меняется следующая строка statlnt = slatWill+1. Остальные константы меняют свое значение автоматически. Минусы - надо более тщательно следить за правильной последовательностью констант и приращений. Например, если забыть изменить в следущем примере значение statlnt - появятся две константы с разными именами и одинаковым значением.

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

 

With   FMain.mm_Stats.Lines   do 

begin

Clear;

for  i:=statMin  to  statMax do

Add(Format(    '    %10s   %d   '    , [StatNames[i],С Stats[i]]));

Этот цикл работает для любого количества характеристик героя (здесь я пользуюсь значением Stat вместо Chars). Метод вывода героя на экран помещен в LowLevel. В даном примере С - переменная героя (передается как параметр). Вывод осуществляется в компонент Memo (mm_Stats) главной формы (FMain)

Я отказался от конкретной записи для героя и обобщил на случай любого существа Preatufe, тo есть любой монстр характеризуется теми же характеристики, что и герой, но другими значениями этих характеристик!).

То есть описывается отдельный метод для вывода любого существа, и он используется как для вывода героя, так и для вывода монстра.

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

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

Категория: Уроки по созданию игр | Добавил: Armageddets (24.11.2012)
Просмотров: 1515 | Комментарии: 1 | Теги: программирование игр, как написать игру, урок по созданию игр, персонаж, Герой, ролевая игра | Рейтинг: 0.0/0
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Наш опрос
Оцените мой сайт
Всего ответов: 103
Мини-чат
Друзья сайта
  • Официальный блог
  • Сообщество uCoz
  • FAQ по системе
  • Инструкции для uCoz
  • Статистика

    Онлайн всего: 1
    Гостей: 1
    Пользователей: 0