С для профессиональных программистов


Мультипликация на экране


Ключевым и наиболее впечатляющим моментом видеоигры является мультипликация. Мультипликация - основной отличительный признак видеоигр. Основной метод мультипликации прост: уничтожить изображение предмета и создать его вновь, но с некоторым небольшим смещением. Скорость этого процесса должна быть очень высокой. Это может быть обеспечено путем непосредственного доступа к видеопамяти дисплея, возможность которого описана в главе 4.

Для повышения качества изображения, быстродействия операций уничтожения и повторного изображения объекта используется операция "НЕ-ИЛИ" для двоичного кода каждой точки объекта на экране. Этот способ обеспечивает возможность быстрого перемещения спрайта по экрану, не меняя его цвет и размеры, и фактически не уничтожая в памяти терминала данные о его изображении.

Программа, отображающая на экране терминала спрайт, представляет собой некоторую модификацию функции display_object() из главы 4.

/* отображение объекта на экране */

void display_object(ob, sides,cc)

double ob[][4];

int sides,cc;

register int i;

for(i=0; i<sides; i++)

line((int)ob[i][0], (int)ob[i][1],

(int)ob[i][2], (int)ob[i][3], cc | 128);

Как вы могли убедиться, функция display_object() рисует все линии объекта, используя приведенную в главе 4 функцию line(). Заметим, что значение номера цвета складывается по схеме "ИЛИ" с числом 128 в команде установки старших битов. Это приводит к тому, что в функции mempoint(), используемой в функции line() для помещения изображения каждой точки, выполняется сложение по схеме "НЕ-ИЛИ" двоичного кода. Это позволяет спрайту всегда оставаться видимым независимо от собственного цвета и цвета фона.

Для демонстрации мультипликации введите в ваш компьютер следующую программу. Эта программа позволит вам перемещать спрайт (в виде маленького крестика размером 6x6 точек растра) по экрану, используя клавиши управления курсором. Если ваш компьютер не включает функцию bioskey(), то просмотрите главу 1 для определения версии компилятора, которая вам необходима.




#include "dos.h"

#include "stdio.h"

void mode(), line();

void mempoint(), palette();

void display_object(),update_object();

unsigned char read_point();

int sprite[2][4] =

3,0,3,5,

0,3,5,3

;

main()

union k

char c[2];

int i;

 key;

int deltax=0,deltay=0;

mode(4); /*m установка 4 режима графики CGA/EGA */

palette(0); /* палитра 0 */

display_object(sprite,2,1);

do

key.i = bioskey(0);

deltax=0;deltay=0;

if(!key.c[0]) switch(key.c[1])

case 75: /* влево */

deltay= -1;

break;

case 77: /* вправо */

deltay= 1;

break;

case 72: /* вверх */

deltax= -1;

break;

case 80: /* вниз */

deltax= 1;

break;

case 71: /* вверх и влево */

deltay= -1;

deltax= -1;

break;

case 73: /* вверх и вправо */

deltay= 1;

deltax= -1;

break;

case 79: /* вниз и влево */

deltay= -1;

deltax= 1;

break;

case 81: /* вниз и вправо */

deltay= 1;

deltax= 1;

break;

/* стирание текущей позиции спрайта */

display_object(sprite,2,1);

if (is_legal(sprite,deltax,deltay,2))

update_object(sprite,deltax,deltay,2);

/* перезапись спрайта в новую позицию */

displey_object(sprite2,1);

   while (key.c[0]!='q');

getchar();

mode(2);

/* Выбор палитры */

void palette(pnum)

int pnum;

union REGS r;

r.h.bh = 1; /* код 4-го графического режима */

r.h.bl = pnum;

r.h.ah = 11;

int86(0x10, &r, &r);

/* Выбор режима */

void mode(mode_code)

int mode_code;

union REGS r;

r.h.al = mode_code;

r.h.ah = 0;

int86(0x10, &r, &r);

/* Изображение линии заданного цвета с использованием

алгоритма Брезенхама */

void line(startx,starty,endx,endy,color)

int startx,starty,endx,endy,color;

register int t,distance;

int x=0,y=0,delta_x,delta_y;

int incx,incy;

/* Вычисление расстояния в обоих направлениях                                                     */

delta_x=endx-startx;

delta_y=endy-starty;

/* определение направления шага,

шаг вычисляется либо по вертикальной, либо по горизонтальной



линии                                                     */

if (delta_x>0) incx=1;

else  if (delta_x==0) incx=0;

else  incx= -1;

if (delta_y>0) incy=1;

else  if (delta_y==0) incy=0;

else  incy= -1;

/* определение какое расстояние больше */

delta_x=abs(delta_x);

delta_y=abs(delta_y);

if (delta_x>delta_y) distance=delta_x;

else distance=delta_y;

/* Изображение линии */

for (t=0; t<=distance+1; t++)

mempoint(startx,starty,color);

x+=delta_x;

y+=delta_y;

if (x>distance)

x-=distance;

startx+=incx;

if (y>distance)

y-=distance;

starty+=incy;

 

/* Запись точки в CGA/EGA */

void mempoint(x,y,color_code)

int x,y,color_code;

union mask

char c[2];

int i;

 bit_mask;

int i,index,bit_position;

unsigned char t;

char xor; /* "исключающее ИЛИ" цвета в случае его

изменения */

char far *ptr=(char far *) 0xB8000000; /* точка в

памяти CGA */ bit_mask.i=0xFF3F; /* 11111111 00111111 в

двоичном виде */

if (x<0 || x>199 || y<0 || y>319) return;

xor=color_code & 128; /* проверка, устанавливался ли

режим "исключающего ИЛИ" */ color_code=color_code & 127; /* маска старших битов */

/*  установка битовой маски и битов режима цвета

в правую позицию */

bit_position=y%4; /* вычисление нужной позиции

в байте */ color_code<<=2*(3-bit_position); /* сдвиг кода цвета

в нужную позицию */ bit_mask.i>>=2*bit_position; /* сдвиг битовой маски в

нужную позицию */

/* определение требуемого байта в памяти терминала */

index=x*40+(y%4);

if (x%2) index+=8152; /* если нечетный, используется

второй блок */

/* запись цвета */

if (!xor)   /* режим изменения цвета */

t=*(ptr+index) & bit_mask.c[0];

*(ptr+index)=t|color_code;

else

t=*(ptr+index) | (char)0;

*(ptr+index)=t & color_code;

/* чтение байта из оперативной памяти CGA/EGA */

unsigned char read_point(x,y)

int x,y;

union mask

char c[2];

int i;

 bit_mask;

int i,index,bit_position;



unsigned char t;

char xor; /* "исключающее ИЛИ" цвета в случае его

изменения */

char far *ptr=(char far *) 0xB8000000; /* точка в

памяти CGA */ bit_mask.i=3; /* 11111111 00111111 в

двоичном виде */

if (x<0 || x>199 || y<0 || y>319) return 0;

/*  установка битовой маски и битов режима цвета

в правую позицию */

bit_position=y%4; /* вычисление нужной позиции

в байте */ bit_mask.i<<=2*(3-bit_position);

/* определение требуемого байта в памяти терминала */

index=x*40+(y>>4);

if (x%2) index+=8152; /* если нечетный, используется

второй блок */

/* запись цвета */

t=*(ptr+index) & bit_mask.c[0];

t>>=2*(3-bit_position);

return t;

/* отображение объекта на экране */

void display_object(ob, sides,cc)

double ob[][4];

int sides,cc;

register int i;

for(i=0; i<sides; i++)

line((int)ob[i][0], (int)ob[i][1],

(int)ob[i][2], (int)ob[i][3], cc|128);

/* Смещение (параллельный перенос) объекта в направлении,

определенном x и y

*/

void update_object(ob, x, y, sides)

int ob[][4];                                  /* объект */

int x, y;                                        /* направление смещения */

register int sides; /* количество сторон объекта */

sides--;

for(; sides>=0; sides--)

ob[sides][0] += x;

ob[sides][1] += y;

ob[sides][2] += x;

ob[sides][3] += y;

/* Определение допустимости перемещения объекта.

Возвращает 1, если перемещение допустимо, 0- в противном случае

*/

void is_legal(ob, x, y, sides)

int ob[][4];                                        /* объект */

int x, y;                                              /* шаг перемещения */

int sides;                                          /* число сторон объекта */

if(x==0 && y==0)

return 1;                         /* пустое перемещение всегда допустимо*/

sides--;

for(; sides>=0; sides--)

/* контроль выхода за допустимую область */ if(ob[sides][0]+x>199 || ob[sides][1]+y>319)



return 0;

if(ob[sides][2]+x<0 || ob[sides][3]+y<0)

return 0;

return 1;

Рассмотрим кратко, как работает эта программа. Клавиши управления курсором (клавиши со стрелками и клавиши <HOME>, <PGUP>, <END> и <PGDN>) определяют положение спрайта. При нажатии клавиши спрайт смещается на одну точку растра в указанном направлении. Клавиши-стрелки управляют горзонтальными и вертикальными перемещениями, остальные - диагональными. Функция is_legal() определяет возможность дальнейшего перемещения спрайта в выбранном направлении. Если возможен выход спрайта за пределы границ экрана, то такое перемещение запрещается. Все остальные функции этой программы работают, как описано в главе 4.

Обычно необходимо сохранять размер объекта, который вы "оживляете" (особенно небольшого) для того, чтобы его можно было перерисовывать с высокой скоростью. Это обеспечивает плавность движения при мультипликации. Если объект достаточно большой, то его движение будет дискретно. При разработке видеоигр необходимо так подбирать размеры спрайта, чтобы возможности компьютера и адаптера реализовывались оптимальным образом.


Содержание раздела