Изометрия

Советы » Создание игр » Изометрия

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

На рисунке показан вид сбоку, где N - нормаль к поверхности пространства a - угол обзора камеры.

Для начала стоит упомянуть где использовался этот способ представления : Diablo, Fallout, Gorky 17 ( хотя несколько видоизменено ). С примерами игр, я конечно могу и ошибаться.

Начнем с разработки спрайтов. В отличии от простого 2D представления, они имеют более сложную форму :

Размер выбирайте : ... 64x32, 60x30 ... 2x1. / В моем примере 60x30 /. Советую сделать спрайтов поверхности штук 30-40 на каждый тип ландшафта.

Наступило время все эти спрайты разместить. Для начала определимся, что карта хранится в массиве, а если точнее то в массиве хранятся индексы этих спрайтов. Сами спрайты будем хранить в TImageList. При написании, я использовал динамический массив составленный из списка. В примере это класс PTable и его описание находится в файле Table.pas. Там все просто:

procedure

Create(const

X, Y: integer); procedure

Put(X, Y, Number: integer); // поместить в ячейку X,Y элемент Number function

Get(X, Y: integer): integer; // получить

Первой процедурой создаем таблицу с размерами X,Y. На самом деле можно создавать только квадратные т.е. 2x2, 5x5, 100x100 ... NxN. Если хотите сделать произвольного размера то надо вводить 2 списка, но мне кажется, что и квадратной формой можно обойтись. А далее, после создания, работаем как с обычным массивом.

C Table все, хотя я может кого то немножко и озадачил, но использовать массив ( Array ) для задания карт в играх не рекомендую. Причина простая. Допустим, уровни имеют разный размер (в Heroes от 64x63 - 256x256), а с массивом Вы сможете сделать только фиксированный - т.к. размер уровня узнается уже после задания массива. Вторая причина более важна : немножко изменив класс PTable, в нем можно хранить не только индексы спрайтов. Например в каждой ячейке сохраняется информация о :

  • ресурсах которые там находятся - количество леса, камня, руды ...
  • степень проходимости для юнитов - по болоту медленнее, чем по дороге.
  • высоту над уровнем моря :) и многое другое

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

Я решил эту проблему просто. Сначала проходим цикл для четных, потом для нечетных :

procedure

TIFlur.Draw(DestCanvas: TCanvas; SourceRect: TRect); var

I, J, dX, dY: integer; begin

// рисуем четные столбцы 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

if

Odd(I) = False then

Resource.Draw(DestCanvas, dX, dY, Map.Get(I, J)); Inc(dY, 30); // !!!!!!!! SpriteHeigth !!!!!!! end

; dy := 0; Inc(dX, 30); // !!!!!! SpriteWidth div 2 !!!!!!! end

; // рисуем нечетные столбцы dx := 0; dy := 15; // !!!!!! SpriteHeigth div 2 !!!!!! for

I := SourceRect.Left to

SourceRect.Left + SourceRect.Right do

begin

for

J := SourceRect.Top to

SourceRect.Top + SourceRect.Bottom do

begin

if

Odd(I) = True then

Resource.Draw(DestCanvas, dX, dY, Map.Get(I, J)); Inc(dY, 30); // !!!!!!!! SpriteHeigth !!!!!!! end

; dY := 15; // !!!!!! SpriteHeigth div 2 !!!!!! Inc(dX, 30); // !!!!!! SpriteWidth div 2 !!!!!!! end

; end

;

Для наглядности приведу 2 картинки :

При наложении образуется уже нормальная картинка. Для удобства я написал класс TIFlur смотрите файл IMap.pas. Все практически тоже, что и у TFlur :

type

TIFlur = class

private

Resource: TImageList; // Ресурсы графики public

Width, Heigth: Integer; // Ширина и высота карты Map: PTable; // Массив карты развернутый в список constructor

Create(const

X, Y: integer); // ширина, высота карты procedure

LoadResource(FileName: string

); // путь к BMP ресурсу procedure

RandomGenerator; // случайное заполнение Table procedure

Draw(DestCanvas: TCanvas; SourceRect: TRect); // Canvas для вывода, // SourceRect - какой кусок карты Table выводить на экран, В КОЛ_ВАХ СПРАЙТОВ destructor

Destroy; override

; end

;

Так, только необходимые процедуры, ничего лишнего.

Пожалуй, самое сложное это определить координаты клетки где находится мышь. Ведь координаты курсора у нас X и Y, а в карте все ячейки распологаются со сдвигами. Но тут нас выручат маски. Для начала создадим такую маску :

Получим координаты мыши и целочисленным делением получим координаты клетки где находится курсор ( так мы поступали при простом виде сверху ) :

 

I := (X div

SpriteWidth) * 2; // для четных I := (X div

SpriteWidth) * 2 - 1; // для нечетных // а Y везде одинаковый: J := Y div

SpriteHeigth;

На рисунке эти координаты 2.0

По такой не слабой формуле мы вычисляем, куда конкретно попал курсор в пределах одной маски :

dX := SpriteWidth * (Frac(X / SpriteWidth));
dY := SpriteHeigth * (Frac(Y / SpriteHeigth));

Это остато от деления умножается на ширину или высоту маски. Соответственно эти переменные ВСЕГДА лежат в пределах dX (0-60) и dY (0-30). С помощью этих координат мы можем определить цвет куда тыркнулась мышка и по цвету задать смещение. Приведу целиком тело этой процедуры.

procedure

TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); var

dX, dY: Real; I, J: integer; begin

case

Odd(Screen.Left) of

False: begin

I := (X div

SpriteWidth) * 2; end

; True: begin

X := X + 30; I := (X div

SpriteWidth) * 2 - 1; end

; end

; J := Y div

SpriteHeigth; CursorX := I; CursorY := J; dX := SpriteWidth * (Frac(X / SpriteWidth)); dY := SpriteHeigth * (Frac(Y / SpriteHeigth)); case

Mask.Canvas.Pixels[Trunc(dx), Trunc(dy)] of

clRed: begin

// Красный CursorX := I - 1; CursorY := J - 1; end

; clBlue: begin

// Синий CursorX := I + 1; CursorY := J - 1; end

; clLime: begin

// зеленый CursorX := I - 1; end

; clYellow: begin

// Желтый CursorX := I + 1; end

; end

;

Теперь при перемещении мыши по экрану, мы сразу можем определить реальные координаты карты. Они на рисунке обозначены черным цветом 3.0 Для компиляции исходника потребуется компонент THeadedTimer. Там же Вы узнаете как избежать "дрожания" курсора.

Теперь пару слов о способах задания объектов для карты. Каждый движущийся объект будет иметь по 2 переменные на каждую координату. Допустим первые X и Y координаты в системе карты. А вторые 2 dX и dY пиксельные координаты. Т.е. персонаж ходит по координатам dX и dY, а взаимодействует с картой по координатам X и Y.

Хотя все это условно и все зависит от Вас.В скором времени я добавлю анимированный спрайт на карту и пару объектов. Все качаем исходник тут.

Другое по теме:

Категории

Статьи

Советы

Copyright © 2022 - All Rights Reserved - www.delphirus.com