GBA ASM - День 13: Эффекты со спрайтами — Архив WASM.RU

Все статьи

GBA ASM - День 13: Эффекты со спрайтами — Архив WASM.RU

Какие именно эффекты со спрайтами?

Мы рассмотрим следующие: вертикальный и горизонтальный кувырок (flip), мозаику, вращение и масштабирование. Тем не менее, относительно вращения, я не буду рассказывать о SIN() и COS(), поэтому мы тольо обсудим что и где поместить в данных для поворота/масштабирования, а остальное вам придётся додумать самим. Я мог бы обсудить создание таблицы значений для массива вращения, но это не слишком вписывается в тему данного Дня.

Флаги кувырка

Это настолько просто, что я даже не буду давать полный пример. Флаг вертикального кувырка - это бит #12 в атрибуте 0. Необходимые константы уже содержатся в sprites.h:

- VERTICAL_FLIP
- HORIZONTAL_FLIP

Флаг горизонтального кувырка - это бит #13 в атрибуте 0. Чтобы включить флаг кувырка, вам нужно поместить содержимое первых 32-х битов OAM-элемента спрайта в регистр и поEORить (то же самое, что и поXORить) его с VERTICAL_FLIP или HORIZONTAL_FLIP. Ок, вот немного кода:

ldr r2,=OAM
ldr r3,[r2] ; загружаем r3 из памяти
; вышеприведённое загрузить r3 атрибутом 0 и атрибутом 1, т.к. регистр 
; 32-х битный, а OAM-атрибуты 16-ти битные.
eor r3,r3,VERTICAL_FLIP   ; XORим r3 с VERTICAL_FLIP и помещаем значение в r3.
; EOR и XOR - это одно и то же, главное помните, что инструкция называется EOR.
str r3,[r2]  ; сохраняем модифицированные атрибуты обратно в OAM.

Этот код включает флаг вертикального кувырка. Я думаю, вы поняли идею, и сможете включить флаг горизонтального кувырка самостоятельно.

Мозаика

Регистр мозаики находится по адресу 0x400004C. Регистр 16-ти битный, биты #15-12 - это вертикальная мозаика, а #11-8 - горизонтальная. Другие биты предназначются для бэкграунда. Кроме инициализации REG_MOSAIC, нам также понадобится установить флаг мозаики в атрибуте 0. Его можно включить, заменив в коде выше VERTICAL FLIP на MOSAIC. Обратите внимание, что вы можете сORить их с помощью символа '|'. Вот изменённый пример:

; иницилизируем регистр мозаики
ldr r4,=REG_MOSAIC
ldr r3,=0x1200  ; 1 для горизонтальной, 2 для вертикальной, ничего для 
                ; бэкграунда
str r3,[r4]   ; сохраняем его

ldr r2,=OAM
ldr r3,[r2] ; загружаем r3 из памяти
; вышеприведённое загрузить r3 атрибутом 0 и атрибутом 1, т.к. регистр 
; 32-х битный, а OAM-атрибуты 16-ти битные.
eor r3,r3,MOSAIC   ; XORим r3 с MOSAIC и помещаем значение в r3.
; EOR и XOR - это одно и то же, главное помните, что инструкция называется EOR.
str r3,[r2]  ; сохраняем модифицированные атрибуты обратно в OAM.

И вуаля! У вас есть мозаичный спрайт.

Вращение

Я надеюсь, что вы прочитали День 3 C-туториала на ThePernProject.COM, потому что если нет, всё это будет достаточно нелегко. Идите, и прочитайте его.

...
...

Прочитайте ещё раз :).
...
...

Думаете, что вы его поняли? Если нет, то неважно, так как я всё равно собираюсь объяснить это сам.

Чтобы с чего-то начать, скажу, что есть нечто под названием "данные вращения" (Rotation Data), в каждом элементе которых есть 4 16-ти битных "пятна". Поняли? Хорошо.

Данные вращения находятся в OAM. Как, спросите вы, разве в OAM не атрибуты спрайтов? Я отвечу: да. Помните, что случилось с атрибутом 3? Мы никогда его не использовали, в то время, как этот атрибут 3 и является данным вращения. Вот маленькая диаграма, показывающая отображение OAM на элементы вращения данных и pa, pb, pc, pd, о которых рассказывается в Дне 3 на ThePernProject:

Sprite 0 Att#0
Sprite 0 Att#1
Sprite 0 Att#2
Sprite 0 Att#3 - Элемент данных вращения #0 - 16bit "spot"#1 - pa
Sprite 1 Att#0 
Sprite 1 Att#1
Sprite 1 Att#2
Sprite 1 Att#3 - Элемент данных вращения #0 - 16bit "spot"#2 - pb
Sprite 2 Att#0
Sprite 2 Att#1
Sprite 2 Att#2
Sprite 2 Att#3 - Элемент данных вращения #0 - 16bit "spot"#3 - pc
Sprite 3 Att#0
Sprite 3 Att#1
Sprite 3 Att#2
Sprite 3 Att#3 - Элемент данных вращения #0 - 16bit "spot"#4 - pd

Sprite 4 Att#0
Sprite 4 Att#1
Sprite 4 Att#2
Sprite 4 Att#3 - Элемент данных вращения #1 - 16bit "spot"#1 - pa
Sprite 5 Att#0 
Sprite 5 Att#1
Sprite 5 Att#2
Sprite 5 Att#3 - Элемент данных вращения #1 - 16bit "spot"#2 - pb
Sprite 6 Att#0
Sprite 6 Att#1
Sprite 6 Att#2
Sprite 6 Att#3 - Элемент данных вращения #1 - 16bit "spot"#3 - pc
Sprite 7 Att#0
Sprite 7 Att#1
Sprite 7 Att#2
Sprite 7 Att#3 - Элемент данных вращения #1 - 16bit "spot"#4 - pd
---- И в таком духе продолжается дальше по всему OAM ----

Я поинмаю, что эта диаграмма получилось довольно большой, но я надеюсь, что она хорошо иллюстрирует расположение данных вращения. Обратите, что любой спрайт может использовать любой элемент данных вращения. Есть 32 элемента, то есть 32 спрайта могут вращаться/масштабироваться.

Тогда что нам нужно поместить в каждое из 16-ти битных "пятен" (pa, pb, pc, pd)? Ок, ThePernProject даёт нам несколько очень неплохих уравнений:

spot 1 - pa needs to be the COSine of the angle.
spot 2 - pb needs to be the SINe of the angle.
spot 3 - pc needs to be the NEGATIVE SINe of the angle.
spot 4 - pd needs to be the COSine of the angle.

Здесь есть ещё кое-что, но оно относится к вращению, которое мы рассмотрим в другой раз.

Обратите внимание, что "пятно" 3 должно быть отрицательным синусом угла, и это то, что делает вращение таким сложным (по крайней мере для меня). Чтобы инициализировать "пятна", нужно получить атрибуты 2 и 3 какого-либо спрайта, очистить атрибут 3 и "пятно" с помощью AND, а затем установить новое значение с помощью OR. Помните, это 16-ти битные числа, поэтому если мы просто инициализируем атрибут 3 нужным значением, мы можем случайно обнулить атрибут 2.

Чтобы облегчить эту задачу, я сделал несколько (на самом деле, довольно много) новых define'ов в sprites.h. Они предназначаются для данных вращения и выглядят как PA_0, PB_0, PC_0, PD_0. Замените '0' номером элемента данных вращения, который вы хотите использовате. Держите в уме, что в sprites.h они определены до PD_19 включительно.

Также, чтобы сделать все это проще, так нам нужно только изучить, как вращать спрайты, мы не будем контролировать обнуление атрибута 2 (мне следует написать соответствующий день о байтовых масках). Вместо этого, мы просто используем спрайт #0 и элемент данных вращения #0. Таким образом, мы можем безболезненно обнулять атрибут 2, так как он и так должен быть равен 0.

Начнём кодирование!

Я надеюсь, что у вас ещё остался sprit.asm из Дня 5. Держите его и дальше, потому что он понадобится в будущем ещё не раз. Давайте глянем на код!

;;--- НАЧАЛО КОДА ---;;
@include screen.h  
@include dma.h   
@include keypad.h 
@include sprites.h

b start
@include sprit.asm 
start

ldr r1,=REG_DISPCNT
ldr r2,=(MODE_1|BG2_ENABLE|OBJ_ENABLE|OBJ_MAP_1D)
str r2,[r1]

ldr r1,=REG_DMA3SAD
ldr r2,=pallete
str r2,[r1]
ldr r1,=REG_DMA3DAD
ldr r2,=OBJPAL
str r2,[r1]
ldr r1,=REG_DMA3CNT
ldr r2,=(128|DMA_32NOW)
str r2,[r1]

ldr r1,=REG_DMA3SAD
ldr r2,=obj
str r2,[r1]
ldr r1,=REG_DMA3DAD
ldr r2,=CHARMEM
str r2,[r1]
ldr r1,=REG_DMA3CNT
ldr r2,=(512|DMA_32NOW)
str r2,[r1]
;;--- СТОП КОПИРОВАНИЕ ---;;

Полагаю, вы должны понять, что здесь происходит.

;;--- ПРОДОЛЖЕНИЕ КОДА ---;;
ldr r1,=OAM
ldr r2,=((SQUARE|COLOR_256|30))|((SIZE_32|(0<<9)|10)<<16)
str r2,[r1]
;;--- СТОП КОПИРОВАНИЕ ---;;

Здесь есть кое-что новенькое. Количество данных вращения сдвигается на 9, чтобы оказаться в правильном "пятне". Мы используем элемент данных вращения #0.

;;--- ПРОДОЛЖЕНИЕ КОДА ---;;
ldr r1,=PA_0    ; сохранение будет происходить в PA.
ldr r2,=70<<16  ; COS(45 градусов) = 0.70, в первом байте регистра, который 
str r2,[r1]     ; работает с числами с фиксированной запятой в данных вращения.

ldr r1,=PB_0    ; сохранение будет происходить в PB
ldr r2,=70<<16  ; SIN(45 градусов) = 0.70. так же как и в предыдущем случае.
str r2,[r1]     

ldr r1,=PC_0    ; сохранение будет происходить в PC
ldr r2,=0       ; Отрицательный SIN(45 degrees) = -0.70
sub r2,r2,70    ; вычитаем 70 от 0, чтобы получить отрицательное значение, и
mov r2,r2,lsl 16   ; сдвигаем, чтобы получить только необходимые 16 бит.
str r2,[r1]

ldr r1,=PD_0    ; сохранение будет происходить в PD
ldr r2,=70<<16  ; COS(45 градусов) = 0.70 снова.
str r2,[r1]  ; сохраняем в атрибуты  #2&3 спрайта 3.
;;--- СТОП КОПИРОВАНИЕ ---;;

Обратите внимание, что мы работаем с 16-ти битными числами с фиксированной запятой, поэтом нам нужны эти 3 сдвига на 16 бит не только для того, чтобы получить значение в нужной части регистра, но также и потому, что 70 - это 0.70 и должно находиться в дробной части числа.

;;--- ПРОДОЛЖЕНИЕ КОДА ---;;
infin

ldr r1,=KEYS           ; первые 3 строки проверяют, нажата ли клавиша A.
ldr r1,[r1]            ; эти строки включают флаг вращения 
ands r1,r1,#KEY_A      ; и флаг двойного размера.
ldreq r1,=OAM          ; однократное нажатие включает эти два флага.
ldreq r2,[r1]          ; получаем атрибуты 0 & 1 спрайта.
eoreq r2,r2,#(ROTATION_FLAG|SIZE_DOUBLE)  ; устанавливаем биты флагов с помощью
                                          ; инструкции EOR (аналог XOR).
streq r2,[r1]
; Обратите внимание, что последние 4 строки выполняются только, если нажата
; клавиша A. 

b infin
;;--- КОНЕЦ КОДА ---;;

Компилируйте и запускайте! Я надеюсь, что вам всё понятно.

Обзор этого дня

Я рад, что доделал этот День, было весело. Да, 13 - счастливое число, вы знаете? Хе-хе. Пока.

2002-2013 (c) wasm.ru