CPU3D.comКомпьютерная графикаДвухмерная графика → Создание карты в игре, методом спрайтов

Создание карты в игре, методом спрайтов

Часть 1

В играх, где используется простая 2D проекция, используется наверное два способа построения игрового пространства (карт). Первый о котором и будет рассказ, заключается в построение карты из спрайтов одинакового размера. А второй из уже готовых больших изображений, но об этом в другой главе.

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

Этот способ построения карт применялся в таких БОЕВИКАХ ( не побоюсь этого слова ) игрового фронта как : WARCRAFT, DUNE и т.д. С помощью этого типа карт можно написать неплохую RPG или стратегия. Ну приступим. Первое что нам нужно это спрайты:

В примере я сделал файл Flur1.bmp там 20 спрайтов размером 32 x 32 / при создании старайтесь делать спрайты квадратные - упрощается вычисление/. Спрайты индексируются 0 ... 19;
Что представляет из себя карта - это двух мерный массив , элементы которого - индексы спрайтов.
Например: MAP: Array [0..100, 0..100] of integer;

Это означает, что мы хоти создать карту размером 100 x 100 спрайтов, допустим размер спрайта 32 x 32 тогда общая длина/ширина карты в пикселях будет 3200 x 3200. Но что вам мешает создавать карты, 1000 x 1000 клеток. ( Пример в HMM2 максимальная карта 128 x 128 при размере спрайта 32 пикселя )
Я советую создать класс для карты, вот как сделал бы я :type TFlur= class
private
SpriteLastIndex, // Кол-во спрайтов в Resource
SpriteWH:integer;// Размер спрайта, должен быть квадрат
Resource:TBitmap;// Ресурсы графики
public
Table:Tlist; // Массив карты развернутый в список
Width, Heigth:Integer;// Ширина и высота карты в спрайтах
Constructor Create(Const X, Y, Sprite:integer);// ширина, высота, размер спрайта
Procedure LoadResource(FileName:String); // путь к BMP ресурсу
Procedure RandomGenerator; // случайное заполнение Table
Procedure Draw(DestHandle:TCanvas;X, Y:Integer;SourceRect:TRect);
// Canvas для вывода,
// Смещение X и Y относительно начала DestHandle
// SourceRect - какой кусок карты Table выводить на экран, В КОЛ-ВАХ СПРАЙТОВ
Destructor Destroy; override;
end;

По поводу Table - я использовал TList вместо Array т.к. при создании мы можем не знаем, какого размера карту будем делать. В игре создавая один TFlur можно подгружать туда новые карты динамически, при переходе на новый уровень ( зону, город ... ), без замедления.
Рассмотрим процедуру Draw, т.к. все остальное достаточно понятно :Procedure TFlur.Draw(DestHandle:TCanvas;X, Y:Integer;SourceRect:TRect);
var
I, J:integer;
dx, dy:integer; // для смещений в пикселях
TableIndex:^integer; // указатель на элемент Table
begin
// Делаем проверку
if SourceRect.Left<0 then exit;
if SourceRect.Top<0 then exit;
if SourceRect.Left+SourceRect.Right>Heigth-1 then exit;
if SourceRect.Top+SourceRect.Bottom>Width-1 then exit;
dx:=0;dy:=0;// обнуляем смещение
// зададим цикл по области карты для показа
for I:=SourceRect.Left to SourceRect.Left+SourceRect.Right do
begin
for J:=SourceRect.Top to SourceRect.Top+SourceRect.Bottom do
begin
TableIndex:=(Table.Items[Width*J+I]);
// Уже знакомой прцедурой копируем один спрайт на экран
BitBlt(DestHandle.Handle, dx+X, dy+Y, SpriteWH,
SpriteWh, Resource.Canvas.Handle, TableIndex^*SpriteWh, 0, SRCCopy); inc(dy, SpriteWH);// увеличиваем смещение dy на SpriteWH
end;
Inc(dx, SpriteWH);dy:=0;// увеличиваем смещение dx на SpriteWH, обнуляем dy
end;
end;

Достанем элемент из списка с координатами I, J

Так, в принципе класс написан, это даже не класс а некое подобие ENGINE :) Что можно добавить, изменить и т.д. Все зависит от конкретной задачи:

1. Если вы хотите сделать что-то подобное Color Lines, шахмат, морского боя и т.д. вам совсем не обязательно вводить Table - поле всегда имеет одинаковые размеры.

2. При выполнении Flur.Draw неплохо ввести проверку на значения SourceRect ( я сделал ее внутри процедуры обработки клавиатуры).

3. Все объекты карты - дома, леса, юниты, технику и д.р. желательно привязывать к координатам пиксельным ( да и вообще скроллинг карты надо делать то же пиксельным - но об зтом позже ).

4. Дописать загрузку Table из файла.

5. При выводе графики на в окно происходит мерцание курсора. Это связано с тем, что обновление курсора намного медленнее, чем вывод графики. Исправить это можно следующим способом. С формы убирается курсор ( Form1.Cursor:=crNone; ). Координаты курсора запоминаем в двух переменных. При от рисовки делаем так : создаем буфер, рисуем Flur в буфер, далее туда пихаем картинку из ImageList'а. И выводим буфер на экран.Плюсы этого метода: можно сделать курсор любой формы и менять его по ходу игры, ну и избавимся от дрожания.

Вроде все, исходник тут.

Хранить спрайты можно не только в BMP, но и в TImageList. При этом появляется множество удобств:

1. Можно спрайты не вытягивать в линейку, а рисовать на прямоугольнике - допустим 640x480. При загрузке достаточно указать размер спрайта, а ImageList сам их разобьет.

2. Удобно добиваться прозрачности. Это может понадобится для спрайтов не квадратной формы - ромб. Они пригодятся для изометрии.

3. Скорость вывода на экран потрясающая. Смотрите пример.

Итак, что я переписал в TFlur : коструктор и Draw

constructor TFlur.Create(ConstMapX, MapY, SpriteW, SpriteH:integer;ResFileName:String);
// MapX, MapY - размер карты в спрайтах
// SpriteW, SpriteH - размер спрайта
// ResFileNama - путь к BMP
var
Resource:Tbitmap; // BMP с ресурсами
begin
Resource:=Tbitmap.Create; // Грузим ресурсы из ResFileName
Resource.LoadFromFile(ResFileName);
ImageList:=TImageList.CreateSize(SpriteW, SpriteH); // Создаем лист под спрайты
ImageList.DrawingStyle :=dsNormal; // Устанавливаем параметры
ImageList.Masked := False;
ImageList.Add(Resource, Resource);
....
end;

И изменения в TFlur.Draw : ....
for J:=SourceRect.Top to SourceRect.Top+SourceRect.Bottom do
begin
TableIndex:=(Table.Items[Width*J+I]);
ImageList.Draw(DestCanvas, dx+X, dy+Y, TableIndex^);
Inc(dy, 32);
end;
Inc(dx, 32);
....

При компиляции использовал компонент THeadedTimer ( инсталируйте его из директории THeadedTimer, как установить компоненты Вы надеюсь знаете ). Этот компонент аналог стандартного TTimer, но использованы потоки - высокая скорость работы. И последнее - если вы будете совместно использовать Flur и спрайты то копируйте все в БУФЕР. Т.е. карту в буфер, спрайты и курсор в буфер, буфер на экран. Тут исходник.

Если есть у кого ссылки на ресурсы с графикой от игр ( лучше стратегий и RPG ) или вообще текстуры, но текстуры к ИГРАМ, а не web ( формат BMP, DIB, PSD) то прошу присылать ссылки. Кто напишет что что-нибудь дельное используя эти методы обязательно размещу. Если ВЫ знаете другие методы построения карт в играх прошу присылать идеи.
Часть 2

Прежде чем читать данную статью, настоятельно рекомендую прочесть ее первую часть.

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

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

Для достижения плавного скроллинга карты, анимации объектов приходится вводить координаты пиксельные.



Источник: http://www.delphigfx.narod.ru