PDA

Просмотр полной версии : Обрезание спрайта на краю экрана.



moroz1999
21.02.2006, 15:51
Я достаточно мало знаю теории по этому вопросу, может быть кто-то подскажет какие-то хитрые решения по следующему вопросу:

Имеется процедура вывода спрайтов на экран со следующими параметрами:
координаты вывода - попиксельные,
X-размеры спрайта кратны 8 пикселям, Y-размеры кратны 2 пикселям.

вывод на экран ведется т.н. "змейкой", т.е нечетные выводятся слева направо, четные для скорости справа налево.

вывод ведется напрямую в видеообласть памяти.

вопрос: как в теории проще/быстрее/хитрее/оптимальнее обрезать спрайт, заступающий за край экрана?

Sinus
21.02.2006, 16:31
размеры спрайта какие? все спрайты разных размеров или одинаковых?
если не очень большие и одинаковые, то можно (читать нужно) раздекрянчить смещая по X и создать несколько процедур вывода (кстати выводя "змейкой" особо не ускоришь).

допустим есть max размер по X в знакоместах будет 4, то создаёшь следующие процедуры для вывода

X <= (-4*8) : нафуй
X > (-4*8) && X < (-3*8) : proc_m1
.....
X>=0 && X<255 : proc_normal
...
X>255 && X<(255+8) : proc_p1

вот.
оч. быстро ;)
но не оптимально по памяти.

------

или так: есть 3 процедуры, спрайты покрянчены

1) спрайт залазит влево
2) спрайт полностью на экране
3) спрайт залазит вправо

если спрайт на экране, то всё очевидно.

допустим, спрайт залазит вправо.

если вывод 2х строк такой:



line_1:
ld a,(bc)
inc с
ld d,a
dup max_width-1
ld a,(de)
and (hl)
inc hl
or (hl)
inc hl
ld (de),a
inc e
edup
ld a,(de)
and (hl)
inc hl
or (hl)
inc hl
ld (de),a
ret

line_2:
ld a,(bc)
inc с
ld d,a
ld a,<mod_this_value>
add a,l
ld l,a
dup max_width-1
ld a,(de)
and (hl)
inc hl
or (hl)
inc hl
ld (de),a
dec e
edup
ld a,(de)
and (hl)
inc hl
or (hl)
inc hl
ld (de),a
ret


то можно просчитать где поставить нужный ret (чтоб лишнего не выводить), замодифицировать line_1 и line_2 для этих целей, так же пропишем <mod_this_value>

обрежем по Y и далее



; b - кол-во 2x линий для вывода
; hl' - указывает на нужное место спрайта
; e' - колонка в экране
; c' - номер линии в экране
; b' - адрес таблички адресов
ld hl,retaddr
push hl
ld hl,line_2
ld de,line_1
lp0:
push hl
push de
djnz lp0
ret

retaddr:
....


! код работать не будет !
тут есть проёб с тем что выводится "змейкой"- тогда не получится так просто доставать адреса из таблички.
лучше без змейки. и из таблички надо доставать не только старшую часть адреса (в данном случае D) но и младшую (E) и добавлять к ней смещение

в общем идея думаю ясна.

moroz1999
21.02.2006, 16:40
спасибо за инфу.
извиняюсь, мой прокол - не все требования выложил.

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

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

кстати, на крайний случай от змейки отказаться несложно.

Sinus
21.02.2006, 16:48
спрайт хранится правильно (маска - спр.)
а вот что покрянчить не можешь это плохо...
а зачем кил 8 на табличку?



RRtab:
DB %00000000,%00000000 ; %00000000
DB %00000000,%10000000 ; %00000001
DB %00000001,%00000000 ; %00000010
.... 256 раз
.... .... и всё это 9 раз (для смещений 0-8)


около 4 кил получается.

кстати можешь прооптимизировать вывод спрайта если смещение==0, но думаю смысла не имеет.

а "змейку" ты так выводишь?


.... вывод 1 линии ....
CALL DOWN_DE
.... вывод 2 линии ....
CALL DOWN_DE


если да, то замени DOWN_DE на выборку из таблички, но тогда со змейкой туго будет ;)

Sinus
21.02.2006, 16:50
и ещё. с твоими требованиями фреймово ты полюбому не сделаешь.
так что особо заморачиваться по поводу скорости и выигрывать каждый такт нет смысла.

лучше всё пофикси под таймер, чтоб с турбой всё стало круто, но не улетело.
а без турбы будет подтормаживать, но не тормозя игровой процесс....

(или ты не игру делаешь?)

... ну думаю меня поймут ...

Wlodek
21.02.2006, 19:14
Общая идея такова: использовать для вывода спрайта таблицу (или две таблицы :) ) адресов видеопамяти и в каждой из них предусмотреть несколько байт или слов холостых адресов за пределами экрана, в зависимости от максимального размера спрайта. Ещё в 1989 году я это успешно использовал в "Приключении в метро" :) .

Sinus
21.02.2006, 19:27
Wlodek: это если по вертикали спрайт вылазит, то можно в ПЗУ позапихивать ;)

а если по горизонтали?
или ты предлагаешь каждый байт из таблички доставать? это ж тормозно.

Wlodek
21.02.2006, 20:41
По горизонтали можно представить, что экран представляет из себя не 32, а 33, 34 и т.д. знакоместа (в зависимости от реального максимального размера спрайта). По вертикали же - верно, сколько "лишних" строк, столько и "лишних" ссылок из таблиц адресов. Говорю, реально просто и элементарно. Процедура вывода спрайтов даже не задумывается над тем, что она на самом деле ряд пиксельных элементов выводит не на экран, а, например, в область адресов ПЗУ. Никаких переходов по "если", никаких ветвлений. Говорю, я это использовал и доволен ;) . А таблица адресов в памяти - ну, так нынче минимум 128К! А я в 1989 году всё в 48К упихнул и без проблем ;) . Всего 41К, со всеми таблицами, образами спрайтов, графикой и, конечно, кодом программы. Сейчас всё намного комфортнее ;) .

axor
21.02.2006, 21:01
Можно сделать экран не во всю ширину, а по краям поставить атрибуты (ширина этой полоски зависит от max ширины спрайта), у которых совпадает ink и paper.

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

Sinus
21.02.2006, 21:12
По горизонтали можно представить, что экран представляет из себя не 32, а 33, 34 и т.д. знакоместа

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


или ты предлагаешь каждый байт из таблички доставать? это ж тормозно.


т.е. ты на каждый выводимый байт берёшь адрес из таблички.
а я говорю, что это ТОРМОЗНО! ;)

Sinus
21.02.2006, 21:14
Можно сделать экран не во всю ширину, а по краям поставить атрибуты (ширина этой полоски зависит от max ширины спрайта), у которых совпадает ink и paper.

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

Vladimir Kladov
21.02.2006, 22:19
двойная маска. т.е. к маске которую имеет сам спрайт, добавлять еще одну маску. Если спрайт ширины 2 или 3 байта - вполне эту маску можно перед выводом спрайта грузануть в регистры со штрихом, и там и держать, для небольшого ускорения. Если не нравится замедление - сделать 2 варианта процедуры вывода, вариант с дополнительной маской работает только для спрайтов, которые "выехали" за край. Идея ясна?

axor
22.02.2006, 09:31
да, такое делалось в некоторых демках и игрушках.
но имхо не лучший выход.
тем более что не всегда применимо- допустим если игра не во весь экран, а в "рамочке". там уж атрибуты особо не расставишь
Но зато самый простой способ :)

Vitamin
22.02.2006, 18:07
спрайт фиксированной ширины? если да, то можно написать набор подпроцедур типа:

;если влазит целиком
put_all:
dup N
ldi
edup
inc d
dec e
dup N
ldd
edup
inc d,e
ret

;если не влазит K знакомест справа
put_noK:
dup N-K
ldi
edup
inc d
dec e
dup K+K
inc hl
edup
dup N-K
ldd
edup
inc d,e
dup K+K
inc hl
edup
ret

естесно соптимизировать коррекцию адреса в HL

вывод будет состоять в вызове нужной процедуры.
плюс отсечение невидимых спрайтов. Итого имеем 1 процедуру полного вывода, N процедур для вывода за правым краем и N процедур для вывода за левым краем.

зы: код можно переделать и под маску

Sinus
22.02.2006, 21:20
acidrain ну во-первых это не я такое решение предложил (кста можно и не в ПЗУ, а в любое свободное место ОЗУ), а во-вторых, если там страничка озу, то надо каким-нибудь битом в каком-нибудь порте запрещать туда запись.

;) или отключалку делать ;)

moroz1999
22.02.2006, 21:52
да, такое делалось в некоторых демках и игрушках.
но имхо не лучший выход.
тем более что не всегда применимо- допустим если игра не во весь экран, а в "рамочке". там уж атрибуты особо не расставишь
выход отличный, сам на данный момент держу в голове как вариант. пофиг до рамочки, главное - геймплэй :)

Sinus
22.02.2006, 22:00
так это... у тя ж вроде спрайты большие???
или мекие?
если мелкие, то лучше процедурок понаписывать на все случаи жизни.

а если большие, так это какие ж поля надо будет чёрными (читать INK=PAPER) оставлять ???

moroz1999
22.02.2006, 23:00
блин, и правда, торможу после работы :o

Soplik
23.02.2006, 13:20
А если кинуть безо всяких проверок, а запорченный экран быстро-быстро забить фоном?

Sinus
23.02.2006, 13:23
1) тормозно
2) проще обрезать спрайт, чем высчитыват где запортился экран
3) тормозно ;)

moroz1999
24.02.2006, 01:03
А если кинуть безо всяких проверок, а запорченный экран быстро-быстро забить фоном?может возникнуть ситуация, что помимо фона на той стороне спрайт будет.

Soplik
24.02.2006, 01:52
Уже понял.
А обязательно все спрайты попиксельно выводить?

Vladimir Kladov
24.02.2006, 11:15
Есть еще такое решение: строить весь кадр в буфере с последовательной организацией строк, и при этом строки делать шире, добавляя с обоих краев нужное число байтов (по размеру максимального спрайта минус 1). При переброске обрезать. Работает в том числе когда задействована вся ширина для игрового поля и когда есть рамки и не только рамки ink=paper. И очень неплохо, когда игровое поле уже (а например справа еще что-то типа как меню команд в лазер сквадроне).

Sinus
24.02.2006, 12:41
Vladimir Kladov: очень хорошее решение, но к сожалению фреймовости не добится.

хотя я вот (когда муль свой тестил) посмотрел пару старых игрулек (Hydson Hawk тот же) - нифига не фреймово, но не менее играбельно

moroz1999
24.02.2006, 13:13
ну, я на фреймовости не зацикливаюсь, имхо, невозможно написать более-менее навороченную аркаду, укладывающуюся в фрейм.

rjhaviy
16.02.2009, 03:25
есть 3 процедуры, спрайты покрянчены

1) спрайт залазит влево
2) спрайт полностью на экране
3) спрайт залазит вправо

если спрайт на экране, то всё очевидно.
...



покрянчены ??? Объясните пожалуйста новичку, что это за метод???

jerri
16.02.2009, 09:43
покрянчены ??? Объясните пожалуйста новичку, что это за метод???

Покрянчены значит приготовлены к быстрому выводу
к примеру у тебя есть спрайт размером 16 х 16 пикселей
и тебе надо выводить его с точностью до 2х пикселей

т.е изначально имеем
2*16 - 32 байта под спрайт
и некую процедуру печати спрайта с точностью до пикселя

но все это работает крайне медленно (см ZX Ревю :) )

а далее берем и на основе имеющегося спрайта 16*16 (2х16)
создаем еще 3 спрайта размером 24*16 (3*16)
для чего скролим спрайт вправо на 2 пикселя и прячем в памяти

и теперь имеем и спрайт 2*16 - 32 байта и еще 3 спрайто 3*16
все вместе весит уже 176 байт

но теперь мы можем существенно облегчить процедуру печати спрайта
как по весу так и по скорости

и теперь при прорисовке смотрим на координату X
и в соответсвии со смещениями берем нужный нам спрайт

кстати если изначально задать при рисовании размер к примеру не 16*16
а 18*16 то при скролле не потребуется менять размер спрайта
а 2 пикселя для таких мелких спрйтов это ОГОГО сколько :)

будут еще вопросы - пиши в аську

rjhaviy
16.02.2009, 15:29
jerri, спасибо! Я все понял, елси мне не изменяет памать, то именно так сделано во многих игрушках Hewson, таких как Exolon, Zynaps, Cybernoid 1 и 2,
и др.?

jerri
17.02.2009, 09:51
Да именно так

jerri
18.02.2009, 17:20
иллюстрация