Как претворить в жизнь идею компьютерной игры? Приходилось ли вам, играя в свою любимую игру, мечтать о том, как можно было бы ее улучшить

Вид материалаДокументы

Содержание


Animation (animation (
Таблица 7.3. Таблица перемещений.
Подобный материал:
1   ...   11   12   13   14   15   16   17   18   ...   37

Ошибка» возникающая при расчете таким методом, пренебрежительно мала, и если уменьшение или увеличение компонентов RGB будет производиться всего несколько раз, изменение тона исходного цвета будет практически незаметным.

Связь мультипликации с контекстом

Как это ни удивительно, но на связь мультипликации с контекстом крайне редко обращают внимание в компьютерных играх. Контекст означает «связь с окружением или основой». В играх контекстом являются разнообразные дейст вия по ходу игры. Что бы ни делал персонаж в игре: умирал, прыгал, стрелял или еще что-нибудь, -- все это часть контекста игры. Мультипликация и различные эффекты, которые связаны с контекстом, придают игре дополнительную реалистичность и размах.

Попробую пояснить на примере, что я имею в виду. Представьте, что мы имеем игру, в которой герой может гулять, прыгать, бегать и стрелять. Независимо от того, перепрыгивает ли он через паука или озеро, во время прыжка мультипликация обычно бывает всегда одна и та же. Но что если для каждого случая, или соответствующего контекста мы сделаем отдельный мультик? Например, когда герой перепрыгивает просто через лужу, то он не совершает ничего кроме собственно прыжка. А вот когда ему приходится перепрыгивать через паука, то он может, например, издать пронзительный крик ужаса и показать изумительную сноровку в прыжках. (И действительно, приземление на большого склизкого паука, возможно, не самый лучший опыт для маленького героя, которого заманили в виртуальный компьютерный мир.)

Итак, контекстнозависимая мультипликация, используя набор соответствующих вариантов движений, учитывает тем самым окружение и внешние обстоятельства в каждый момент игрового действия, что дополнительно разнообразит зрительное восприятие игры.

«Animotion»

^ Animation (animation (мультипликация )+ motion (движение)) — это придуманное мною слово, которое должно войти в словарь терминов компьютерных игр. (Дальше я тоже буду выдумывать слова, но они уже не будут так хороши как это.) Оно описывает точное слияние анимации (мультипликации) и движения.

В игре Tombstone четвертой главы, "Механизмы двухмерной графики", маленький ковбой гуляет по улице. В действительности это больше похоже на то, что он одновременно ковыляет и совершает дикие прыжки (если такое вообще можно представить). Вся проблема заключается в принципе его движения, которое не было синхронизировано с мультипликационными кадрами. Многие объекты, в компьютерных играх имеют постоянную скорость. Такие игровые объекты как ракета и прочие летающие предметы — хороший тому пример, Однако к имеющим под собой опору (например, землю) объектам, которые должны выглядеть реально, нужно применять совсем другой подход.

«Animotion» абсолютно необходим, если вы хотите, чтобы движение -ходьба, бег или прыжки — выглядели реалистично. Иначе мультипликационные объекты выглядят неестественно. Мы не можем просто в цикле менять мульти пликационный кадр и одновременно передвигать объект на произвольное расстояние. Мы должны рассмотреть каждый кадр и определить, на сколько в действительности следует перемещать объект для этого кадра. Затем мы создаем таблицу выбора для движения, в которой в качестве индекса используем номера кадров. В этой таблице будут содержаться величины перемещений, которые и будут использоваться при оживлении картинки.

На рисунке 7.6 изображены мультипликационные кадры движения худого человечка, которого-мы назовем «человечек-палка».

(У «человечка-палки» серьезные проблемы со зрением — он имеет только один глаз.) Для движения человечка у нас будет 12 мультипликационных кадров. Для каждого из кадров я прикинул и задал величину перемещения, при котором движение становится действительно похожим на прогулку. Немного помучавшись, я получил для каждого мультипликационного кадра значения, приведенные в таблице 7.3.

^ Таблица 7.3. Таблица перемещений.

Кадр

Значение

Кадр

Значение

Кадр

Значение

1

17

5

3

9

6

2

0

6

0

10

2

3

6

7

17

11

3

4

2

8

0

12

0

Чтобы показать на примере, насколько «animotion» превосходит по качеству постоянное движение, я создал программу в которой игрок, нажимая на клавишу пробел, может выбирать между ними. Нашим «подопытным кроликом» снова будет беззащитный «человечек-палка». Мы собираемся поместить его в действительно ужасный городской квартал и при этом у него не будет ничего, кроме пары ног, пары рук и пары глаз. (Ой, извините! Глаз-то всего один.) Теперь посмотрите, как он разгуливает по экрану. Затем нажмите клавишу пробела, чтобы перейти с постоянного движения к «animotion». Вы увидите большую разницу. Текст программы приведен в Листинге 7.7, она также использует функции из GRAPH0.C, поэтому не забудьте скомпоновать программу с этим модулем.





Листинг 7.7. Демонстрация «animotion» (STICK.С).

// ВКЛЮЧАЕМЫЕ ФАЙЛЫ ////////////////////////////////////////

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include "graph0.h" // включаем нашу графическую библиотеку

// определения /////////////////////////////////////////////

#define VEL_CONST -1 // флаг постоянной скорости перемещения

// ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ ///////////////////////////////////

unsigned int far *clock = (unsigned int far *)0x0000046C;

// указатель на внутренний таймер 18.2 "тик"/с

sprite object;

pcx_picture stick_cells, street_cells;

// таблица выбора содержит величины перемещения для каждого

// мультипликационного кадра, чтобы движение было более реалистичным

int object_vel = {17,0,6,2,3,0,17,0,6,2,3,0};

// функции ////////////////////////////////

void Timer (int clicks)

{

// эта функция использует внутренний таймер с частотой 18.2 "тик"/с

// 32-битовое значение этого таймера находится по адресу 0000:046Сh

unsigned int now;

// получаем текущее время

now = *clock;

//Ожидаем до истечения указанного периода времени.

// Заметьте, что каждый "тик"' имеет длительность примерно в 55 мс

while(abs(*clock - now) < clicks)() {}

// конец Timer

// ОСНОВНАЯ ПРОГРАММА //////////////////////////////////////

void main(void) {

int index, done=0,

vel_state=VEL_CONST;

// установка видеорежима 320х200х256

Set_Mode(VGA256);

// установка размера для системы отображения спрайта

sprite_width = 32;

sprite_height =64;

// инициализация файла PCX, который содержит изображение улицы PCX_Init((pcx_picture_ptr)&street_cells) ;

// загрузка файла PCX, который содержит изображение улицы

PCX__Load("street.pcx", (pcx_picture_ptr)&street_cells,1} ;

PCX_Show_Buffer((pcx_picture_ptr)&street_cells) ;

// используем буфер PCX как дублирующий

double_buffer = street_cells.buffer;

Sprite_Init((sprite_ptr)&object,0,0,0,0, 0,0);

// инициализация файла PCX, который содержит кадры спрайта PCX_Init((pcx_picture_ptr)&stick_cells);

// загрузка файла PCX, который содержит кадры спрайта

PCX_Load("stickman.pcx", (pcx_picture_ptr) &stick_cells,1) ;

// выбираем 6 кадров движения

PCX_Grap_Bitmap((pcx_picture_ptr)&stick_cells,

(sprite_ptr)&object,0,0,0);

PCX_Grap_Bitmap((pcx_picture_ptr)&stick_cells,

(sprite_ptr)&object,1,1,0);

PCX_Grap_Bitmap( (pcx_picture_ptr) &stick_cells,

(Sprite_ptr)&object,2,2,0);

PCX_Grap_Bitmap((pcx_picture_ptr)&stick_cells,

(sprite_ptr)&object,3,3,0);

PCX_Grap_Bitmap((pcx_picture_ptr)&stick cells,

(sprite_ptr)&object/4,4,0);

PCX_Grap_Bitmap( (pcx_picture_ptr) &stick_cells, (sprite_ptr) &object, 5, 5, 0);

PCX_Grap_Bitniap ( (pcx_picture_ptr) &stick_cells,(sprite_ptr)&object,6, 0,1);

PCX_Grap_Bitmap((pcx_picture_ptr)&stick_cells,

(sprite_ptr)&object,7, 1,1);

PCX_Grap_Bitmap ( (pcx_picture_ptr) &stick_cells,

(sprite_ptr)&object,8, 2,1) ;

PCX_Grap_Bitmap((pcx_picture_ptr)&stick_cells,

(sprite_ptr) &object, 9, 3,1);

PCX_Grap_Bitmap((pcx_picture_ptr)&stick_cells,(sprite_ptr)&object,10,4,1);

PCX_Grap_Bitmap((pcx_picture_ptr)&stick_cells,

(sprite_ptr)&object,11,5,1);

// файл stickman.pcx больше не нужен

PCX_Delete((pcx_picture_ptr)&stick_cells);

// настраиваем параметры человечка

object.x = 10;

object.у = 120;

object.curr_frame = 0;

// сохраняем фон

Behind_Sprite((sprite_ptr)&object);


// главный цикл

while(!done)

{

// стираем спрайт

Erase_Sprite((sprite_ptr)&object) ;

// увеличиваем номер кадра на единицу

if (++object.curr_frame > 11) object.curr_fcame = 0;

// перемещаем спрайт, используя или постоянную скорость,

// или таблицу выбора

if (vel_state==VEL_CONST)

{

object.x+=4;

}

// конец if

else

/ {

// используем номер кадра для определения величины

// перемещения по таблице выбора

object.x += object_vel[object.curr_frame];

} // конец else

}

Вы наверняка обратили внимание на горы и местность за окнами, которые перемещаются при ваших поворотах. Фактически, этот пейзаж есть не что иное, как плоское изображение, прокручиваемое как фон. Эти изображения, чтобы они выглядели трехмерными, прорисовываются специальным образом, но, тем не менее, в основе их перемещения лежит все та же прокрутка двухмерного изображения.

Персональные компьютеры могут иметь специальное аппаратное обеспечение, облегчающее прокрутку. Некоторые карты VGA имеют до мегабайта оперативной памяти, что дает возможность рисовать мир вашей игры прямо в видеобуфере, поручая производить прокрутку самой видеокарте. Однако существует две проблемы:

Во-первых, если мы будем полагаться на определенную аппаратуру, наши программы станут аппаратнозависимыми и не будут работать на картах VGA с меньшей памятью;

Во-вторых, прокрутка в режиме 13h существенно сложнее, чем прокрутка в режимах EGA, так как видеопамять в этом случае не разбита на несколько плоскостей. Эта означает, что мы не можем использовать в данном режиме аппаратную прокрутку, таким же образом как в режимах EGA.

Таким образом, мы не будем использовать для прокрутки аппаратное обеспечение персонального компьютера. Мы будем это делать программно, применяя блочное копирование из памяти в видеобуфер. Используя этот метод, мы будем абсолютно независимыми от аппаратного обеспечения, следовательно наши программы будут более гибкими.

Прокрутку целого экрана или его части можно осуществлять двумя путями:
  • Можно нарисовать все пространство игры в обширном буфере памяти. Однако шесть предварительно нарисованных экранов займут 6х64000 байтов, то есть 384К. Это довольно большие потери памяти. В любом случае, как мы только что говорили, мы должны будем сделать воображаемое окно, перемещающееся по этому буферу и отображать все, что в него попадает, на экран; |
  • Второй метод более медленный, но требует меньше памяти. Он основан на генерации изображения «на лету». Под выражением "на лету" я подразумеваю, что мир будет представляться с помощью иначе структурированных данных - например, в виде двухмерной матрицы, где каждая ячейка 8х8 пикселей ставится в соответствие растровому изображению части игрового пространства. Такой ячеистый мир в шесть экранов будет занимать всего 6000 байт. При перемещении окна по ячейкам матрицы, на экране будут воспроизводиться соответствующие растровые изображения.

Главный недостаток этого метода — скорость. Ведь для каждой ячейки нужно будет найти соответствующее растровое изображение и перенести его в дублирующий буфер или на экран, поэтому на перерисовку экрана будет уходить много времени. Вы наверняка встречали игры, которые выглядят не очень симпатично во время прокрутки из-за того, что перерисовка экрана явственно заметна. Это как раз и происходит из-за применения второго метода. Мы должны принести в жертву либо время, либо память. Выбор как всегда за вами.

Чтобы показать вам пример скроллинга, я написал программу, которая создает игровое пространство размером 640х100 пикселей. Я двигаю окно по изображению мира игры и передаю его содержимое в середину экрана. В этой игре мир состоит из звезд и гористого горизонта. (Этот пейзаж немного напоминает игру Defender.) Перемещая с помощью клавиатуры окно просмотра вправо и влево, вы можете прокрутить весь пейзаж. Листинг 7.8 содержит текст этой программы, которая называется DEFEND.С.

Листинг 7.8. Пример прокрутки (DEFEND.C).

// ВКЛЮЧАЕМЫЕ ФАЙЛЫ ////////////////////////////////////////

#include

#include

#include

#inciude

#include

#include

// ОПРЕДЕЛЕНИЯ ///////////////////////////////////////////

#define SCREEN_WIDTH (unsigned int)320

#define SCREEN_HEIGHT (unsigned int)200

// ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ ////////////////////////////////////////

unsigned char far *video_buffer = (char far *)0xA0000000L;//указатель на видеобуфер

unsigned char far *double_buffer = NULL;

// ФУНКЦИИ /////////////////////////////////////////////////

void Show_View_Port(char far *buffer,int pos)

{

// Копирование части дублирующего буфера на экран

unsigned int y,double_off, screen_off;

// нужно переместить 100 строк, перемещаем их построчно

for (y=0; у<100; у++) {

// расчет начального смещения дублирующего буфера

//у * 640 +pos

double_off = ((у<<9) + (у<<7) + роs);

// расчет начального смещения в видеобуфере

// у * 320 + 80

screen_off = (((у+50)<<8) + ((у+50)<<6) + 80);

// перемещение данных

_fmemmove ((char far *)&video_buffer[screen off],

(char far *)&double_buffer[double_off],160);

} // конец цикла for

} // конец Show View_Port ////////////////////////////////////////////////////////////////

void Plot_Pixel_Fast_D2(int x,int y,unsigned char color)

{

// прорисовка пикселей в дублирующем буфере нашего виртуального

// экрана размером 640х100 пикселей

// учтем, что 640*у = 512*у + 128*у = у<<9 + у<<7

double_buffer[ ((у<<9) + (у<<7)) + х] = color;

} // конец Plot_Pixel_Fast_D2 ////////////////////////////////////////////////////////////

void Draw_Terrain(void) {

// эта функция рисует ландшафт в дублирующем буфере

// размером 640х100 пикселей

int х,у=70,index;

// очистка памяти

_fmemset(double_buffer,0,(unsigned int}640*(unsigned int)100);

// рисуем звезды

for (index=0; index<200; index++)

{

Plot_Pixel_Fast_D2(rand()%640,rand()%70,15);

} // конец цикла for

//рисуем горы

for (x=0; x<640; х++)

{

// расчет смещения

y+=-1 + rand()%3;

// проверяем, находятся ли горы в приемлемых границах

if (y>90) у=90;

else

if (y<40) у=40;

// рисуем точку в дублирующем буфере

Plot_Pixel_Fast_D2 (x,y, 10);

} // конец цикла for } // конец Draw_Terrain

// ОСНОВНАЯ ПРОГРАММА //////////////////////////////////////

void main(void)

{

int done=0,sx=0;

// установка видеорежима 320х256х256

_setvideomode(_MRES256COLOR);

_settextposition(0,0);

printf("Use < > to move. Press Q to quit.");

// рисуем небольшое окно

_setcolor(l);

_rectangle(_GBORDER, 80-1,50-1,240+1,150+1) ;

// резервируем память под дублирующий буфер

double_buffer=(char far *)_fmalloc(SCREEN_WIDTH*SCREEN_HEIGHT+1);

Draw_Terrain() ;

Show_View_Port(double_buffer,sx) ;

// главный цикл

while (!done)

{// не Нажал ли игрок клавишу?

if (kbhit())

switch(getch())

{

case ',': // переместить окно влево, если это возможное

{

sх-=2;

if (sx<0)

sx=0;

} break;

case '.':// переместить окно вправо если это возможно

{

sx+=2;

if (sx > 640-160)

sx=640-160;

} break;

case 'q': // игроку надоело?

{

done=1; } break;

} // конец оператора switch

// копируем окно просмотра на экран

Show_View_Port(double_buffer,sx);

_settextposition(24,0);

printf("Viewport position = %d ",sx);

} // конец оператора if

} // конец оператора while

// восстановление видеорежима

_setvideomode(_DEFAULTMODE);

}// конец функции main

Если вы похожи на меня, то, вероятно, захотите написать игру типа Defender. Отлично! Отложите книгу — и вперед. Не беспокойтесь, я подожду. По крайней мере, попытайтесь в своей программе заставить джойстик управлять полетом корабля.

О, вы уже вернулись? Тогда поговорим о тех специальных потрясающих эффектах, без которых ни одна игра никогда не завоюет популярности.

Специальные эффекты

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

"Ого!!!"

Специальные эффекты могут быть какими угодно, начиная от ярких взрывов и кончая сверхъестественными явлениями, психоделической графикой и всем-чем-хотите. Например, сейчас я работаю над компьютерной моделью системы частиц для того, чтобы монстр разлетался на биллионы маленьких атомов, несущихся каждый по своей орбите. Это должно выглядеть отлично! Я не уверен, что я сделаю это, но если мне это удастся, то это будет сводить с ума!

Специальные эффекты — это та область, в которой вы должны полностью Проявить себя, потому что это самая творческая часть любой компьютерной игры. Запомните одну вещь: в любой игре должен быть по-крайней мере один потрясающий воображение эффект, такой эффект, который игрок не скоро забудет! Итак, к делу, тем более что в этом разделе мы поговорим и о других важных вещах.

Фоновая мультипликация

Мы просто не можем пройти мимо фоновой мультипликации. Компьютерные Игры в плане эффектов обошли своих предшественников — игры с фишками. Добавление небольших мультиков и эффектов, которые впрямую не связаны с Действиями в игре делает компьютерные игры забавнее и оригинальнее.

Хороший пример этому — оформление в игре Karate. Летающие птички, время от времени садящиеся на вершину действующего вулкана, — это как раз то, что надо. Для игры, созданием которой мы займемся в конце этой книги, я Придумал светлячков, которые просто мигают то тут, то там. Они не служат для Какой-то определенной цели, а просто делают мир игры немного более реалистичным и полным.

Эти небольшие, длящиеся не более минуты эпизоды вы должны добавить Уже после того, как игра сделана. Всегда пытайтесь добавить в оформление игры Немного небольших мультфильмов и зрительных эффектов — это придаст вашей игре своеобразие.

Экранные эффекты

Компьютерные игры становятся все больше и больше похожими на кинофильмы. В титрах количество программистов и инженеров совсем невелико. Их заменили режиссеры и продюсеры. И вообще все выглядит так, как будто это не дисплей компьютера, а кино- или телеэкран. В кино и по телевизору мы видим изображения смикшированные и растушеванные, кроме того, там применяются и другие эффекты (например, изменяется план или фокус съемки). В компьютерных играх также используют разнообразные экранные эффекты. Например, прохождение вами очередного уровня игры может отмечаться такими эффектами, как гаснущий или тающий экран (или еще что-нибудь эдакое)!

Смена экранов влияет на впечатление от игры. Экран, на котором просто меняется фон эпизодов, быстро надоедает. Я бы предпочел, например, чтобы новая картинка появлялась из-под разлетающихся осколков старого изображения или что-нибудь еще более замысловатое.

Программировать эффектные способы смены экрана на самом деле достаточно просто. Большинство из алгоритмов требует всего нескольких строк кода, а самые сложные из них не более дюжины строк. Это еще одна область, в которой вы должны будете проявить все свои таланты, чтобы добиться действительно стоящих результатов. Для примера я объединил три различных варианта исчезновения содержимого экрана в программу SCREENFX.C. Текст этой программы приведен в Листинге 7.9. Она использует функции работы с изображениями в формате PCX (созданные в пятой главе, «Секреты VGA-карт») и некоторые функции из библиотеки GRAPH0.C.

Программа SCREENFX.C демонстрирует три эффекта:
  • Гаснущее изображение;
  • Исчезновение изображения по точкам;
  • Оплывание изображения.

Попытаемся кратко разобраться в механизмах каждого из них, чтобы понять основные идеи экранных эффектов вообще.