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

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


Школа программирования

53) Рисуем героев и монстров

Мы научились рисовать карту, и теперь аналогичным образом запрограммируем отображение персонажа, монстров и предметов. Нам потребуются две переменные для образов "текущего" героя, которым управляем, и героя/ев, не выбранного на данный момент (в DOS-игре они разделялись цветами, белым и желтым). Подготовим эти два изображения 32*32 пикселя в файлах h1.bmp и h2.bmp - с черным фоном, который будет считаться "прозрачным". Их определения будут храниться в модуле главной формы:

     ...
     TileBits: array[0..tileLast] of TBitmap;
      bmpMainHero, bmpNoMainHero: TBitmap;

    end;

Загрузку выполним в момент создания формы, следом за загрузкой тайлов местности:

   bmpMainHero := TBitmap.Create;
   bmpMainHero.LoadFromFile( ' h1.bmp ' );
   bmpNoMainHero := TBitmap.Create;
   bmpNoMainHero.LoadFromFile( ' h2.bmp ' );

А процедура отображения героя будет выглядеть похоже на DOS-версиею (в модуле LowLevel):

  { ----------------- }
  procedure ShowHero(HeroNum: Integer);
  var x,y: Integer;
  begin
  x :=
  WINDOW_LEFT+Heroes[HeroNum].x-GameMap[CurMap].LocalMapLeft;
  y :=
  WINDOW_TOP+Heroes[HeroNum].y-GameMap[CurMap].LocalMapTop;
  x := (x-1)*MAP_CELL_WIDTH;
  y := (y-1)*MAP_CELL_HEIGHT;

  if Heroes[HeroNum].HP > 0 then
   if HeroNum = CurHero
     then GameForm.PaintBox.Canvas.Draw(x,y, GameForm.bmpMainHero )
     else GameForm.PaintBox.Canvas.Draw(x,y, GameForm.bmpNoMainHero );
  end;

Тут мы ввели две дополнительные константы

    {$IFDEF WIN_GAME}
    ...
    const MAP_CELL_WIDTH = 32;
               MAP_CELL_HEIGHT = 32;

Они задают размер одной клетки в пикселах. Задействуем их также и в процедуре ShowCell:

  { ----------------- }
  procedure ShowCell(t: TMapCell; x,y: Integer);
  begin

  x := WINDOW_LEFT+x-GameMap[CurMap].LocalMapLeft;
  y := WINDOW_TOP+y-GameMap[CurMap].LocalMapTop;
  x := (x-1)*MAP_CELL_WIDTH;
  y := (y-1)*MAP_CELL_HEIGHT;

  if t.IsVisible
     then GameForm.PaintBox.Canvas.Draw(x,y, GameForm.TileBits[t.Tile] )
     else GameForm.PaintBox.Canvas.Draw(x,y, GameForm.TileBits[0] );

  end;

Теперь на карте уже будет виден герой:)

Для монстров и предметов требуется по восемь изображений (рисуем восемь разных монстриков и восемь разных предметов-видов оружия, брони, ...), введем массивы для их хранения в модуле главной формы:

  public
      { public declarations }

      RealTimeFlag: Boolean;
      IsGame: Boolean;

      TileBits: array[0..tileLast] of TBitmap;
      bmpMainHero, bmpNoMainHero: TBitmap;

      bmpMonsterRecords: array[1..MaxMonsterTypes] of TBitmap;
      bmpItemRecords: array[TGameItemType] of TBitmap;

    end;

А вот код их загрузки, там же размещаем, где и код загрузки тайлов. Монстры хранятся в файлах m1 - m8.bmp, предметы - i1 - i8.bmp.

  for i := 1 to MaxMonsterTypes do
    begin
    bmpMonsterRecords[i] := TBitmap.Create;
    bmpMonsterRecords[i].LoadFromFile( ' m ' +IntToStr(i) + ' .bmp ' );
    end;

   gi:= itemHandWeapon;
   for i := 1 to 8 do
     begin
     bmpItemRecords[gi]:=TBitmap.Create;
     bmpItemRecords[gi].LoadFromFile( ' i ' +IntToStr(i) + ' .bmp ' );
     if i < 8 then
        gi := Succ(gi);
    end;

Отображение монстров и предметов также создадим по аналогии:

  { ----------------- }
  procedure ShowMonster(var mns: TMonster);
  var x,y: Integer;
  begin
  x := WINDOW_LEFT+mns.x-GameMap[CurMap].LocalMapLeft;
  y := WINDOW_TOP+mns.y-GameMap[CurMap].LocalMapTop;
  x := (x-1)*MAP_CELL_WIDTH;
  y := (y-1)*MAP_CELL_HEIGHT;
  GameForm.PaintBox.Canvas.Draw(x,y,
  GameForm.bmpMonsterRecords[mns.ID] );
  end;

  { ----------------- }
  procedure ShowItem(itm: TGameItem);
  var x,y: Integer;
  begin
  x := WINDOW_LEFT+itm.x-GameMap[CurMap].LocalMapLeft;
  y := WINDOW_TOP+itm.y-GameMap[CurMap].LocalMapTop;
  x := (x-1)*MAP_CELL_WIDTH;
  y := (y-1)*MAP_CELL_HEIGHT;
  GameForm.PaintBox.Canvas.Draw(x,y,
  GameForm.bmpItemRecords[itm.IType] );
  end;

Основная часть, ответственная за вывод карты на экран, завершена!

Напомним, что все действия по этому выводу выполняются в процедуре ShowGame. Windows устроена таким образом, что при необходимости перерисовки экрана (например, после свертывания и последующего развертывания окна) ему посылается сообщение, которое программист должен обрабатывать. В нашем случае это будет событие OnPaint компонента PaintBox, в обработчике которого надо указать вызов основной процедуры отображения:

  // -----
  procedure TGameForm.PaintBoxPaint(Sender: TObject);
  begin
   if not IsGame then Exit;
   ShowGame;
  end; //

Здесь мы вставили проверку переменной IsGame, которую создали специально для данного случая - ведь вызывать ShowGame можно только тогда, когда выполнена процедура начала игры, а это может произойти отнюдь не сразу после открытия формы.

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

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


(c) 2004-2007 Сергей Бобровский : bo собака russianenterprisesolutions.com

Школа программирования с нуля
Все предыдущие выпуски базового курса всегда тут:
http://www.infiltration.ru/p/

Неофициальный сайт поддержки (со срочными вопросами - сюда):
www.prog-begin.net.ru.


Мои книги (учебные курсы) "Технологии Delphi / C++ / C#".
http://shop.piter.com/publish/authors/17681/191180213/
Дизайн рассылки: Алексей Голубев - Web-дизайн и web-программирование


В избранное