Из журнала Adventurer#11,
Ярославская область, г.Рыбинск, 17.08.2000
DEMON/XPC/CPU
GAME MAKING #2
Приветствую тебя, забредший сюда...
Речь здесь опять ( смотри ADV#10 ) о го-
веном гаме-макинге >:->
Ну, начнем со спрайт-выводилок, все
просто и ясно - бывают они:
- с попиксельными координатами вы-
вода и с знакоместовыми;
- с масочкой и без масочки;
- с атрибутами и без.
Дальше идут комбинации вышеперечис-
ленной лажи. Делить на виды по способу
организации вывода (зависит от структуры
хранения инф-ции в спрайтах) я не буду,
все равно все не перечислить, даже не
помню, как хранились спрайты в сраном
спрайт генераторе от THD (козлы), кото-
рый был для меня первой программой тако-
го рода.
Несколько слов о выводилках спрай-
тов: по моему мнению они должны соответ-
ствовать определенным условиям, напри-
мер, для меня не нужны были stack-fast
'овые, и до охереннных размеров раздек-
ранченные, т.к. юзались оба экрана, то
выводилка должна была сидеть в основной
памяти Спека (#5b00-#bfff), а кроме нее
еще много чего там сидело.
Расскажу о трех выводилках; по Y
они пиксельные, по X две из них знакоме-
стовые и одна пиксельная.
1. Х - ЗНАКОМЕСТОВАЯ БЕЗ МАСКИ
Тут все стандартно и без всяких
гребаных наворотов со стеком и др. при-
чудами, чем проще, тем меньше #бли. Но
если у тебя есть желание трахаться с
прерываниями и трассировать стек, то
флаг тебе в руки...
Итак 32 LDI - это rulez, всего 16
тактов на байт (и на моем четно-тактовом
Скорпе тоже).
Задаем координаты в DE, размер
спрайта в BC и сам спрайт в HL, делаем
CALL ssp, и он на экране (если задано не
правильно, то на экране скорее всего
появится говно). Процедурка эта ничего
не проверяет и ничего не возвращает,
т.к. это низший уровень ядра проги. Хо-
чешь - переделай или вообще лучше сделай
свою, будет чем гордиться...
ВНИМАНИЕ! В листингах присутствует
ненормативная лексика - директивы
XAS 'a.
;(C) DEMON/XPC
;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 .
Насчет этих двух проц действует
следующее правило: ,
"ЛУЧШЕ ШИРЕ И КОРОЧЕ, ЧЕМ УЖЕ И
ДЛИНHЕЕ" , т.е. спрайт размером Х=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,S-
pace" и движения мыши (на ПЦ - эмулях
какой-то голюн с нашим драйвером: ПЦ
МАСТ ДАЙ);
- идет обращение к проце под именем
INT_GFX. Это и есть разные фишки, сос-
тоит в основном из кучи векторов, кото-
рые меняются в зависимости от действий
геймера, например: появляется менюха,
где крутится предмет (надо отрубить лам-
почку на фонаре, а потом повесить вра-
щающийся предмет, а если вызвать карман,
то 4-е предмета; мультики тоже играются
по INT) или диалоговое окно, все по INT
на х..;
- запоминается кусок в новом месте,
если было перемещение (иначе в старом),
рисуется курсор, после всех наворотов,
чтоб не попасть под какой-нибудь спрайт
или текст;
- в последнюю очередь играется му-
зон и для прикола меняются атрибуты на
светофоре;
- восстанавливаем страницу памяти,
переменную how_scr и назад.
Первый глюк возник, когда я решил
все заоптимизить: убрал собственные про-
цы вывода графики из прерываний, чтоб
все делалось через единые процы. И тут
началось... На экране стало появляться
всякое дерьмо, без всякой закономернос-
ти, иногда вообще все висло или сбрасы-
валось. А при трейсе все работало без
проблем. Такие глюки ненавижу, т.к. при-
чина сразу-то и не ясна. А вся херня бы-
ла из-за того, что в старом варианте вы-
водилки сохранялись локальные переменные
(размер спрайта) и при попадании на INT
они киллились, т.к. INT тоже юзал эти
процы. ВЫВОД: если процу юзают все, то
при ее работе все переменные надо хра-
нить на регистрах или толкать их в стэк,
иначе в прерываниях придется сохранять
все переменные.
Второй глюк возник с переключением
экранов, но об этом я, по-моему, уже го-
ворил...
В общем, если все в проге тщательно
проверить, то глюков обычно возникает
мало и не советую вешаться на IM 1, хотя
кому как нравится, хоть на IM 0 работай-
те >:->
Теперь о структуре "Full Shit" ,
которая оставляет желать лучшего, но что
есть, на том и кашу сварим...
ПОЯСНИЛОВА
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...
... и так далее...
Вот теперь пишем менеджер, который
будет все эти массивы разгребать и ре-
шать, какой выводилкой рисовать следую-
щий "кусок" и где его рисовать (перево-
дить относительные корды в реальные).
Понятно, что все это будет работать
не так быстро, как хотелось бы, но вопе-
рвых, вполне нормально, а во-вторых -
оптимизация не знает границ ! Можно уже
готовые массивы конвертить в другой фор-
мат таким образом, чтобы не пересчиты-
вать для каждого "куска" корды. Мне лич-
но в лом, но если будет тозить, то, ко-
нечно, я это сделаю... А так, можно кон-
вертить уже готовые массивы "кусков"
так, чтобы не пересчитывать каждый раз
корды для них, ясно ?
Ну ладно, на хрен, достало писать,
пока!