SGDK. Используем русский шрифт.

Предисловие.

В этой статье вы научитесь добавлять русский шрифт в SGDK. Сразу скажу, способ костыльный, как и массив коллизий из SGDK. Создание платформера для Sega Genesis. однако он простой, и главное рабочий! Первым делом, покопаемся в функции VDP_drawText из первого урока по SGDK.

Скачайте готовый проект с MEGA.

Немного про символы.

Символ (char), как и любой другой тип это набор битов, т.е. число. Так вот, к символу можно прибавить число, и получить другой символ. Например, если к ‘c‘ прибавить 1, получится ‘d‘. Т.е. с символом можно проводить те же операции, что и с числами.

Копаемся в библиотеках SGDK.

Откройте C:\SGDK\src\vdp_bg.c и найдите там VDP_drawText

void VDP_drawText(const char *str, u16 x, u16 y)
{
    VDP_drawTextBG(text_plan, str, x, y);
}

Как видно, VDP_drawText это оболочка для VDP_drawTextBG, переходим туда.

void VDP_drawTextBG(VDPPlane plane, const char *str, u16 x, u16 y)
{
    u16 data[128];
    const u8 *s;
    u16 *d;
    u16 i, pw, ph, len;

    // get the horizontal plane size (in cell)
    pw = (plane == WINDOW)?windowWidth:planeWidth;
    ph = (plane == WINDOW)?32:planeHeight;

    // string outside plane --> exit
    if ((x >= pw) || (y >= ph))
        return;

    // get string len
    len = strlen(str);

    // if string don't fit in plane, we cut it
    if (len > (pw - x))
        len = pw - x;

    s = (const u8*) str;
    d = data;
    i = len;
    while(i--)
        *d++ = TILE_FONTINDEX + (*s++ - 32);

    VDP_setTileMapDataRowEx(plane, data, text_basetile, y, x, len, CPU);
}

Здесь, мы видим, что функция, просто рисует тайлы на плейне (слое) с помощью функции VDP_setTileMapDataRowEx. Данный код я разбирать не буду, т.к. написал свой, более простой. Писал его на основе этой функции.

Разбираем ресурсы.

В папке res, содержится шрифт, фоновая картинка и resources.res

В resources.res шрифт загружается в качестве тайлсета

TILESET font_rus "font.png" BEST NONE

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

: и ;

Или же:

0 и О

Тогда, если включена оптимизация, SGDK удалит повторяющиеся тайлы из тайлсета. В итоге, тайлы сместятся и порядок нарушится. У меня самого были с этим проблемы, сколько же свободного времени это убило, которое уже не вернуть.

В общем, данный tileset, потом загоним в VRAM, из которого будем брать тайлы наших символов, и рисовать на плейне.

Изучаем код.

Найдите функцию VDP_drawTextOffset в main.c

void VDP_drawTextOffset(VDPPlane plane, const u16 *vram_offsets, u16 len, u16 first_tile, u16 x, u16 y)
{
    u16 i, pw, ph, curX;

    // get the horizontal plane size (in cell)
    pw = (plane == WINDOW)?windowWidth:planeWidth;
    ph = (plane == WINDOW)?32:planeHeight;

    // string outside plane --> exit
    if ((x >= pw) || (y >= ph))
        return;

    //len = array_u16len(vram_offsets); иногда работает иногда нет, забил на эту функцию теперь ввожу длину ручками.

    // if string don't fit in plane, we cut it
    if (len > (pw - x))
        len = pw - x;
    i = 0;
    curX = 1;
    u16 curTileInd = 0;
    VDP_setPaletteColor(16+1,RGB24_TO_VDPCOLOR(0xffffff));
    while(i < len) {
        curTileInd = first_tile-1 + vram_offsets[i]; 
	//KLog_S1("curTile: ", curTileInd);
		
	VDP_setTileMapXY(plane, TILE_ATTR_FULL(PAL1,0,FALSE,FALSE,curTileInd), curX, y);
	i++;
	curX++;
    }
}

Данная функция вместо строки, принимает массив значений

const u16 *vram_offsets

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

  • len — длина строки.
  • first_tile — индекс тайла, в который загружен тайлсет нашего шрифта. Значения, внутри массива vram_offsets, задаются относительно first_tile.

Функция на 60% копипаст VDP_drawTextBG, часть кода до:

i = 0;

Просто, ограничивает текст в рамках экрана.

i = 0;
curX = 1;
u16 curTileInd = 0;
VDP_setPaletteColor(16+1,RGB24_TO_VDPCOLOR(0xffffff));

Обьявили нужные переменные

VDP_setPaletteColor(16+1,RGB24_TO_VDPCOLOR(0xffffff));

И установили 2-ой цвет во 2-ой палитре (VDP_setPaletteColor). Подробнее про палитры в SGDK. Переходы Fade-in и Fade-out.

2-ой цвет, во 2-ой палитре, это цвет нашего текста.

Указываем индекс тайла, который хотим нарисовать.

curTileInd = first_tile-1 + vram_offsets[i]; 

Наверное, здесь нужно пояснение по поводу -1. Ведь, по идее, формула должна выглядеть так.

Идекс_тайла = первый_тайл + смещение_символа

Число -1, это такой способ пофиксить баг. Поскольку, если vram_offsets[i] == 0, то цикл останавливается и строка обрезается. В любом случае, в vram_offsets смещения, и так, завышены на 1, поэтому все компенсируется.

Далее, задаются тайлы на экране

VDP_setTileMapXY(plane, TILE_ATTR_FULL(PAL1,0,FALSE,FALSE,curTileInd), curX, y);

По следующему синтаксису

VDP_setTileMapXY(плейн, тайл, x, y);
  • плейн — слой, на котором будет расположен тайл (BG_A или BG_B).
  • тайл — информация о тайле, задается через макрос TILE_ATTR_FULL, подробнее здесь.
  • x, y — координаты тайла.

Рисуются тайлы (символы), друг за другом (curX++).

VDP_setTileMapXY(plane, TILE_ATTR_FULL(PAL1,0,FALSE,FALSE,curTileInd), curX, y);
i++;
curX++;

Теперь, заглянем в функцию main

VDP_drawImage(BG_A, &img, 0, 0);
u16 ind = 1300;
VDP_loadTileSet(&font_rus, ind, DMA);

Здесь, сделали следующее:

  • Нарисовали фоновое изображение (кружка на черном фоне)
  • Установили индекс начального тайла (тайла, начиная с которого начинается наш шрифт)
  • Загрузили тайлсет в VRAM.
Первый символ шрифта, это пробел. Шрифт можно посмотреть в папке res.

Далее, идут массивы смещений (строки).

u16 textArr[15] = {49, 86, 79, 76, 88, 72, 74, 83, 103, 102, 1, 51, 37, 38, 44};
u16 textArr2[28] = {52, 77, 87, 77, 88, 100, 13, 1, 90, 99, 1, 87, 86, 85, 80, 84, 72, 77, 96, 100, 1, 88, 91, 89, 89, 82, 80, 81};

И вывод их, на экран.

VDP_drawTextOffset(BG_A, textArr, 15, ind, 1, 1);
VDP_drawTextOffset(BG_A, textArr2, 28, ind, 1, 2);

В итоге, получилось следующее.

А теперь я расскажу, как сделать массив смещений (строку).

Как предложил Битард Битардович, вы также можете посчитать количество символов, программно, упростив себе работу.

VDP_drawTextOffset(BG_A, textArr, sizeof(textArr)/sizeof(*textArr), ind, 1, 23);

Создаем массив смещений (строку).

Сначала, скачайте Python скрипт с MEGA.

Затем, создайте текстовый файл, и напишите туда что-нибудь (на русском языке).

Файл должен быть сохранен в кодировке Windows-1251, стандартное приложение блокнот, сохраняет именно в этой кодировке.

Данный файл, перенесите на text_to_arr.exe

Теперь, в текстовом файле, появился массив смещений и можно заменить текст на экране.

int main()
{
    VDP_drawImage(BG_A, &img, 0, 0);
	u16 ind = 1300;
	
    VDP_loadTileSet(&font_rus, ind, DMA);
	u16 textArr[15] = {49, 86, 79, 76, 88, 72, 74, 83, 103, 102, 1, 51, 37, 38, 44};
	u16 textArr2[28] = {52, 77, 87, 77, 88, 100, 13, 1, 90, 99, 1, 87, 86, 85, 80, 84, 72, 77, 96, 100, 1, 88, 91, 89, 89, 82, 80, 81};
	// СГДК, выпей кофе.
	u16 textArr3[17] = {51, 37, 38, 44, 13, 1, 74, 99, 87, 77, 81, 1, 82, 86, 92, 77, 15};
	
	VDP_drawTextOffset(BG_A, textArr, 15, ind, 1, 1);
	VDP_drawTextOffset(BG_A, textArr2, 28, ind, 1, 2);
	VDP_drawTextOffset(BG_A, textArr3, 17, ind, 1, 3);
    while(1)
    {
		
		//drawCharCode('а');
        SYS_doVBlankProcess();
    }
    return (0);
}

VDP_drawTextOffset я перенес из главного цикла (while), что-бы не нагружать систему.

Данный шрифт, немного артифачит, потому-что *барабанная дробь* он сгенерирован автоматически из ttf шрифта.

Переносим TTF шрифт в SGDK.

Скрипт номер 2, который я создал на Python, что-бы меньше работать, лежит на MEGA.

Теперь перенесите ttf шрифт на иконку ttf2Img4_SGDK.exe

Шрифт создан.

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

И перенести font.png в папку res проекта.

Заключение.

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

Итоговый результат.