3. ПОСТРОЕНИЕ ИГРОВОГО ПРОСТРАНСТВА
Для того, чтобы написать хорошую игрушку, прежде всего нужно тщательно продумать сюжет, и только потом приступать к созданию спрайтов, необходимых для реализации ваших идей.
Из чего состоит игровое пространство
На смену одним играм приходят другие, количество сюжетов безгранично - но, как и все на свете, компьютерные игры поддаются классификации.
Большое количество компьютерных игр создано по сюжетам приключенческих фильмов и книг: Robocop, Rambo, Empire, Batman, Death Star и т. д. В основу других положены распространенные «настольные» игры - Chess, Poker, Draught. Компьютер позволяет смоделировать и логические игры типа Tetris, Petris, Sokoban, Bolder Dash и другие. Многие программы предлагают вам стать участником космических сражений (Elite, Star Raiders, R-Type, Star Fox), воздушных боев (MIG-29, Spitfire, F-16, Flying Shark, River Raid) или выполнить рискованные задания (Into The Eagles Nest, Saboteur, Bruce Lee, Flying Dragons). Получили распространение игры-имитаторы спортивных состязаний - Emlyn Huger Soccer, Basket Master.
B большинстве случаев игровое пространство может быть разбито на три существенно отличающихся друг от друга части. К первой отнесем все неподвижные элементы изображения, имитирующие небо, землю, воду, космическое пространство со звездами, и «мелкие» объекты - здания, корабли, деревья, мосты и т. д. В дальнейшем все это для краткости будем называть пейзажем.
Вторую часть составляют подвижные элементы изображения, на которые вы не можете оказывать управляющих воздействий. Как правило, к этой группе относятся изображения ваших противников, например, силуэт танка в игре Tanx или автомобили в Wee Le Mans.
Наконец, к третьей части игрового пространства обычно относится одно изображение (или, реже, несколько), действиями которого вы можете управлять с помощью клавиатуры или джойстика. Так, в игре Капе это ковбой, преодолевающий различные препятствия и ведущий борьбу с многочисленными врагами. В игре Tetris - падающие на дно стакана геометрические фигурки, отличающиеся друг от друга цветом и формой. Наконец, в игре Cabal и многих других управляемым элементом является перекрестье прицела.
Итак, представим себе, что выбрана тема игры, тщательно проработан сюжет (мысленно определены все элементы, их цвет, размер и траектория движения), четко сформулирована цель и намечены способы ее достижения. Можно приступать к программированию.
Создание графических объектов
За исключением самых первых текстовых программ жанра Adventure, ни одна игра не обходится без множества подвижных графических объектов - спрайтов, поэтому давайте сделаем еще один шаг вперед по пути создания хороших игровых программ - научимся создавать такие объекты, а затем заставим их двигаться.
Каким образом получить изображение на экране? Во-первых, можно воспользоваться операторами PLOT и DRAW. Но при этом, как вы понимаете, программа выйдет достаточно громоздкой и «медлительной». Если не верите -введите и выполните программку, рисующую на экране ракету:
10 PLOT 120,80: DRAW 3,-3 20 DRAW 0,-11: DRAW 4,-4 30 DRAW 0,-5: DRAW -4,4 40 DRAW -6,0: DRAW -4,-4 50 DRAW 0,5: DRAW 4;4 60 DRAW 0,11: DRAW 2,2
Нетрудно догадаться, что таким способом можно создавать только простейшие изображения, а для рисунков с обилием мелких деталей этот рецепт никуда не годится. Поэтому программисты обычно заранее заготавливают картинки, называемые спрайтами, а затем вводят их в память в виде блока данных (набора чисел). Нам остается выяснить, каким образом можно получить такой блок данных и как потом превратить его в изображение на экране.
Идея здесь довольно проста. Любое изображение на экране (спрайт) можно разбить на несколько маленьких спрайтиков. А любые символы (буквы, цифры) тоже, по сути, маленькие спрайтики, размером в одно знакоместо. И выводить их на экран проще простого - с помощью оператора PRINT. Значит, если нам удастся изменить некоторые символы таким образом, чтобы при выводе в совокупности они составили нужную картинку, то задачу можно будет считать решенной.
По счастью, разработчики ZX Spectrum предусмотрели возможность изменения набора символов. Проще всего изменить символы, специально предназначенные для этого. Они так и называются: символы, определяемые пользователем - User Defined Graphics (UDG). Они выводятся на экран в режиме курсора [G] при нажатии клавиш от А до U. Режим курсора [G] включается (и выключается) одновременным нажатием клавиш Caps Shift и 9.
Теперь перейдем к главному вопросу: как закодировать изображение? Вы знаете, что символы строго вписываются в знакоместа экрана с размерами 8x8 точек - пикселей. Начертим на листе в клетку поле 8x8 клеток и изобразим на нем какую-либо фигуру, например, «гномика» (рис. 3.1).
Если заменить пустые клеточки нулями, а закрашенные - единицами, то мы
получим последовательность чисел в двоичном представлении:
00000000 00111000 01010100 00111000 00111000 00101000 01101100 00000000
Для записи двоичных чисел в Бейсике используется ключевое слово BIN. Например,
PRINT BIN 10010110
или
LET a=BIN 10010110: PRINT a
Наконец, нам остается занести полученный ряд чисел в память. Как это сделать? Наверное, для вас не будет новостью, что память компьютера представляет собой последовательность ячеек, предназначенных для хранения чисел. При этом каждая ячейка имеет свой адрес, только не дом и не улицу, а номер от 0 до 65535. Чтобы занести в память какое-то число, нужно выполнить оператор РОКЕ, после которого сначала указывается адрес ячейки, а затем, после запятой, -само число. Помните только, что записывать в память можно не любые значения, а лишь целочисленные из диапазона от 0 до 255. Эти числа в дальнейшем мы будем называть байтами, а каждый отдельный разряд двоичного числа - битом. Таким образом, байт состоит из восьми битов.
По каким же адресам расположить числа, составляющие изображение нашего «гномика»? Узнать адреса размещения символов UDG позволяет функция USR, аргументом которой должен быть символ нужной клавиши. Например, PRINT USR "S" напечатает на экране адрес символа, определяемого пользователем, который соответствует клавише S.
Теперь расположим, начиная с этого адреса, коды изображения «гномика» и затем выведем его на экран:
10 POKE USR "S", BIN 00000000 20 POKE USR "S"+1,BIN 00111000 30 POKE USR "S"+2,BIN 01010100 40 POKE USR "S"+3,BIN 00111000 50 POKE USR "S"+4,BIN 00111000 60 POKE USR "S"+5,BIN 00101000 70 POKE USR "S"+6,BIN 01101100 80 POKE USR "S"+7,BIN 00000000 90 PRINT "S"
Конечно же, вовсе не обязательно записывать в программе длинные блоки данных из двоичных чисел. Лучше заранее привести их в к десятичному виду. Сделать это можно двумя способами: воспользовавшись ключевым словом BIN, либо подсчитать значения кодов вручную, просуммировав разряды числа. Например:
00111000 => 0S128 + 0S64 + 1S32 + 1S16 + 1S8 + 0S4 + 0S2 + 0S1 = 56
Теперь приведем пример программы, записывающей изображение нашего
«гномика» в пользовательский символ, который соответствует клавише А.
10 FOR 1=0 ТО 7
20 READ N: POKE USR "A"+I,N
30 NEXT I
40 DATA 0,56,84,56,56,40,108,0
10 - начало цикла, состоящего из восьми повторяющихся процедур (по числу запоминаемых байтов);
20 - функция USR "А" возвращает адрес символа UDG, соответствующего букве А, а оператор РОКЕ записывает в ячейку памяти с этим адресом значения переменной N, которые последовательно считываются оператором READ из списка данных, следующих за оператором DATA;
30 - конец цикла по переменной I;
40 - коды изображения «гномика», приведенные к десятичному виду.
После выполнения этой мини-программы в память компьютера будут введены коды «гномика». И если потребуется вывести на экран его изображение, например,
в знакоместо с координатами (10, 15), достаточно набрать в программе дополнительную строку 50 PRINT AT 10,15;"A" причем клавишу А необходимо нажимать в режиме курсора [G].
Предположим теперь, что нам требуется закодировать, а затем вывести на экран более сложный рисунок, например, изображение реактивного самолета.
Рисунок состоит из пяти знакомест. Обозначим их буквами А, В, С, D и Е (рис. 3.2), а затем закодируем каждое способом, описанным в предыдущем примере.
Программа, осуществляющая запись изображения самолета в память, должна выглядеть примерно так:
Программа 7. Спрайт "реактивный самолет".
10 BORDER 1: PAPER 1: INK 6 20 CLS
30 FOR N=0 TO 39 40 READ S
50 POKE USR "A"+N,S 60 NEXT N 70 GO TO 300
100 DATA 24,24,24,24,24,24,60,60: REM A 110 DATA 60,36,36,60,60,60,90,90: REM B 120 DATA 12,13,15,127,255,255,15,0: REM C 130 DATA 219,219,219,219,219,219,255,60: REM D 140 DATA 48,176,240,254,255,255,240,0: REM E 300 PRINT AT 10,10;"A" 310 PRINT AT 11,10;"B" 320 PRINT AT 12,9;"CDE"
10 - установка атрибутов экрана;
20 - очистка экрана;
30 - начало цикла, в котором должно быть прочитано 5x8 = 40 кодов (с 0 по 39 включительно);
40 - в процессе работы цикла оператор READ считывает в переменную S поочередно все данные из списка DATA;
50 - в этой строке происходит следующее. Число, присвоенное переменной S, теперь переписывается в ячейку памяти компьютера, имеющую адрес USR "A" + N. При N = 0 этот адрес составляет 65368. Так как в цикле по N величина USR "A" + N все время увеличивается на единицу, то требуемые коды будут последовательно записаны в ячейки памяти с адресами 65368, 65369, 65370, ...
60 - конец цикла Теперь можно либо вывести изображение самолета на экран (как сделано у нас), либо обратиться к другим частям программы;
70 - переход на строку 300, с которой начинается вывод на экран изображения самолета;
100... 140 - здесь хранятся коды, соответствующие пяти знакоместам изображения самолета. Заметим, что мы могли бы записать все данные одним оператором DATA, но при этом пострадала бы «читабельность» программы;
300 - вывод на экран в знакоместо с координатами (10, 10) фрагмента А реактивного самолета. (Еще раз напомним, что при наборе строки клавишу А следует нажимать в режиме курсора [G]);
310 - вывод на экран фрагмента В;
320 - вывод на экран фрагментов С, D и Е.
Итак, в нашем распоряжении появилась программа, которую можно использовать как фрагмент сложной компьютерной игры. Чтобы убедиться в этом, сделаем несколько заготовок с расчетом использовать их в будущем. Вначале создадим спрайт вертолета с вращающимся винтом (рис. 3.3). Здесь нам понадобятся две картинки, изображающие вертолет
с разным положением винта. Если быстро подменять одну другой на экране, то появится иллюзия движения - как в мультфильме.
Закодируем два этих изображения, заменив в предыдущей программе строки с оператором DATA на такие:
100 DATA 0,0,135,0,120,137,15,39: REM A 110 DATA 0,0,255,2,127,201,201,255: REM B 120 DATA 0,0,255,0,192,112,24,12: REM C 130 DATA 3,1,0,0,0,0,0,0: REM D 140 DATA 255,255,127,63,4,255,0,0: REM E 150 DATA 254,254,252,248,34,252,0,0: REM F 160 DATA 0,0,32,0,120,127,15,135: REM G 170 DATA 0,0,7,2,127,201,201,255: REM H 180 DATA 0,0,0,0,192,112,24,12: REM ! Кроме того, максимальное значение переменной N в строке 30 программы нужно исправить на 71. Теперь можно выводить спрайт на экран, придерживаясь следующей последовательности символов: ABC - верхняя часть картинки, DEF -нижняя часть, GHI - верхняя часть, но с винтом, повернутым в другую сторону.
Нижняя часть вертолета не изменяется, поэтому во второй картинке используются те же символы DEF. Попытайтесь написать программку, рисующую вертолет с вращающимся винтом, самостоятельно. Если же у вас возникнут проблемы, загляните в главу 5 и посмотрите, как это сделано в программе ВЕРТОЛЕТ
Теперь нарисуем спрайт с изображением самолета (рис. 3.4), который в пятой главе мы заставим летать по всему экрану, участвуя в воздушном бою. Операторы DATA здесь будут такими:
100 DATA 0,0,0,0,255,0,0,0: REM А
110 DATA 0,0,0,0,255,255,6,4: REM В
120 DATA 60,126,255,255,255,255,126,60: REM С
130 DATA 0,0,0,0,255,255,96,32: REM D
140 DATA 0,0,0,0,255,0,0,0: REM E
150 DATA 0,0,0,24,24,24,24,24: REM F
Управляющая переменная N принимает максимальное значение 55. Для вывода спрайта на экран оператором PRINT сначала рисуются крылья и фюзеляж, изображаемые рядом символов UDG, соответствующих буквам А, В, С, D и Е, а затем точно над С ставится символ F, хранящий изображение хвоста самолета.
Ниже приведен листинг еще одной небольшой программы, создающей на экране подвижные спрайты.
Программа 8. ПОДВИЖНЫЕ СПРАЙТЫ.
10 BORDER 1: PAPER 1: INK 6: CLS 20 FOR 1=0 TO 47 30 READ N: POKE USR "A"+I,N 40 NEXT I
55 DATA 0,2,3,7,15,19,83,126
56 DATA 0,64,64,224,240,200
57 DATA 202,126,127,115,120,31
58 DATA 15,4,4,28,254,206,30
59 DATA 248,240,32,56,0,127
60 DATA 127,120,27,15,4,28,0
61 DATA 254,254,30,216,240,32
62 DATA 32,56
100 PLOT INK 4;60,71: DRAW INK 4;135,0 105 LET C=3
110 PAUSE 50: IF INKEY$=" " THEN GO TO 200 120 PRINT AT 10,14;"AB";AT 10,18; INK C;"ПП" 130 PRINT AT 11,14;"CD";AT 11,18; INK C;"AB" 140 PRINT AT 12,14;"ПП";AT 12,18; INK C;"E£" 150 PAUSE 50: IF INKEY$=" " THEN GO TO 200 160 PRINT AT 10,14;"ПП";AT 10,18; INK C;"AB" 170 PRINT AT 11,14;"AB";AT 11,18; INK C;"CD" 180 PRINT AT 12,14;"EF";AT 12,18; INK C;"ПП" 190 GO TO 110 200 CLS
Поскольку все операторы этой программы рассматривались нами ранее, попытайтесь самостоятельно разобраться в том, как она работает. Заметим лишь, что нажатие любой клавиши приводит к ускорению движения фигурок. Это связано с прекращением действия оператора PAUSE. А если нажать клавишу Space, то экран очистится и программа завершит свою работу.
Спрайт-генератор
Кодирование изображения спрайта вручную - хотя и полезная, но довольно утомительная работа. У тех, кому приходится создавать много спрайтов, возникает естественный вопрос: нельзя ли сам компьютер заставить заниматься кодированием, причем в удобной для пользователя форме? Эта мысль была реализована в программе генератора спрайтов. Поскольку эта программа представляет самостоятельный интерес как пример сервисной программы, облегчающей программисту жизнь, то
рассмотрим ее, как и все другие, - подробно. Однако предупредим вас заранее, что в этой программе встречается ряд операторов и приемов, о которых еще не говорилось и речь о которых пойдет в следующих главах. Поэтому начать досконально разбираться с некоторыми ее фрагментами лучше после изучения пятой главы книги.
После ввода программы и ее пуска на экране появится рабочее поле (рис. 3.5), каждая клеточка которого соответствует пикселю изображения спрайта. Поле разделено на шесть квадратов размером 8x8 клеток. Каждый квадрат изображает одно знакоместо спрайта. По клеткам поля может перемешаться курсор, который выполняет роль пера. Об управлении программой можно узнать, нажав клавишу Н (HELP - подсказка). Курсор перемещается с помощью клавиш Q - вверх, А - вниз, О - влево и Р - вправо. Точки ставятся (или удаляются) нажатием клавиши М. После нажатия клавиши N справа от поля по вертикали расположатся 8 чисел, соответствующих кодам знакоместа, номер которого виден тут же под словом SYMBOL. Эти числа затем можно записать в оператор DATA и использовать в нужном месте игровой программы подобно тому, как мы делали это для спрайтов «гномика», вертолета, реактивного самолета и т. д. Когда очередное знакоместо будет закодировано, нажмите клавишу Enter - и курсор переместится в следующий квадрат-знакоместо. При необходимости, квадрат, в котором находится курсор (текущее знакоместо), можно очистить от изображения, нажав клавишу С.
Прежде, чем привести текст программы, необходимо сказать вот о чем. В ней не предусмотрена возможность остановки и выхода в Бейсик. Но если вы уже набрали и запустили программу, а записать ее на магнитофон или диск забыли, то не хватайтесь за голову и не тянитесь к кнопке сброса. Разработчики ZX Spectrum позаботились о возможности остановки любой программы на Бейсике, если только она специально не защищена. Одновременное нажатие клавиш Caps Shift и Space (Break) позволяет прервать работу программы в любой момент, не дожидаясь ее завершения. При этом на экране появится сообщение BREAK into program.
Теперь рассмотрим саму программу.
Программа 9. СПРАЙТ-ГЕНЕРАТОР.
20 DIM g$(6,8,8): DIM c(8): GO SUB 6000 30 LET a$="SYMBOLПП" 40 GO SUB 8000
50 BORDER 0: PAPER 0: INK 6: CLS 60 PRINT AT 0,6;"S P R I T E S" 70 INK 3
80 PLOT 40,165: DRAW 120,0 90 INK 4
100 FOR n=1 TO 8
110 PRINT AT n+3,0;g$(1,n);
120 PRINT INK 7;g$(2,n);
130 PRINT INK 4;g$(3,n)
140 NEXT n
150 INK 7
160 FOR n=1 TO 8
170 PRINT AT n+11,0;g$(4,n);
180 PRINT INK 4;g$(5,n);
190 PRINT INK 7;g$(6,n)
200 NEXT n
210 INK 7
220 PLOT 0,15: DRAW 63,0
230 PLOT 128,15: DRAW 64,0: DRAW 0,128
240 PLOT 197,15: DRAW 0,128
250 DRAW 54,0: DRAW 0,-128
260 DRAW -54,0
270 PLOT 64,15: DRAW 63,0
280 INK 2
290 PLOT 213,37: DRAW 29,0: DRAW 0,21 300 DRAW -29,0: DRAW 0,-21 310 INK 6
320 PRINT AT 18,25;"H-HELP" 330 FOR i=0 TO 1 340 FOR j=0 TO 2
350 PRINT AT i*18+3,j *8+3;i*3+j+1 360 NEXT j 370 NEXT i
400 REM -------------------
410 LET x=1: LET y=1: LET s=1 420 LET px=-1: LET py=3 430 LET c=4: LET s1 = 1
450 REM -------------------
460 GO SUB 2000 470 LET x1=x: LET y1=y 480 LET k$=INKEY$
490 IF k$="P" OR k$="p" THEN LET x=x+1: GO TO 600 500 IF k$="O" OR k$="o" THEN LET x=x-1: GO TO 600 510 IF k$="Q" OR k$="q" THEN LET y=y-1: GO TO 600 520 IF k$="A" OR k$="a" THEN LET y=y+1: GO TO 600
530 IF (k$="M" OR k$="m") AND g$(s,y,x)="A" THEN LET g$(s,y,x)="B": PRINT AT py+y,px+1; INK c;g$(s,y): GO SUB 7310: GO TO 600
540 IF (k$="M" OR k$="m") AND g$(s,y,x)="B" THEN LET g$(s,y,x)="A": PRINT AT
py+y,px+1; INK c;g$(s,y):GO SUB 7310: GO TO 600
550 IF CODE k$=13 THEN GO SUB 7210: LET s1=s1+1: IF s1=7 THEN LET s1=1
560 IF k$="N" OR k$="n" THEN GO SUB 2000: GO TO 600
570 IF k$="C" OR k$="c" THEN GO SUB 7200: GO TO 3000
580 IF k$="H" OR k$="h" THEN GO SUB 5000: GO TO 50
590 GO TO 620
600 REM -----------
610 GO SUB 7110
620 IF x>8 OR x<1 OR y>8 OR y<1 THEN LET x=x1: LET y=y1
630 PRINT AT y+py,x+px; OVER 1; INK 8; PAPER 1;" "
640 IF k$<>"" THEN PRINT AT y1+py,x1+px; OVER 1; INK 8;PAPER 0;" "
650 LET s=s1
660 GO SUB 1010: GO TO 470 1000 REM -----------
1010 IF s=1 THEN LET c=4: LET px=-1: LET py=3: RETURN 1020 IF s=2 THEN LET c=7: LET px=7: LET py=3: RETURN 1030 IF s=3 THEN LET c=4: LET px=15: LET py=3: RETURN
1040 IF s=4 THEN LET c=7: LET px=-1: LET py=11: RETURN 1050 IF s=5 THEN LET c=4: LET px=7: LET py=11: RETURN 1060 IF s=6 THEN LET px=15: LET py=11: LET c=7 1070 RETURN
2000 REM ---- CODE ----
2010 GO SUB 7000 2020 FOR n=1 TO 8 2030 LET o=0
2040 LET ad=(USR CHR$ (66+s))+(n-1) 2050 FOR m=1 TO 8
2060 IF g$(s,n,m)="B" THEN LET o=o+c(m) 2070 NEXT m 2080 POKE ad,0 2090 INK 4
2100 PRINT AT n+4,26;a$(n);"ПППП" 2110 PRINT AT n+4,28;o 2120 NEXT n 2130 INK 7
2140 PRINT AT 15,27;"CDE" 2150 PRINT AT 16,27;"FGH" 2160 INK 3
2170 PRINT AT 12,26;s 2180 RETURN
3000 REM ---- CLEAR ----
3010 LET b=1
3020 PRINT #0;AT 1,2; INK 2; BRIGHT b;"CLEAR SYMBOL ";s;"?(Y/N)"
3030 IF INKEY$="N" OR INKEY$="n" THEN BEEP .01,20: GO SUB 3200: GO TO 600
3040 IF INKEY$="Y" OR INKEY$="y" THEN BEEP .01,0: GO SUB 3200: GO TO 3100
3050 LET b=NOT b
3060 GO TO 3020
3100 FOR m=1 TO 8
3110 LET g$(s,m)="AAAAAAAA"
3120 PRINT AT m+py,1+px; INK c;g$(s,m)
3130 NEXT m
3140 GO SUB 1000
3150 GO SUB 2000
3160 GO TO 600
3200 PRINT #0;AT 1,2;"ППППППППППППППППППППП": REM 21 Space 3210 RETURN
5000 REM ---- HELP ----
5010 LET C=0
5020 BORDER 1: PAPER 1: INK 6
5030 INK 5: CLS
5040 PLOT 0,0: DRAW 0,175
5050 DRAW 255,0: DRAW 0,-175
5060 DRAW -255,0
5070 INK 7
5080 PRINT AT 8,2;"P - RIGHT";TAB 14;"C - CLEAR SYMBOL"
5090 PRINT AT 10,2;"0 - LEFT";TAB 14;"N - CODE SYMBOL"
5100 PRINT AT 12,2;"Q - UP";TAB 14;"ENTER - SYMBOL+1"
5110 PRINT AT 14,2;"A - DOWN";TAB 14;"M - SET/RESET"
5120 PRINT AT 20,4; INK 6;"Press any key to continue"
5130 PRINT AT 2,12; INK C;"H E L P"
5140 LET C=C+1: IF C=8 THEN LET C=0
5150 IF INKEY$="" THEN GO TO 5130
5160 BEEP .002,15
5170 RETURN
6000 REM ---- DATA 1 ----
6010 FOR n=1 TO 8 6020 LET c(n)=2~(8-n)
6030 NEXT n 6040 RETURN
7000 REM ---- BEEP 1 ----
7010 BEEP .0004,30 7020 BEEP .0004,40 7030 BEEP .0004,50 7040 RETURN
7100 REM ---- BEEP 2 ---7110 BEEP .0003,50 7120 BEEP .0003,30 7130 RETURN
7200 REM ---- BEEP 3 ----
7210 BEEP .005,15 7220 BEEP .004,10 7230 RETURN
7300 REM ---- BEEP 4 ----
7310 BEEP .003,25 7320 RETURN
8000 REM ---- GRAPHICS ----
8010 FOR n=0 TO 15
8020 READ s
8030 POKE USR "A"+n,s
8040 NEXT n
8050 FOR n=0 TO 48
8060 POKE USR "C"+n,0
8070 NEXT n
8080 FOR i=1 TO 6
8090 FOR n=1 TO 8
8100 LET g$(i,n)="AAAAAAAA"
8110 NEXT n
8120 NEXT i
8130 RETURN
9000 DATA 255,128,128,128,128,128,128,128 9010 DATA 0,127,127,127,127,127,127,127 20 - массив g$(6,8,8) описывает 6 знакомест спрайта площадью 8x8 точек, а с(8) содержит значения отдельных битов для кодировки изображения;
40 - обращение к подпрограмме, формирующей в определяемых пользователем символах, соответствующих клавишам А и В. графические элементы, изображающие выключенные (А) и включенные (В) пиксели: 50 - установка атрибутов экрана;
60...80 - вывод на экран названия программы и его подчеркивание; 90... 140 - печать трех увеличенных знакомест спрайта, расположенных в верхней части поля;
150...200 - печать трех знакомест нижней части спрайта;
210...270 - дорисовывание нижней и боковой частей рабочего поля, а также рамки справа от него;
280...300 - вычерчивание рамки красного цвета, в которой после кодировки будет размещаться изображение полученного спрайта целиком и в реальном масштабе;
320 - печать надписи, подсказывающей, что для получения страницы помощи нужно нажать клавишу Н;
330...370 - вывод на экран номеров фрагмента спрайта;
410...430 - установка начальных значений переменных: х, у - текущие координаты курсора внутри любого фрагмента (знакоместа спрайта); s - номер фрагмента; рх, ру - приращения координат х и у. изменяющиеся в зависимости от номера выбранного фрагмента; с -цвет фрагмента;
460 - обращение к подпрограмме, которая выводит в рамке справа от рабочего поля коды и, кроме того, рисует спрайт в реальном масштабе;
470 - запоминание «старых» координат;
480...520 - стандартная схема управления курсором с помощью клавиш. С такой схемой мы еще встретимся и подробно ее обсудим в пятой главе;
530, 540 - эти строки управляют включением и выключением пикселя, на который указывает курсор. Если при нажатии клавиши М пиксель выключен, то он включится, и наоборот. Оператор AND в этих строках означает, что операторы, следующие за THEN, будут выполняться только в том случае, если истинно И то, И другое условие (and по-английски значит «и»);
550 - при нажатии клавиши Enter происходит перемещение курсора в следующее знакоместо спрайта;
560 - кодировка текущего знакоместа (того, где стоит курсор);
570 - очистка текущего знакоместа;
580 - вызов подсказки;
620 - проверка на предмет выхода курсора за пределы знакоместа;
630 - вывод на экран курсора синего цвета;
640 - удаление курсора на старом месте, но только в том случае, если была нажата какая-либо клавиша;
1000...1060 - в зависимости от номера текущего знакоместа определяется цвет и величины приращений координат курсора;
2000 - начало подпрограммы кодировки текущего знакоместа спрайта, печати полученных кодов и вывода изображения спрайта в реальном масштабе;
2020 - начало цикла кодирования 8 байт текущего знакоместа;
2030 - в переменной о будет подсчитываться значение каждого байта;
2040 - расчет адреса в области UDG, куда будет записываться полученный байт. Попробуем разобраться со сложным выражением после знака равенства. В переменной cod хранится так называемый ASCII-код символа, соответствующего номеру знакоместа (s).
Для начала выясним, что же такое ASCII-код. Возможно, вы уже задавались вопросом, каким образом компьютер обрабатывает символьные значения, ведь микропроцессор может работать только с числами и не знает, что такое буквы. Поэтому всем символам (и даже ключевым словам Бейсика) соответствуют специальные числа - ASCII-коды. Например, буква В кодируется числом 66, которое и используется в нашей программе. Так как переменная s принимает значения от 1 до 6, то переменная cod = 66 + s будет равна 67...72, что соответствует кодам букв С, D, Е, F, G и Н. Функция CHR$ превращает код обратно в символ. Дальше все просто: функция USR возвращает адрес соответствующего символа UDG, к которому прибавляется номер кодируемого байта и полученный адрес записывается в переменную ad.
Теперь давайте вернемся немного назад и еще раз посмотрим на строку 550, Код символа перевода строки и клавиши Enter равен 13. Вы можете проверить это, введя строку
PAUSE 0: PRINT CODE INKEY$
и нажав клавишу Enter. Функция CODE - обратная по отношению к CHR$ и возвращает код символа (в данном случае - символа нажатой клавиши). Таким образом, условие CODE k$=13 в строке 550 выполняется при нажатии клавиши Enter;
2050 - начало цикла подсчета значения байта, которое определяется состоянием 8 битов;
2060 - если бит установлен (графический символ В изображает включенный бит), то переменная о увеличивается на значение соответствующего бита;
2070 - конец цикла кодирования одного байта;
2080 - полученный байт заносится в область UDG по ранее рассчитанному адресу ad;
2100 - печать слова SYMBOL по вертикали и стирание ранее напечатанных кодов;
2110 - печать новых значений кодов;
2120 - конец цикла кодирования байтов;
2130...2150 - вывод изображения спрайта в реальном масштабе;
2170 - печать номера кодируемого знакоместа;
2180 - выход из подпрограммы;
3000...3140 - очистка того знакоместа спрайта, в котором находится курсор. Не будем описывать эти строки подробно. Объясним лишь смысл функции NOT в строке 3050. Эта функция возвращает одно из двух возможных значений: 1, если аргумент равен нулю, и 0, если аргумент ненулевой Таким образом, короткая запись LET b = NOT b заменяет целых две строки. IF b = 0 THEN LET b = 1 и IF b<>0 THEN LET b = 0,
Далее идут подпрограммы, смысл строк которых вам уже должен быть понятен без лишних подсказок:
5000...5170 - печать странички помощи HELP;
6000...6040 - заполнение массива с(8), определяющего значения отдельных битов, которые равны степеням числа 2: 20= 1, 21 = 2, ..., 27= 128. Этот массив включен в программу для ускорения кодирования изображения;
7000...7320 - четыре подпрограммы звукового оформления;
8000...9010 - подпрограмма кодирования используемых в программе образов включенного и выключенного пикселей увеличенного в 8 раз изображения спрайта, а также очистка шести символов UDG для записи в них готового спрайта.