Тайловая графическая система /0x01

В этом цикле статей мы реализуем тайловую графическую систему.

Начнем с того, что такое тайл?

Тайлы — небольшие изображения одинаковых размеров, которые и служат фрагментами большой картины.

Что же нам нужно для реализации этой графической системы?

  • Набор тайлов
  • «Карта» (массив) в котором будут храниться номера тайлов
  • Программа, которая согласно «карте» будет выводить их на экран.
  • Если карта большая — реализация вертикального/горизонтальное скроллинга (прокрутки)

В статьях будут примеры кода на C++/Wiring C++. В коде не будут применяться классы и прочее из соображений скорости, также не будет использоваться никаких сторонних библиотек. Код может быть не оптимизирован, я хочу донести лишь только идею.

Набор тайлов

Итак, нужно определиться с размером самого тайла. Пусть это будет 16×16. Создаем набор тайлов. Формат: 16x(кол-во тайлов*16), 256цветов. Также можно(нужно) определить цвет прозрачности, если ваша система не поддерживает палитру цветов с альфа-каналом.

Здесь Альфа-канал — ядовито-розовый цвет или 100%Красного + 100%Синего. Набор тайлов лучше хранить одной «вертикальной» картинкой, тогда доступ к ним будет осуществляться быстрее. Количество тайлов — 4. У каждого тайла есть свой номер, если отсчет начать с 1, то у последнего тайла будет номер 4. Чтобы добраться до последнего тайла — нам нужно пропустить (номер-1)*16 пикселей. Все элементарно!

Пример кода на C++:

1
2
3
4
5
6
7
const uint8_t TexturePack[] = {
 
0x00, 0xE3, 0x56, 0xC6, 0x7D,
 
....
 
};

Массив элементов (Карта)

Теперь представим, что нам нужно сделать большую «картину» или игровую карту из этих тайлов. Что же мы имеем? — Набор тайлов и их номера. Теперь нужно создать двумерный массив, каждая ячейка которого будет хранить в себе номер нужно тайла.

К примеру, у нас в распоряжении экран/видеопамять 256×208 (не говорите, что это очень мало :)). 1 тайл — 16х16. Соответственно,  массив должен быть размером 16×13.

Давайте сопоставим значения с номерами тайлов из набора.

Напишем кусочек кода для этой матрицы на C++:

1
2
3
struct Map{
    unsigned char tile1[13][16];
};

А если мы хотим больше, чем один слой? Сделать задный план, передний план… Чтобы наши тайлы были могли накладываться друг на друга. Добавим соответствующие правки.

1
2
3
4
5
struct Map{
    unsigned char tile1[13][16];
    unsigned char tile2[13][16];
    unsigned char tile3[13][16];
};

Теперь у нас аж 3 массива.

Вывод тайла

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

Для начала разберемся, как вывести вообще что-нибудь. Вывод будет происходить напрямую в видео-память. Ее мы представим в виде массива (одномерного): 256×208 (8bit). 1 пиксел — 1байт, тк 256 цветов. :-> VGA.buf — вот наш видео-буфер. (Не обращайте внимание на  класс VGA) Обратиться к пикселю по x,y можно так: VGA.buf[y*256+x]. Создадим простую подпрограмму:

1
2
3
4
5
6
7
8
9
void drawBitmap(unsigned char num, const uint8_t *tex, int16_t x, int16_t y) {
   const uint8_t p = tex+(num*16*16);
   for (uint8_t y=0; y<16; ++y) {
      for (uint8_t x=0; x<16; ++x) {
         VGA.buf[y*256+x] = *p;
         ++p;
      }
   }
}

Эта функция выведет указанный [номером-1] тайл из набора тайлов на координаты x/y в видео-память. Но если учитывать, что тайл может не поместиться в видео-памяти: один его кусочек будет заходить за грацицы. Нужна другая функция, чтобы выводить тайл с учитыванием этих нюансов. Один хороший человек, Кузин Андрей, уже проработал этот вопрос и подарил нам замечательную функцию, выводящую изображение, которую я немного переделал под тайловую систему.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void drawBitmap(unsigned char tex, const uint8_t* b, int16_t posx, int16_t posy) {
   uint16_t texx = (tex*16*16);
   int16_t lsx;
   if ((posx>256) || (posy>208) || (posx+16<=0) || (posy+16<=0)) return;
   int16_t x1,y1,x2,y2;
   x1=y1=0;
   x2=16;
   y2=16;
   if (posx<0) x1=-posx;
   if (posy<0) y1=-posy;
   if (posx+16>256) x2=256-posx;
   if (posy+16>208) y2=208-posy;
   lsx=x2-x1;
   const uint8_t *p1=b+texx+y1*16+x1;
   int16_t i;
   long Addr=(posy+y1)*256+posx+x1;
 
   for (i=y1; i<y2; ++i) {
      MoveLineOfImage(p1,VGA.buf+Addr,lsx);
      p1+=16;
      Addr+=VGA_W;
   }
}
 
inline void MoveLineOfImage(const uint8_t *Source, uint8_t *Dest, uint8_t Count) {
   for (uint8_t i=0; i<Count; ++i){
      if ((*Source)!=0xE3) {
         *Dest=*Source;
      }
      ++Source;
      ++Dest;
   };
}

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


Все части статьи.

Вы можите оставить комментарий, или поставить трэкбек со своего сайта.

Написать комментарий

XHTML: Вы можете использовать эти теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Bug Report
Локализовано: шаблоны Wordpress