Предисловие.
В этой статье вы научитесь добавлять русский шрифт в 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;
PAL_setColor(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;
PAL_setColor(16+1,RGB24_TO_VDPCOLOR(0xffffff));
Обьявили нужные переменные
PAL_setColor(16+1,RGB24_TO_VDPCOLOR(0xffffff));
И установили 2-ой цвет во 2-ой палитре (PAL_setColor). Подробнее про палитры в 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.
Далее, идут массивы смещений (строки).
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 проекта.
Заключение.
Если есть вопросы — задавайте, тема сложная, и я мог многое упустить. Если есть предложения по выпуску следующих статей, пишите, разберемся.