Главная страница Случайная страница КАТЕГОРИИ: АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника |
Листинг 5.16. Tombstone (TOMB.С).
// ВКЛЮЧАЕМЫЕ ФАЙЛЫ ///////////////////////////////////////////// #include < io.h> #include < conio.h> #include < stdio.h> #include < stdlib.h> #include < dos.h> #include < bios.h> #include < fcntl.h> #include < memory.h> #include < math.h> #include < string.h> // ОПРЕДЕЛЕНИЯ ////////////////////////////////////////////////// #define ROM_CHAR_SET_SEG 0xF000 // сегмент описания символов в ПЗУ #define ROM_CHAR_SET_OFF 0xFA6E // смещение, соответствующее // описанию первого символа #define VGA256 0х13 #define TEXT_MODE 0х03 #define PALETTE_MASK ОхЗC6 #define PALETTE_REGISTER_RD 0x3C7 #define PALETTE_REGISTER_WR 0x3C8 #define PALETTE_DATA 0x3C9 #define SCREEN_WIDTH (unsigned int)320 #define SCREEN_HEIGHT (unsigned int)200 #define CHAR_WIDTH 8 #define CHAR_HEIGHT 8 #define SPRITE_MIDTH 24 #define SPRITE_HEIGHT 24 #define MAX_SPRITE_FRAMES 16 #define SPRITE_DEAD 0 #define sprite_alive 1 #define SPRITE_DYING 2 // СТРУКТУРЫ ДАННЫХ //////////////////////////////////////// typedef struct RGB_color_typ { unsigned char red; // красная составляющая цвета (0-63) unsigned char green; // зеленая составляющая цвета (0-63) unsigned char blue; // синяя составляющая цвета (0-63) } RGB_color, *RGB_color_ptr;
typedef struct pcx_header_typ { char manufacturer; char version; char encoding; char bits_per_pixel; int x, y; int width, height; int horz_res; int vert_res; char ega_palette[46]; char reserved; char num_color_planes; int bytes_per_line; int palette_type; char padding[58]; } pcx_header, *pcx_header_ptr; typedef struct pcx_picture_typ { pcx_header header; RGB_color palette[256]; char far *buffer; } pcx_picture, *pcx_picture_ptr; typedef struct sprite_typ { int x, y; // текущая позиция спрайта int x_old, y_old; // предыдущая позиция спрайта int width, height; // размеры спрайта int anim_clock; // время анимации int anim_speed; // скорость анимации int motion_speed; // скорость движения int motion_clock; // время движения char far *frames[MAX_SPRITE__FRAMES]; // массив указателей // на образы int curr_frame; // отображаемый фрейм int num_frames; // общее число фреймов int state; // статус спрайта char far *background; // фон под спрайтом }sprite, *sprite_ptr; // ВНЕШНИЕ ФУНКЦИИ ///////////////////////////////// extern Set_Mode(int mode); // ПРОТОТИПЫ /////////////////////////////////////// void Set_Palette_Register(int index, RGB_color_ptr color); void Plot_Pixel_Fast(int x, int y, unsigned char color); void PCX_Init(pcx_picture *image); void PCX_Delete(pcx_picture *image); void PCX_Load(char *filename, pcx_picture_ptr image, int enable_palette); void PCX_Show_Buffer(pcx_picture_ptr image); // ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ ////////////////////////////// unsigned char far *video_buffer = (char far *) 0xA0000000L; unsigned int far *video_buffer_w = (int far *)0xA0000000L; unsigned char far *rom_char_set = (char far *)0xF000FA6EL; // ФУНКЦИИ //////////////////////////////////////////// void Blit_Char(int xc, int yc, char c, int color) { // эта функция отображает символ на экране // используя описание // символов размером 8х8 точек, хранящееся в ПЗУ int offset, х, у; unsigned char data; char far *work_char; unsigned char bit_mask = 0х80; // вычисляем смещение описания символа в ПЗУ work_char = rom_charset + с * CHAR_HEIGHT; // вычисляем смещение символа в видеобуфере offset = (ус < < 8} + (ус < < 6) + хс; for (у=0; y< CHAR_HEIGHT; y++) { // сбросить битовую маску bit_mask = 0х80; for (x=0; x< CHAR_WIDTH; x++) { // если бит равен 1, рисуем пиксель if ((*work_char & bit_mask)) video_buffer[offset+x] = color; // сдвигаем маску bit_mask = (bit_mask> > 1); } // конец отрисовки строки // переходим к следующей строке offset += SCREEN_WIDTH; work_char++; } // конец рисования } // конец функции ////////////////////////////////////////////////////////// void Blit_String(int x, int y, int color, char *string) { // функция отображает на экране передаваемую строку символов // Расстояние между символами строки постоянно // Для отображения символов вызывается функция blit_char int index; for (index=0; string[index]! =0; index++) { Blit_Char(x+(index< < 3), y, string[index], color); } // конец цикла for } // конец функции /////////////////////////////////////////////////////// void Delay(int t) { float x = 1; while(t—> 0) x=cos(x); } // конец функции /////////////////////////////////////////////////////// void Set_Palette_Register(int index, RGB_color_ptr color) { // функция устанавливает элемент таблицы цветов задаваемый // параметром index. Значения компонент цвета задаются полями // структуры color. // указываем VGA-карте, что мы будем изменять регистр палитры _outp(PALETTE_MASK, Oxff); // указываем номер изменяемого регистра _outp(PALETTE_REGISTER_WR, index); // теперь меняем значения компонентов. Каждый раз используется // один и тот же порт _outp(PALETTE_DATA, color-> red); _outp(PALETTE_DATA, color-> green); _outp(PALETTE_DATA, color-> blue); } // конец функции /////////////////////////////////////////////////////// void PCX_Init(pcx_picture_ptr image) { // функция выделяет память для загрузки PCX-файла if ((image-> buffer = (char far *)malloc (SCREEN_WIDTH * SCREEN_HEIGHT +1))); printf(" \ncouldn't allocate screen buffer"); } // конец функции /////////////////////////////////////////////////////// void Plot_Pixel_Fast(int x, int y, unsigned char color) { // функция отображает на экране точку заданного цвета // вместо умножения используется сдвиг // пользуемся тем, что 320*у=256*у+64*у=у< < 8+у< < 6 video_buffer[ ((у< < 8) + (у< < 6)) + х] = color; } // конец функции /////////////////////////////////////////////////////////////////////////// void PCX_Delete(pcx_picture_ptr image) { // функция освобождает память, выделенную для загрузки PCX-файла _ffree (image-> buffer); } // конец функции ////////////////////////////////////////////////////////// void PCX_Load(char *filename, pcx_picture_ptr image, int enable_palette} { // функция загружает PCX-файл и заполняет поля структуры // pcx_picture, включая изображение (после декомпрессии), // заголовок и палитру FILE *fp; int num_bytes, index; long count; unsigned char data; char far *temp_buffer; // открыть файл fp = fopen(filename, " rb"); // загрузить заголовок temp_buffer = (char far *)image; for (index=0; index< 128; index++) { temp_buffer[index] = getc(fp); } // конец цикла for // загрузить данные и декодировать их в буфере count=0; while(count< =SCREEN_WIDTH * SCREEN_HEIGHT) { // получить первую часть данных data = getc(fp); // это RLE? if (data> =192 & & data< =255) { // сколько байт сжато? num_bytes = data-192; // получить значение цвета для сжатых данных data = getc(fp); // заполняем буфер полученным цветом while(num_bytes--> 0) { image-> buffer[count++] = data; } // конец цикла while } // конец обработки сжатых данных else { // поместить значение цвета в очередную позицию image-> buffer[count++] = data; } // конец обработки несжатых данных } // конец цикла while // перейти в позицию, не доходя 768 байт от конца файла fseek(fp, -768L, SEEK_END); // загрузить палигру for (index=0; index< 256; index++) { // красная составляющая image-> palette[index].red = (getc(fp) > > 2); // зеленая составляющая image-> palette[index].green = (getc(fp) > > 2); // синяя составляющая image-> palette[index].blue = (getc(fp) > > 2); } // конец цикла for fclose(fp); // если установлен флаг enable_palette, установить новую палитру if (enable_palette) { for (index=0; index< 256; index++) { Set_Palette_Register(index, (RGB_color_ptr)& image-> palette[index]); } // конец цикла for
} // конец установки палитры } // конец функции
////////////////////////////////////////// void PCX_Show_Buffer (pcx_picture_ptr image) { // функция копирует буфер, содержащий изображение из PCX-файла, // в видеопамять _fmemcpy(char far *)video_buffer, (char far *) image-> buffer, SCREEN_WIDTH*SCREEN__HEIGHT); } // конец функции //////////////////////////////////////////////////// void Sprite_Init(sprite_ptr sprite, int x, int y, int ac, int as, int mc, int ms) { // функция инициализирует спрайт int index; sprite-> x = x; sprite-> y = у; sprite-> x_old = x; sprite-> y_old = у; sprite-> width = SPRITE_WIDTH; sprite-> height = SPRITE_HEIGHT; sprite-> anim_clock = ac; sprite-> anim_speed = as; sprite-> motion_clock = me; sprite-> motion_speed = ms; sprite-> curr frame = 0; sprite-> state = SPRITE_DEAD; sprite-> num_frames = 0; sprite-> background = (char far *)malloc (SPRITE_WIDTH * SPRITE_HEIGHT+1); // устанавливаем все указатели в значение NULL for (index=0; index< MAX_SPRITE_FRAMES; index++) sprite-> frames[index] = NULL; } // конец функции //////////////////////////////////////////////////////// void Sprite_Delete(sprite_ptr sprite) { // функция освобождает всю связанную со спрайтом память int index; _ffree(sprite-> background); // освобождаем память, выделенную под кадры анимации for (index=0; index< MAX_SPRITE_FRAMES; index++) _ffree(sprite-> frames(index]); } // конец функции /////////////////////////////////////////////////////// void PCX_Grap_Bitmap (pcx_picture_ptr image, sprite_ptr sprite, int sprite_frame, int grab_x, int grab_y) { // функция выделяет один кадр из буфера PCX-файла. // Предполагается, что изображение размером 320х200 пикселей // в действительности представляет собой массив 12х8 изображений // каждое размерностью по 24х24 пикселя int x_off, y_off, x, y, index; char far *sprite_data; //вначале выделяем память для размещения спрайта sprite-> frames[sprite_frame] = (char far *)malloc (SPRITE_WIDTH * SPRITE_HEIGHT); // создаем альтернативный указатель для ускорения доступа sprite_data = sprite-> frames[sprite_frame]; // загружаем битовый образ в выделенную область памяти // вначале вычисляем, какое из изображений копировать. // Помните: в действительности PCX-файл представляет собой массив // из отдельных элементов размером 24х24 пикселя. // Индекс (0, 0) соответствует левому верхнему изображению, // (11, 7) - правому нижнему. x_off = 25 * grab_x + 1; y_off = 25 * grab_y + 1; // вычисляем начальный адрес y_off = y_off * 320; for (y=0; y< SPRITE__HEIGHT; y++) { for (x=0; x< SPRITE_WrETH; x++) { // берем очередной байт и помещаем его в текущую позицию буфера sprite_data[y*24 + х] = image-> buffer [y_off + х_off + х]; } // конец копирования строки // переходим к следующей строке y_off+=320; } // конец копирования // увеличиваем счетчик кадров sprite-> num_frames++; } // конец функции ////////////////////////////////////// void Behind_Sprite(sprite_ptr sprite) { // функция сохраняет содержимое видеопамяти в той области, // куда будет выведен спрайта char far *work back; int work_offset=0, offset, y; // создаем альтернативный указатель для ускорения доступа work_back = sprite-> background; // вычисляем смещение в видеопамяти offset = (sprite-> y < < 6) + (sprite-> y < < 6) + sprite-> x; for (y=0; y< SPRITE_HEIGHT; y++) { // копируем очередную строку видеопамяти в буфер _fmemcpy((char far *)& work_back[work_offset], (char far *)& video_buffer[offset], SPRITE_WIDTH); // переходим к следующей строке offset += SCREEN_WIDTH; work_offset += SPRITE_WIDTH; } // конец цикла for } // конец функции /////////////////////////////////////////// void Erase_Sprite(sprite_ptr sprite) { // функция восстанавливает фон, сохраненный перед выводом спрайта char far *work_back; int work_offset=0, offset, y; // создаем альтернативный указатель для ускорения доступа work_back = sprite-> background; // вычисляем смещение в видеобуфере offset = (sprite-> y_old < < 8) + (sprite-> y_old < < 6} + sprite-> x_old; for (y=0; y< SPRITE_HEIGHT; y++) { // копируем в видеопамять очередную строку буфера _fmemcpy((char far *)& video_buffer [offset], (char far *)& work_back[work_offset], SPRITE_WIDTH); // перейти к следующей строке offset += SCREEN_WIDTH; work_offset += SPRITE_WIDTH; } // конец цикла for } // конец функции /////////////////////////////////////////////////////// void Draw_Sprite(sprite_ptr sprite) { // функция, рисующая спрайт на экране вместо умножения // используется сдвиг char far *work sprite; int work_offset=0, offset, x, y; unsigned char data; work sprite = sprite-> frames[sprite-> curr_frame]; // вычислить смещение спрайта в видеобуфере offset = (sprite-> y < < 8) + (sprite-> y < < 6) + sprite-> x; for (y=0; y< SPRITE_HEIGHT; y++) { for (x=0; X< SPRITE_WIDTH; X++) { // проверяем, не является ли пиксель " прозрачным" (с кодом 0), // если нет - рисуем if ((data=work_sprite[work_offset+x])) video_buffer[offset+x] = data; } // конец вывода строки // перейти к следующей строке в видеобуфере и буфере спрайта offset += SCREEN_WIDTH; work_offset += SPRITE_WIDTH; } //конец цикла no Y } // конец функции // ОСНОВНАЯ ПРОГРАММА ///////////////////////////////////////// void main(void) { long index, redraw; RGB_color color; int frame_dir = 1; pcx_picture town, cowboys; sprite cowboy; // установить видеорежим 320х200х256 Set_Mode(VGA256); // установить глобальные указатели на область памяти экрана Set_Screen_Pointers(); // загрузить фон PCX_Init((pcx_picture_ptr)& town); PCX_Load(" town. pox", (pcx_picture_ptr)& town, 1); PCX_Show_Buffer((pcx_picture_ptr)& town); PCX_Delete((pcx_picture_ptr)& town); // вывести на экран заставку игры Blit_String(128, 24, 50, " TOMBSTONE"); // загрузить образы PCX_Init((pcx_picture_ptr)& cowboys); PCX_Load(" cowboys.pcx", (pcx_picture_ptr) & cowboys, 0); // извлечь все образы из PCX-файла Sprite_Init((sprite_ptr)& cowboy, SPRITE_WIDTH, 100, 0, 7, 0, 3); PCX_Grap_Bitmap ((pcx_picture_ptr) & cowboys, (sprite_ptr) & cowboy, 0, 0, 0); PCX Grap_Bitmap((pcx_picture_ptr) & cowboys, (sprite_ptr)& cowboy, 1, 1, 0); PCX_Grap_Bitmap((pcx_picture_ptr)& cowboys, (sprite_ptr)& cowboy, 2, 2, 0); PCX_Grap_Bitmap ((pcx_picture_ptr)& cowboys, (sprite_ptr)& cowboy, 3, 1, 0); // очистить память, выделенную для загрузки PCX-файла PCX_Delete((pcx_picture_ptr)& cowboys); Behind_Sprite((sprite_ptr)& cowboy); Draw_Sprite ((sprite_ptr) & cowboy); // главный цикл cowboy.state = SPRITE_ALIVE; while (! kbhit()) { redraw = 0; // используется как флаг необходимости перерисовки if (cowboy.state==SPRITE_ALIVE) { //не пора ли менять кадр? if (++cowboy.anim_clock > cowboy.anim_speed) { // сбрасываем счетчик времени cowboy.anim_clock = 0; if (++cowboy.curr_frame > = cowboy. num_frames) { cowboy.curr_frame = 0; } // конец обработки достижения последнего кадра redraw=1; } // конец смены кадра // проверяем не пора ли передвигать спрайт if (++cowboy.motion_clock > cowboy.motion_speed) { // переустановить счетчик движения cowboy.motion clock =0; // сохранить старую позицию cowboy.x_old == cowboy.x; redraw =1; //передвинуть спрайт if (++cowboy.x > = SCREEN_WIDTH-2*SPRITE_WIDTH) { Erase_Sprite((sprite_ptr)& cowboy); cowboy.state = SPRITE_DEAD; redraw = 0; } // конец обработки достижения последнего кадра } // конец обработки движения спрайта } // конец обработки ситуации " ковбой жив" else { // пытаемся " оживить" ковбоя if (rand()%100 == 0) { cowboy.state = SPRITE_ALIVE; cowboy.x = SPRITE_WIDTH; cowboy.curr frame = 0; cowboy.anim.speed = 3 + rand()%6; cowboy.motion_speed = 1 + rand()%3; cowboy.anim_clock = 0; cowboy.motion_clock = 0; Behind_Sprite{(sprite_ptr)& cowboy); } } // конец процесса " оживления" // теперь состояние спрайта изменено if (redraw) { // удалить спрайт в старой позиции Erase_Sprite((sprite_ptr)& cowboy); // сохранить фон в новой позиции Behind_Sprite((sprite_ptr)& cowboy); // нарисовать спрайт в новой позиции Draw_Sprite((sprite_ptr)& cowboy); // обновить старые координаты cowboy.x_old = cowboy.x; cowboy.у_old = cowboy.у; } // конец перерисовки Delay(1000); } // конец главного игрового цикла for(index=0; index < 300000; index++, Plot_Pixel_Fast(rand()%320, rand()%200, 0)); //Перейти обратно в текстовый режим Set_Mode(TEXT_MODE); }//Конец функции main
Итог Эта глава, наверное, самая важная в книге. Среди всей информации, которая, может быть, не относится напрямую к играм, вы узнали общую концепцию построения видеоигр, что абсолютно необходимо для их написания. Так что, если вы чего-то не поняли, то остановитесь и прочтите главу вновь (не беспокойтесь, я вас подожду). Если вы прочитали и поняли в этой главе все, то.должны знать, что: § Мы изучили VGA-карту и ее архитектуру; § Мы узнали о режиме l3h, который дает лучшее разрешение и наиболее прост для программирования; § Мы узнали, как в режиме l3h программировать регистры цвета, рисовать пиксели, загружать PCX-файлы и перемещать битовые образы; § Мы поговорили о спрайтах и создали простую библиотеку для работы с ними; § Мы затронули довольно сложные вещи, такие как дублирующий буфер и § вертикальная синхронизация, о которых более подробно мы поговорим чуть позже; § И, наконец, собрав все вместе, мы сделали демонстрационную программу, рисующую маленького ковбоя, который ходит по городу. До встречи в новой главе.
|