Читайте также: |
|
// ВКЛЮЧАЕМЫЕ ФАЙЛЫ /////////////////////////////////////////////
#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-файлы и перемещать битовые образы;
§ Мы поговорили о спрайтах и создали простую библиотеку для работы с ними;
§ Мы затронули довольно сложные вещи, такие как дублирующий буфер и
§ вертикальная синхронизация, о которых более подробно мы поговорим чуть позже;
§ И, наконец, собрав все вместе, мы сделали демонстрационную программу, рисующую маленького ковбоя, который ходит по городу.
До встречи в новой главе.
Дата добавления: 2015-07-12; просмотров: 134 | Нарушение авторских прав
<== предыдущая страница | | | следующая страница ==> |
Листинг 5.15. Отображение символа. | | | ТРЕТЬЕ ИЗМЕРЕНИЕ |