Альтернатива функции синуса для движения спрайта

Работа над некоторыми идеями C64 Intro. Я перемещаю спрайты на основе предварительно созданных таблиц синусов, используя приведенный ниже код и таблицу синусов.

подпрограмма

        ldx counter
        cmx #100
        jmp +

        ldx #00
        stx counter

+       lda sprite_sinus,x

        inc counter

        rts

Синус стол

sprite_sinus   
    !by 25,23,21,20,18,17,15,14,12,11,10,8,7,6,5,4
    !by 3,2,2,1,1,0,0,0,0,0,0,0,0,0,1,1
    !by 2,3,4,5,6,7,8,9,10,12,13,14,16,17,19,21
    !by 22,24,25,27,28,30,32,33,35,36,37,39,40,41,42,43
    !by 44,45,46,47,48,48,49,49,49,49,49,49,49,49,49,48
    !by 48,47,47,46,45,44,43,42,41,39,38,37,35,34,32,31
    !by 29,28,26,25

Но мне нужно что-то другое. Перемещать спрайты в направлении x и y по циклу. Какие еще функции я могу использовать?

1 ответ

Решение

Перебирая этот вопрос, я не удержался, прогуливаясь по переулку памяти, думая о том, как этого можно было достичь во всех тех демонстрациях, которые когда-то казались мне такими удивительными... Вот что я придумал:

То, что вы хотите сделать, чтобы получить несколько сложный вид двумерного движения на основе таблицы синусов, - это объединить несколько синусоидальных волн для построения каждой координаты. Как минимум, вы захотите использовать одну синусоидальную волну для x-координаты, а другую для y-координаты. Используя ваш тривиальный алгоритм, который во время каждой итерации просто увеличивает индекс таблицы на единицу, обе синусоидальные волны будут ограничиваться циклическим переходом на одной и той же частоте. Результатом будет либо движение по диагональной линии, либо с соответствующим фазовым сдвигом между двумя синусами, возможно, по кругу или эллипсу. Это, кстати, уже правильные фигуры Лиссажу; просто не самый захватывающий.

Чтобы сделать этот взгляд более интересным, вам нужно пройтись по разным синусоидам на разных частотах. В качестве простого теста попробуйте увеличить индекс таблицы на два вместо одного за одну итерацию в одной из двух координат и понаблюдайте, что происходит. Для более общего решения вам потребуется реализовать "осциллятор" для каждого индекса таблицы: переменную, в данном случае, возможно, ширину 16 бит, которая будет увеличиваться в соответствии со значением "частоты", хранящимся в другой переменной (просто путем добавление последнего во время каждой итерации и допущение переполнения осциллятора к циклу). Затем вы можете взять только несколько старших бит этого генератора для использования в качестве индекса таблицы. Для простоты ваш стол должен иметь размер степени двойки.

Чтобы получить еще более сложные фигуры, попробуйте добавить две (или более) синусоидальные волны, колеблющиеся на разных частотах, чтобы построить каждый компонент координат. Возможно, вы могли бы по-разному масштабировать синусоиды, например, разделив свои значения на два перед добавлением. Прошло какое-то время (например, более двух десятилетий), с тех пор как я в последний раз смотрел демонстрацию, основанную на движении спрайтов Лиссажу, но если моя память верна, это была именно та комбинация, которая использовалась.

Ниже приведены некоторые фрагменты кода, которые я только что написал, еще не тестировал. Тем не менее, их анализ должен, надо надеяться, прояснить, что я пытался выразить в этой прогулке.

; Routine for advancing a 16-bit oscillator depending on a 16-bit frequency
;
; Upon entry, x must hold offset to oscillator data. (So that this
; routine can be reused with multiple oscillators. x not preserved)
; Returns sine-table value for next oscillator step in a.
advance_osc:
    clc            ; increment 16-bit oscillator by 16-bit frequency
    lda osc_lo,x
    adc frq_lo,x
    sta osc_lo,x
    lda osc_hi,x
    adc frq_hi,x
    sta osc_hi,x
                   ; get table index (osc_hi still in a)
    and #$3f       ; mask for 6-bit table size
    tax            ; x now holds 6-bit table index
    lda table,x    ; a holds value from table
    rts

; Snippet for building sprite coordinate from multiple sine-table values

; Step 1: use one sine wave to start building x coordinate
    ldx #osc_offset
    jsr advance_osc   ; returns next table value for that osc
    sta x_coord
; Step 2: add another sine wave to x coordinate
    ldx #next_osc_offset
    jsr advance_osc
    lsr               ; optional: scale down result by two
    clc
    adc x_coord       ; add sine-waves
                      ; (may want to rework this to handle 9-bit x_coord)
    sta x_coord
; Step 3..4: now, repeat the above steps for y_coord
    ...
; Step 5: once done, copy coordinates into VIC registers...
    ...
; repeat for next sprite (may be reworked into a loop)
Другие вопросы по тегам