game making (c) demon/xpc/cpu
приветствую тебя, забредший сюда... речь здесь опять ( смотри adv#10 ) о говеном
гаме-макинге >: ->
ну, начнем со спрайт-выводилок, все просто и ясно - бывают они:
- с попиксельными координатами вывода и с знакоместовыми;
- с масочкой и без масочки;
- с атрибутами и без.
дальше идут комбинации вышеперечисленной лажи. делить на виды по способу организации
вывода (зависит от структуры хранения инф-ции в спрайтах) я не буду, все равно все не
перечислить, даже не помню, как хранились спрайты в сраном спрайт генераторе от thd
(козлы), который был для меня первой программой такого рода.
несколько слов о выводилках спрайтов: по моему мнению они должны соответствовать
определенным условиям, например, для меня не нужны были stack-fast 'овые, и до охереннных
размеров раздекранченные, т.к. юзались оба экрана, то выводилка должна была сидеть в
основной памяти спека (#5b00-#bfff), а кроме нее еще много чего там сидело.
расскажу о трех выводилках; по y они пиксельные, по x две из них знакоместовые и одна
пиксельная.
1. х - знакоместовая без маски
тут все стандартно и без всяких гребаных наворотов со стеком и др. причудами, чем проще, тем меньше #бли. но если у тебя есть желание трахаться с прерываниями и трассировать стек, то флаг тебе в руки... итак 32 ldi - это rulez, всего 16 тактов на байт (и на моем четно-тактовом скорпе тоже). задаем координаты в de, размер спрайта в bc и сам спрайт в hl, делаем call ssp, и он на экране (если задано не правильно, то на экране скорее всего появится говно). процедурка эта ничего не проверяет и ничего не возвращает, т.к. это низший уровень ядра проги. хочешь - переделай или вообще лучше сделай свою, будет чем гордиться...
внимание! в листингах присутствует ненормативная лексика - директивы xas 'a.
;sprite x_symbol print without mask ;average_speed= 18.5 t/b (scorp) ;prog_len=120 b ;-------------- ;in : hl- sprite ; de- coord (x-sim,y-pix) ; bc- size (y-pix,x-sym) ;-------------- ssp push ix ld ix,wo_r_ok+4; рассчитываем и ld a,c; смещаем цикл add a,a; по ldi sub 64; через jp (ix) neg ld c,a ld a,b ld b,0 add ix,bc ld b,a push hl call adres_y; расчет адреса ex de,hl pop hl jr wo_r_ok; входим в цикл... wo_rinc 28+ ld a,e; 4 add a,32; 8 (7) ld e,a; 4 jr c,wo_r_ok; 8 (7)/12 ld a,d; 4 sub 8; 8 (7) ld d,a; 4 jp wo_r_ok; 10 wo_ylop inc d; 4 ld a,d; 4 and 7; 8 (7) jr z,wo_rinc; 8 (7) wo_r_ok push de; 10 ld c,d ; 4 шоб было c>32 jp (ix); 8 !assm 32; 32-е инструкции ldi ldi ; 16 !cont pop de; 12 (11) djnz wo_ylop; 14 (13) pop ix ret ;------------- расчет адреса в экране ;in: de- x(sym), y(pix) ;out: hl- adres in screen ;------------- adres_y ld l,d ld a,e and 7 ld h,a ld a,e and #38 rlca rlca or l ld l,a ld a,e and #c0 rra rra rra or h ld h,a ld a,(how_scr) or h ld h,a ret ;------------- var how_scr db #40
такты указаны в критичных местах, первые цифры справедливы для скорпа ,в скобках - для всего остального. кстати, на моем скорпе тесты кажут 74884 t в прерывании по четнo-t-вым и от 71141 до 71440 по нечетно-t-вым командам. переменная how_scr является определяющей для того, с каким экраном в данный момент работает прога (не видимый, а рабочий! ), #40 для 5-го и #c0 для 7-го, например: рисуем 5-й экран (how_scr=#40), а видим 7-й, вдруг прерывание: определяем по переменной pag_or (см.ниже), что включен 7-й экран, меняем how_scr на #c0 и стрелку выводим на 7-ом; т.к. все gfx процы, работают через эту переменную, то и адреса все будут рассчитаны для соответствующего экрана. можно вякать, что это тупо и надо бы просто 5-й врубать с #c000 ( sorry, elf ), но при работе с двумя экранами я пришел именно к этому методу.
2. х - знакоместовая с маской
;(c) demon/xpc ;sprite x_symbol printer with mask ;average_speed= 50 t/b (scorp) ;len= 287 b ;------------- ;in: hl- sprite with mask ; de- coord x(sym), y(pix) ; bc- size y(pix), x=(sym) ;------------- ssp_m push ix; усе тоже самое ld ix,wm_r_ok+3 ld a,c add a,a add a,a add a,c add a,c add a,c sub 224 neg ld c,a ld a,b ld b,0 add ix,bc ld b,a push hl call adres_y ld d,b ld b,h ld c,l pop hl jr wm_r_ok; опять цикл... wm_rinc; 28+ ld a,c ; 4 add a,32; 8 (7) ld c,a ; 4 jr c,wm_r_ok; 4 (7)/12 ld a,b ; 4 sub 8; 8 (7) ld b,a ; 4 jp wm_r_ok; 10 wm_ylop inc b; 4 ld a,b ; 4 and 7; 8 (7) jr z,wm_rinc; 8 (7) wm_r_ok ld e,c ; 4 jp (ix); 8 !assm 32 ld a,(bc) ; 8 (7) на байт экрана and (hl); 8 (7) накладываем маску inc hl; 6 и байт спрайта or (hl); 8 (7) ложим на результат inc hl; 6 ld (bc),a ; 8 (7) кладем в экран inc c; 4 !cont ld c,e 4 dec d 4 jp nz,wm_ylop; 14 pop ix ret
спрайт должон иметь следующую структуру: байт-маска, байт-дата, байтмаска и т.д., как делает sprite land от dr. насчет этих двух проц действует следующее правило: , "лучше шире и короче, чем уже и длиннее" , т.е. спрайт размером х=32 и y=10 нарисуется быстрее, чем размером х=10 и y=32, хотя размер в байтах одинаковый.
3. попиксельная с маской
сделал эту процу в основном elf , xn0bys чего-то тоже делал. когда костян (elf) заявил, что это самое, мол, быстрое, то на спор я ее, родимую, зафастил. выигрыш по сравнению со старой в среднем составил 5000-7000 тактов, но если брать спрайт больших размеров (испытуемый кушал около 45000 тактов), то он (выигрыш) пропорционально возрастает. а все за счет использования недокументированных команд; думаю, что можно еще ускорить, но влом...
;fast pixel's sprite with maska... ;by elf/auryn & xn0bys/sg ;...fastest ;by demon/xpc'99 ;in: hl adr_of sprite ; d coord x in pixels ; e coord y in pixels ; c -x_size symbol ; b -y_size pixel spr_pxm push hl call adres_p ex de,hl add a,a push bc ld c,a ld b,0 ld hl,rol_tab add hl,bc ld a,(hl) inc hl ld h,(hl) ld l,a ld (pxspr+1),hl pop iy pop hl pxspr jp bit_0 ;in iy-y & x size ; hl-sprite with mask ; de-adres on screen bit_0 ld c,ly bit_0l1 ld b,c ld lx,e bit_0l2 ld a,(de) and (hl) inc hl or (hl) ld (de),a inc hl inc e djnz bit_0l2 ld e,lx call down_de dec hy jr nz,bit_0l1 ret bit_1 ex de,hl ld a,hy ld hx,a bit1_l1 ld hy,ly ld a,l ex af,af' bit1_l2 ld bc,#ff ld a,(de) scf rra rr c and (hl) ld (hl),a inc de ld a,(de) rra rr b or (hl) ld (hl),a inc l inc de ld a,c and (hl) or b ld (hl),a dec hy jr nz,bit1_l2 ex af,af' ld l,a call down_hl dec hx jr nz,bit1_l1 ret bit_2 ex de,hl ld a,hy ld hx,a bit2_l1 ld hy,ly ld a,l ex af,af' bit2_l2 ld bc,#ff ld a,(de) scf !assm 2 rra rr c !cont and (hl) ld (hl),a inc de ld a,(de) !assm 2 rra rr b !cont or (hl) ld (hl),a inc l inc de ld a,c and (hl) or b ld (hl),a dec hy jr nz,bit2_l2 ex af,af' ld l,a call down_hl dec hx jr nz,bit2_l1 ret bit_3 ex de,hl ld a,hy ld hx,a bit3_l1 ld hy,ly ld a,l ex af,af' bit3_l2 ld bc,#ff ld a,(de) scf !assm 3 rra rr c !cont and (hl) ld (hl),a inc de ld a,(de) !assm 3 rra rr b !cont or (hl) ld (hl),a inc l inc de ld a,c and (hl) or b ld (hl),a dec hy jr nz,bit3_l2 ex af,af' ld l,a call down_hl dec hx jr nz,bit3_l1 ret bit_4 ex de,hl ld a,hy ld hx,a bit4_l1 ld hy,ly ld a,l ex af,af' bit4_l2 ld bc,#ff ld a,(de) scf !assm 4 rra rr c !cont and (hl) ld (hl),a inc de ld a,(de) !assm 4 rra rr b !cont or (hl) ld (hl),a inc l inc de ld a,c and (hl) or b ld (hl),a dec hy jr nz,bit4_l2 ex af,af' ld l,a call down_hl dec hx jr nz,bit4_l1 ret bit_5 ex de,hl ld a,hy ld hx,a bit5_l1 ld hy,ly ld a,l ex af,af' bit5_l2 ld a,(de) ld c,a ld a,#ff !assm 3 sli c rla !cont and (hl) ld (hl),a inc de ld a,(de) ld b,a xor a !assm 3 rl b rla !cont or (hl) ld (hl),a inc l inc de ld a,c and (hl) or b ld (hl),a dec hy jr nz,bit5_l2 ex af,af' ld l,a call down_hl dec hx jr nz,bit5_l1 ret bit_6 ex de,hl ld a,hy ld hx,a bit6_l1 ld hy,ly ld a,l ex af,af' bit6_l2 ld a,(de) ld c,a ld a,#ff !assm 2 sli c rla !cont and (hl) ld (hl),a inc de ld a,(de) ld b,a xor a !assm 2 rl b rla !cont or (hl) ld (hl),a inc l inc de ld a,c and (hl) or b ld (hl),a dec hy jr nz,bit6_l2 ex af,af' ld l,a call down_hl dec hx jr nz,bit6_l1 ret bit_7 ex de,hl ld a,hy ld hx,a bit7_l1 ld hy,ly ld a,l ex af,af' bit7_l2 ld a,(de) ld c,a ld a,#ff sli c rla and (hl) ld (hl),a inc de ld a,(de) ld b,a xor a rl b rla or (hl) ld (hl),a inc l inc de ld a,c and (hl) or b ld (hl),a dec hy jr nz,bit7_l2 ex af,af' ld l,a call down_hl dec hx jr nz,bit7_l1 ret down_hl inc h ld a,h and 7 ret nz ld a,l add a,32 ld l,a ret c ld a,h sub 8 ld h,a ret down_de inc d ld a,d and 7 ret nz ld a,e add a,32 ld e,a ret c ld a,d sub 8 ld d,a ret adres_p ld a,e and a rra scf rra and a rra xor e and #f8 xor e ld h,a ld a,d rlca rlca rlca xor e and #c7 xor e rlca rlca ld l,a ld a,d and #07 ret rol_tab dw bit_0,bit_1,bit_2,bit_3 dw bit_4,bit_5,bit_6,bit_7
смысл в следующем: вычисляется на сколько придется роллировать байты спрайта с маской, из таблицы берется соответствующая проца, и процесс цикла начинается. спрайт имеет такую же структуру, как описывалось выше. наверно в этой проце можно че-нибудь подчистить, если есть желание, хотя работает она без проблем и уже довольно долго нами юзается в таком виде. все эти выводилки работают с монохромными спрайтами, т.к. вывести атрибуты с точностью до пиксела по y очень и очень проблематично >: ->
прежде чем перейти к следующей части своего повествования, хочу рассказать о том, как у меня свапуется память и экран.
;-------------------page lister ;in: a- page logic nomber (0...7), ; if a=#ff then goto old_pag pager ex af,af' ld a,r jp pe,pg_n_im ld a,r pg_n_im push af di ex af,af' and a jp m,old_pag; a=0...7 pg_f_op ld (sav_pag),a ; for old_pag now_pag equ $+1 ld a,7 ld (old_pag+1),a sav_pag equ $+1 ld a,0 ld (now_pag),a pg_f_sw and 7; for scr_swp pag_or equ $+1 or 0 out (#fd),a ld (23388),a pop af di ret po ei ret old_pag ld a,0 jr pg_f_op ;----------------------screen swaper ;in: a- 0 is #4000, other is #c000 viewing scr_swp ex af,af' ld a,r jp pe,sw_n_im ld a,r sw_n_im push af di ex af,af or a ld a,(pag_or) jr nz,sw_is_7; if a=0 then scr#5 res 3,a sw_f_7 ld (pag_or),a ld a,(now_pag) jr pg_f_sw sw_is_7 set 3,a jr sw_f_7 ;----------------- #fd mask detecter ;out: a- mask for pager fd_mask ld a,#10 ld bc,#7ffd out (c),a ld hl,#ffff ld e,(hl) ld a,#50 ld (hl),a out (c),a cp (hl) ld a,#10 out (c),a ld (hl),e ret nz ld a,#50 ret
все вышеперечисленные процы составляют один пакет для работы с памятью 128 кб. - проца fd_mask запускается один раз в самом начале для определения 512ти килограммовых компов, если нет, то щелкаем память с включенным 6-м битом ( для скорпа а ). - проца pager свапует 128-ю память, запоминает текущую страницу в now_pag, предыдущую в old_pag и все свапует через pag_or. mаразм с регистром r и определением состояния прерываний взят из статьи ивана рощина , за что огромное ему спасибо. таким макаром у меня ни разу не было глюков по определению текущей страницы. а почему щелкаю по #fd, а не по #7ffd? да вот нравится так. - scr_swp работает через pager, поэтому когда щелкаю память не болит голова о том, какую маску ставить для экрана.
ну теперь про int (прерывания). очень рулезная вещь, эти самые прерывания, можно вешать на них, что хочешь, хоть весь движок. но лучше повесить такие вещи, как сканирование кнопок и мыши, движение курсора (если он присутствует), какие-нибудь фишки, типа мини-скролла с хелпом, либо небольшой анимации где-нибудь в углу экрана и, конечно, музыку и эффекты в конце. при работе с int может возникнуть множество неприятностей, все даже не предсказать; расскажу о своих. в adv#10 был приведен мой обработчик прерываний, аналогичный сидит в "full shit" , в котором: - маленькая проца по переключению видимого экрана, юзает глобальные переменные now_pag, page_or; - сохраняется номер текущей страницы памяти и определяется видимый экран, при этом сохраняется, а затем меняется переменная how_scr; - врубается страница #7, т.к. в ней сидят все процы. следует учесть, что int-таблица содержит 257 байт и находится во 2-м банке, чтоб хоть как-то не обидеть оригинальный спек . на возможный вопрос: "почему 7-й банк?" , ответ таков: юзаются оба экрана, места в #5b00-#bfff мало, а в 7-ом за вычетом экрана и скрина локации остается целых 3 кило. - восстанавливается экран под курсором, но если ранее сработало переключение видимого экрана, то дерьмо со старого не восстанавливается; - сканируется клава на предмет нажатия кнопок "6, 7, 8, 9, 0, break, q, a, o, p, space" и движения мыши (на пц - эмулях какой-то голюн с нашим драйвером: пц маст дай); - идет обращение к проце под именем int_gfx. это и есть разные фишки, состоит в основном из кучи векторов, которые меняются в зависимости от действий геймера, например: появляется менюха, где крутится предмет (надо отрубить лампочку на фонаре, а потом повесить вращающийся предмет, а если вызвать карман, то 4-е предмета; мультики тоже играются по int) или диалоговое окно, все по int на х..; - запоминается кусок в новом месте, если было перемещение (иначе в старом), рисуется курсор, после всех наворотов, чтоб не попасть под какой-нибудь спрайт или текст; - в последнюю очередь играется музон и для прикола меняются атрибуты на светофоре; - восстанавливаем страницу памяти, переменную how_scr и назад.
первый глюк возник, когда я решил все заоптимизить: убрал собственные процы вывода графики из прерываний, чтоб все делалось через единые процы. и тут началось... на экране стало появляться всякое дерьмо, без всякой закономерности, иногда вообще все висло или сбрасывалось. а при трейсе все работало без проблем. такие глюки ненавижу, т.к. причина сразу-то и не ясна. а вся херня была из-за того, что в старом варианте выводилки сохранялись локальные переменные (размер спрайта) и при попадании на int они киллились, т.к. int тоже юзал эти процы. вывод: если процу юзают все, то при ее работе все переменные надо хранить на регистрах или толкать их в стэк, иначе в прерываниях придется сохранять все переменные. второй глюк возник с переключением экранов, но об этом я, по-моему, уже говорил... в общем, если все в проге тщательно проверить, то глюков обычно возникает мало и не советую вешаться на im 1, хотя кому как нравится, хоть на im 0 работайте >: -> теперь о структуре "full shit" , которая оставляет желать лучшего, но что есть, на том и кашу сварим...
can: on/off [t],all process; lock [c] +========+=======+======+=======+=====+ +------------+ w w w w w | | int_mode 2 | begin <------------+ | +------------| +------>------+ | | | 1.scr_swp | +------+ pass | | | | 2.key_scn | +--| go |<------[t]<| | +==| 3.cr_proc | | +------+ | ^ | | | +------+ pass | +---<--+ | 4.int_gfx | | +-| menu |<-----[t]<| | | | 5.play_mz | | | +------+ | | | | | | | +------+ pass | |n | +-----+------+ | | +| time |<----[c]<+ |o | | | | |+------+ +-------+ | | | | | +-----------|chk_cnt|>[time?] | | | +---------+ +-------+ | | save all | | | | and use |+-------+ |+-------+ | ^ | +|clc_way| +|chk_map++ |y | | +---+---+ +-------+| | | | | | |e | | v is not no | |s | | [way?]---->-+-<--[thing?] +-+ | | | | | | | | +----+ | +--+ v | | | | | | | | |+-------+ v |+-------+ | | | +|mak_way| return +|win_put| | +<-----+ | +---+---+ to +---+---+ | | | | +----+ "begin" +-----| | | | | | | | | | | | |+-------+ | +---+---+ | | use "go" | +|go_draw| | |actions| | | | | +---+---+ | +---+---+ | ^ ^ | | +----<-----------<--+ | | | | | | +------->[doing]--+ | | | | +-----------| | | v v | +---+---+ | | v v v | v +-----+ +-----+ | gfx_engine |death| |happy| | +---------------------+ | cut | | cut | | | draw location: | +--+--+ +--+--+ | | 1. background | | | | | 2. hero | +---+---+ | | 3. upground | | | | 4. turn on "scr_swp"| | | | use many primitive | v | | gfx_procs | | | +---------------------+ | | | | | +---------------------+ | | | low level procs | <-------+---------<---+ +---------------------+ | | | v v v video ram disk loc_map +------------------------+ | 32*20=640 bytes | +---------------+ use it:| %%+=+%%%% ******|cal_way - read ##|#о##### *+o+*|chk_map - read ##|#о###### ++ *|actions - read & write #=+=########++ *+--------+
пояснилова
1) обозначения:
[t] - порог (#0 или #с9);
[c] - порог-счетчик (пропускает через n-ое кол-во вызовов;
[time?] - условие (например: время);
2) подпроги:
go: - проца, отвечающая за перемещение героя;
clc_way - просчет пути, если нет, то нет перемещения;
mak_way - иначе делает специальный управляющий массив;
go_draw - работа со спрайтами перемещения по упр. массиву.
menu: - действия с предметами;
chk_map - проверяет статус курсора "на предмете или нет", если нет, то игнорирует юзера
win_put - иначе выводит окно действий и ждет действий геймера;
actions - производит выбранные действия, сопровождая это дело выводом сообщений и анимации, и согласно им делает переход на соотв. процу либо возвращается в основной цикл.
time: - постоянно перерисовывает игровой экран;
chk_cnt - периодически добавляет разную анимацию (автобус, вышибала, герой...)
int_mode2 - контролирует все процессы с помощью "порогов входа" в процы (nop or ret), свапует, сканирует, рисует и поет...
gfx_engine - участвует всегда в перерисовке экрана либо выводе чего-либо на экран.
low level procs - утилиты-примитивы, работающие именно с ресурсами машины.
loc_map - это массив размером 32*20 элементов, являющийся картой локации. в основном используется при расчете пути и работе с предметами.
now пойдет речь о процедуре рассчета пути в карте локации для того самого мудака, которого ты видел в "full shit". алгоритм взят у славы медноногова (который его тоже где-то взял и т.д. и т.п.) из его статьи в одном из zx-fоrmat 'ов. для начала лучше почитать эту статью, а уж потом суйся сюда, т.к. я прос-то приведу ассемблерный вариант этого метода... сразу скажу, что это дело для меня оказалось несколько проблематичным, т.к. мой чиж (ну, персонаж) получился не элементом 1х1, а педрюком 1х4. поэтому я просто зае...ся точить прогу для этого говнюка, чтоб он хоть как-то нормально ходил по локации, тем более из-за ограничений по памяти он не мог ходить по диагонали. в общем процесс написания этого куска движка затормозил вообще весь процесс. ладно, вот вообщем оно-самое:
;(c) demon / xpc'99 ;---------------------------- ;wawe algoritm for search way ;main idea v.mednonogov ;realized by me,of course... s_cod equ #50; start cod gc_mint equ s_cod+45; max iteration ;----------------- go_wawe ret ; turn ld a,#c9 ld (go_wawe),a ;-------------------- make work dim 34*22 =748 b ;делается окантовочка, чтоб лишний раз не проверять выход ;за границу массива ld b,20 ld hl,#6200; map_loc ld de,gc_wdim+35 gd_lop ld c,h !assm 32 ldi !cont inc de inc de djnz gd_lop ;-------------------- set fin & start inside go_dim ;в скопированной карте ставим код старт и цель gc_ctar ld bc,0 ld a,c sub 4 ld c,a call gc_ccrd ld (hl),128; target cod gc_csrt ld bc,0 ld a,c sub 4 ld c,a call gc_ccrd ld (hl),s_cod; start cod inc hl ld (hl),s_cod inc hl ld (hl),s_cod inc hl ld (hl),s_cod ld lx,s_cod ;-------------------- main () gc_main call gc_sway jr nz,gc_found dec a ld (go_way),a ; #ff ret ;----------------- make way ; если нашли цель... gc_found exx ld hl,gcb_way-2 exx ld c,lx inc lx inc lx ld de,34 gc_b_lp dec c call gc_lb jr z,gcb_fin call gc_rb jr z,gcb_fin call gc_db jr z,gcb_fin call gc_ub jr z,gcb_fin inc c ld a,lx cp c ret c jr gc_b_lp+1 ;----------------- found iteration (-1) gcb_fin ld a,b exx ld (hl),a dec hl exx ld (hl),#81 jr gc_b_lp ;----------------- make normal way_map gc_reway pop af ld a,b exx ld (hl),a ld de,go_way gcrw_lp ld b,(hl) ld a,b cp #ff jr z,gcrw_end call gcw_sub ld (de),a inc hl inc de jr gcrw_lp gcrw_end ld (de),a jp rewayer ;----------------- invert way_step gcw_sub ld a,"l" cp b ld a,"r" ret z cp b ld a,"l" ret z ld a,"u" cp b ld a,"d" ret z ld a,"u" ret ;----------------- back_search gc_lb ld b,"l" dec hl ld a,(hl) cp s_cod jr z,gc_reway cp c ret z inc hl ret ;----------------- gc_rb ld b,"r" inc hl ld a,(hl) cp s_cod jr z,gc_reway cp c ret z dec hl ret ;----------------- gc_ub ld b,"u" and a sbc hl,de ld a,(hl) cp s_cod jr z,gc_reway cp c ret z add hl,de ret ;----------------- gc_db ld b,"d" add hl,de ld a,(hl) cp s_cod jr z,gc_reway cp c ret z and a sbc hl,de ret ;-------------------- is way being ? ;if no way then a=0 gc_sway ld a,lx ld hl,gc_wdim+35 ld bc,679 gc_cpir cpir jr z,gc_l1 inc lx ld a,gc_mint cp lx jr nz,gc_sway xor a ret ;----------------- i_cod founded gc_l1 push hl,bc dec hl ld (element),hl call gc_left call gc_right call gc_down call gc_up pop bc,hl ld a,lx jr gc_cpir ;-------------------- step left gc_left element equ $+1 ld hl,0 dec hl ld a,(hl) or a ret p cp 128 jr z,glo_end ld a,lx inc a ld (hl),a ret ;-------------------- step right gc_right ld hl,(element) inc hl gc_v_jr ld a,(hl) or a ret p cp 128 jr z,glo_end ld a,lx inc a ld (hl),a ret ;-------------------- step down gc_down ld hl,(element) ld bc,34 add hl,bc gcu_jr push hl call gc_c_4b pop hl jp m,gc_c_ok ret ;-------------------- step up gc_up ld hl,(element) ld bc,34 and a sbc hl,bc jr gcu_jr ;-------------------- up or down way is ok! gc_c_ok ld a,128 ex af,af' ld a,lx inc a !assm 4 ex af,af cp (hl) jr z,glo_end ex af,af' ld (hl),a inc hl !cont ret ;-------------------- check 4 bytes gc_c_4b ld a,(hl) inc hl and (hl) inc hl and (hl) inc hl and (hl) ret ;-------------------- it's start !!! glo_end pop de,de ,de or a ret ;----------------- in bc- r (x,y) > out hl- adr of ... gc_ccrd inc c inc b ld a,b ld b,c ld hl,gc_wdim gc_mlp ld de,34 add hl,de djnz gc_mlp add a,l ld l,a ld a,0 adc a,h ld h,a ret !assm !off ;-------------------- in hl- il in dim> out de- x,y gc_c_el ld de,0 ld bc,34 and a gc_dlp sbc hl,bc jr c,dv_end jr z,dv_end+1 inc e jr gc_dlp dv_end add hl,bc ld d,l dec e dec d ret !cont ;----------------- very fatly array gc_wdim ds 748,#3030 go_way ds 50,#ffff gcb_way
вначале карта локации копируется в буфер, равный размером ей самой, чтоб не обосрать ее родимую своими итерациями. как видно не вооруженным взглядом, массивно юзается команда cpir, так, я думаю, быстрее получается, хотя я мало чего оптимизил, итак времени утрахал на саму процу. если ты все-таки удосужился прочесть ту статью, про которую я упомянул ранее, то тебе, друг мой, все будет ясно... командой cpir ищем итерацию, проверяем ее на вшивость, потом ищем другую и так до конца массива. я думаю, все предельно ясно. когда весь массив обработан (забит дерьмом), начинаем обратный отсчет, пока не приходим к цели. делается это все довольно мудово, и даже заметно в игре, хотя int, конечно, не стопорится >: -> максимальное время на расчет пути уходит, когда этого пути просто нет. тогда массив 32х20 элементов полностью замуживается и у меня это отнимает аж 13 прерываний, то бишь 900 000 тактов !!! мудово...
так как предметы на локации у меня являются непроходимыми, то при указании стрелкой на них выбирается уже заранее забитые координаты допустимого места, куда может быть просчитан путь для моего мудака. т.е. если ты выберешь, например, вышибалу, то программа определит, что ты выбрал предмет, и возьмет из таблицы координаты для него (точнее для героя, чтоб подойти к предмету) и подсунет их считалке пути, а не реальные координаты курсора, как это было бы иначе.
и еще... проца хождения героя у меня сделана таким образом, что она работает через команды (понятные только ей, of couse). в специальном буфере ей передается строка команд, которые надо выполнить, типа: "l, 10, u, 2, r, 3, ff" - это означет, что надо сделать 10 шагов влево, потом 2 вверх и затем 3 шага вправо, #ff - end. все просто, но зато очень удобно, теперь переделывая движок под цветные спрайты, я просто поменяю значения в таблице размеров спрайтов и все! так что не удивляйся значениям типа "l" или "d" в вышеприведенном коде.
также сделаны и другие процы, так что прошу мотать на ус...
итак, пипл, прошло довольно много времени с момента написания первой части статьи, поэтому можете считать все вышенаписанное полным отстоем. "а что не отстой ?" - спросишь ты меня, мой дорогой друг. все, что я или ты сделал является отстоем, т.к. ты это уже сделал! а вот то, чего мы пока не можем - по сути и является для нас самоцелью. тут недавно узнал такую вещь - маску для спрайта можно хранить черезстрочно, т.е. в два раза меньше! такая простая и крутая идея, а сама по себе ни хрена ни пришла, эх, если б раньше знать... для тех, кто не понял: у маски хранятся только четные или, наоборот, нечетные линии. при выводе недостача компенсируется дублированием линий. все просто - а спрайт занимает аж в 1, 5 раза меньше места (например, при размере 16 кило имеем экономию аж в 6 кило! ). я думаю эта фишка давно известна гейммэйкерам, но я лично благодарен volgasoft за данную идею. можно с этой фигней экскрементировать как угодно, может у кого вообще получится какой-нибудь рулез.
ща ant и vaniac перерисовывают с нуля графику для full shit , и т.к. она вся цветная, то соответственно мне пришлось столкнуться с рядом проблем...
спрайты рисуются спец-методом (рис.1):
cпрайт рисуется какими угодно инками и паперами, но по границе должон не использовать цветов, это окантовка позволяет квадратную атрибутную графику превратить во вполне приемлемую картину.
на рис.2 показана суть метода:
все геморы (от слова "геморой") начинаются, когда всю эту туеву хучу кусочков надо вывести на экран как единое целое. тут, в принципе, нет ничего особо сложного, но я сделал так:
1. все "куски" представляют собой отдельные спрайты, цветные выводятся как спрайт с атрибутами (put), а окантовка как простые (lines) по or-методу (правда можно и по маске, но тактов жрать больше будет, а эффектом не особо отличается).
2. соответственно пишем (или берем) програмку для вывода спрайтов данного формата (хранить и выводить можете как угодно, у меня таким методом: 8 линий данных, потом для них линия атрибутов, снова 8 линий...) и делаем менеджер...
3. "куски" одного спрайта хранятся у меня в одном массиве: +0 x, y (у первого всегда по нулям) +2 x_size, y_size +4 смещение до следующего спр. (nn) +6 тип спрайта (attr, lines... = 0, 1, ...) +7 сам спрайт .. ............ .. ............
+nn x, y относительно самого первого спр.
nn+1 x_size, y_size...
... и так далее...
вот теперь пишем менеджер, который будет все эти массивы разгребать и решать, какой выводилкой рисовать следующий "кусок" и где его рисовать (переводить относительные корды в реальные).
понятно, что все это будет работать не так быстро, как хотелось бы, но вопервых, вполне нормально, а во-вторых оптимизация не знает границ ! можно уже готовые массивы конвертить в другой формат таким образом, чтобы не пересчитывать для каждого "куска" корды. мне лично в лом, но если будет тозить, то, конечно, я это сделаю... а так, можно конвертить уже готовые массивы "кусков" так, чтобы не пересчитывать каждый раз корды для них, ясно ? ну ладно, на хрен, достало писать, пока!