Предисловие.
В этой статье вы узнаете как анимировать тайлы на слоях (BG_A, BG_B), что позволит вам, создавать анимированные фоны. Но сперва нужно определится где в VRAM, находятся тайлы которые будем анимировать.

Скачайте SGDK проект с github.
Как будем анимировать.
Слои BG_A, BG_B, используют тайлы из VRAM, все что нам нужно это заменить эти тайлы.
Как SGDK строит тайлсет вашей карты.
При создании ресурса TILESET, SGDK проходится по всем тайлам карты слева-направо, сверху-вниз, т.е. сначала проходит по первой строке тайлов, затем по строке ниже и так далее. Что дает нам возможность забить порядок тайлов тайлсета в первую строку тайлов карты. Так можно сделать, но не желательно, ведь есть способ получше. А именно, создание своего тайлсета карты.
Создание собственного тайлсета карты.
Создайте карту вашего уровня в Aseprite, или другом редакторе.
Определитесь, какую часть карты вы будете анимировать (в тайлах). В моем случае, я выбрал часы.

Карту в .res файле, импортируете таким образом:
PALETTE pal_img "image.png"
TILESET tileset_img "image.png" BEST ALL
MAP map_img "image.png" tileset_img BEST 0
Данные настройки максимально компактно уместят тайлы карты.
Теперь, создайте проект под свою карту, пример проекта можете взять в этой статье. И запустите его в эмуляторе Gens KMod.
Выберите CPU->Debug->Genesis->VDP, и нажмите Tiles to Bitmap, чтобы сохранить тайлсет.

Откройте этот тайлсет в Aseprite, и выделением, перекопируйте тайлсет на картинку карты, чтобы палитра тайлсета подстроилась под палитру карты. Данный прием, я объяснял в этой статье.
И меняйте порядок тайлов так, чтобы тайлы которые вы хотите анимировать шли слево-направо и сверху вниз. У меня получилось следующее.

И привязываем этот тайлсет (map_tileset.png) к карте:
PALETTE pal_img "image.png"
TILESET tileset_img "map_tileset.png" NONE NONE
MAP map_img "image.png" tileset_img BEST 0
Хотя, в данном случае редактирование тайлсета не потребовалось, у вас может случится что SGDK, оптимизирует тайлсет там где не надо, т.е. в области анимации. В этом случае, используйте данный метод.
Создание анимации.
Анимацию делаем отдельными кадрами, размер кадра, в моем случае 32×32 px (размер часов). Кадры должны быть в той же палитре, что и карта уровня.

В .res файл импортируем эти картинки как тайлсет, таким образом:
TILESET tileset_clock_anim1 "clockAni1.png" NONE NONE
TILESET tileset_clock_anim2 "clockAni2.png" NONE NONE
TILESET tileset_clock_anim3 "clockAni3.png" NONE NONE
TILESET tileset_clock_anim4 "clockAni4.png" NONE NONE
Т.е. импортируем как есть, без удаления дубликатов.
Подготовка завершена, теперь разберем код.
Разбор кода.
Разберем функцию main
int main()
{
u16 vramIndex = TILE_USER_INDEX;
PAL_setPalette(PAL0, pal_img.data, CPU);
VDP_loadTileSet(&tileset_img, vramIndex, DMA); //Импортируем тайлсет карты
bga = MAP_create(&map_img, BG_A, TILE_ATTR_FULL(0, FALSE, FALSE, FALSE, vramIndex)); //Создаем карту
vramIndex += tileset_img.numTile;
MAP_scrollTo(bga, 0, 0); //Обновляем карту, чтобы она была нарисована с первого кадра
s16 timer = 120; //таймер, задающий скорость анимации
s16 cur_frame = 0; //текущей кадр анимации
u16 baseTileIndex = bga->baseTile; //индекс тайла в VRAM куда будем вставлять тайлсет
//Маcсив тайлсетов (кадров)
TileSet** tile_anim[] = {&tileset_clock_anim1, &tileset_clock_anim2, &tileset_clock_anim3, &tileset_clock_anim4};
while(1){
handleInput();
timer--;
if(timer <= 0){ //если таймер истек
VDP_loadTileSet(tile_anim[cur_frame], baseTileIndex, DMA); //Загружаем тайлсет
timer = 60; //Возобновляем таймер
cur_frame++; //текущий кадр +1
if(cur_frame > 3){ //зацикливаем текущий кадр
cur_frame = 0;
}
}
SYS_doVBlankProcess();
}
return (0);
}
Т.е. по кругу заменяем тайлсет часов следующим кадром.
В bga->baseTile хранится первый тайл тайлсета карты, и так уж вышло, что этот тайл принадлежит часам.
u16 baseTileIndex = bga->baseTile;

Остальное я объяснил в комментариях, либо это уже объяснял здесь.
По итогу, получили следующее.
Итоговый результат.
