Программирование игр для Windows. Советы профессионала

       

Моделирование реального мира


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

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

 Object_Position += 2;

Если вы посмотрите на координаты объекта как на функцию времени, та увидите график, приведенный на рисунке 11.9.

Что такое ускорение? Ускорение - это изменение скорости за единицу времени или, другими словами, приращение скорости. Когда вы в своем автомобиле нажимаете на акселератор, то скорость становится все 6ольше ибольше. Ускорение может быть записано в следующем виде:

Object_Position += velocity;

velocity += acceleration_factor;

Приведенный фрагмент программы увеличивает скорость. Это выглядит как ускорение. Чаще всего мы сталкиваемся с ускорением свободного падения. Когда объект падает на землю, его ускорение составляет 9,8 м/сек . То есть если в первую секунду падения его скорость достигнет 9,8 м/сек, то во вторую она вырастет до 19,6 м/сек.

Чтобы увидеть, как это осуществляется, посмотрим на текст программы в Листинге 11.5. Она показывает падение мяча под действием силы тяжести. Вы можете изменять ускорение с помощью клавиш + и -.

Листинг 11.5. Падение мяча (BALL.C).

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

#include <stdio.h>

#include <math.h>

#include <graph.h>                                                 

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



#define EARTH_GRAVITY 9.8

//ГЛОБАЛЬНЫЕ

ПЕРЕМЕННЫЕ




////////////////////////////

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

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

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

void Timer(int clicks)

{

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

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

unsigned int now;

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

= *clock;

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

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

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

} // конец Timer

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

void main(void)

{

float ball_x   = 160, ball_y = 50, ball_yv = 0, ball_acc = EARTH_GRAVITY;

int done=0, key;

// используем графические функции Си

_setvideomode(_MRES256COLOR) ;

_settextposition(0,0);

printf("Q to quit, use +,- to change gravity.");

while(!done)

{ // была ли нажата клавиша?

if

(kbhit())

{

// какая клавиша была нажата?

switch(getch())

{

case ' -' :

{

ball_acc-=.1;

} break;

case '=':

{

ball_acc+=.1;

} break;

case 'q' :

{

done=1;

} break;

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

// сообщим игроку новое значение ускорения

_settextposition(24,2);                  

printf("Gravitational Constant = %f",ball_асc);

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

// стираем изображение мяча

_setcolor(0) ;

_ellipse(_GBORDER, ball_x,ball_y,ball_x+10,ball_y+10);

// перемещаем мяч

ball_y+=ball_yv;                             

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

ball_yv+=(ball_acc*.1);                      

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

if (ball_y>190)

{

ball_y=50;

ball_yv=0 ;

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

// рисуем мяч

_setcolor(l) ;

_ellipse(_GBORDER, ball_x,ball_y,ball_x+10,ball_y+10) ;

// немного подождем

Timer(2);

} // конец while

// восстановить начальный видеорежим

_setvideomode(_DEFAULTMODE);

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

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



В качестве последнего примера рассмотрим упругое столкновение. Программа текст которой приведен в Листинге 11.6, создает группу атомов, сталкивающихся в замкнутом пространстве. Когда атомы сталкиваются со стенкой резервуара, они отскакивают, сохраняя при этом свою кинетическую энергию. Мы могли бы использовать систему физических уравнений, однако нам надо только, чтобы это хорошо выглядело. Если вы посмотрите на столкновение бильярдных шаров с портиком стола, то увидите, что они всегда отскакивают под тем же углом, под которым ударились (то есть их угол падения равен углу отражения), как это показано на рисунке 11.10.



Таким образом, чтобы смоделировать это, мы должны только отразить значение скорости атома и все будет выглядеть «корректно с точки зрения физики». Текст программы приведен в Листинге 11.6.

Листинг 11.6. Идеальный газ (GAS.C).

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

#include <dos.h>

#include <bios.h>

#include <stdio.h>

#inciude <math.h>

#include <conio.h>

#include <graph.h>                             

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

#define NUM_ATOMS 300

// СТРУКТУРЫ ///////////////////////////////////////////////

// структура атома

typedef struct ant_typ

{             

int x,y;            // позиция атома

int xv,yv;        // скорость атома

} atom, *atom_ptr;

// ГЛОБАЛЬНЫЕ

ПЕРЕМЕННЫЕ' ///////////////////////////////////

unsigned char far *video_buffer = (char far *)0xA0000000L;

// указатель

на видеобуфер

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

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

// наши

атомы

atom atoms[NUM_ATOMS];

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

void Timer(int clicks)

{

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

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

unsigned int now;

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

now = *clock;

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


Заметьте, что

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

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

} // конец Timer

////////////////////////////////////////////////////////

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

{ // эта функция рисует точку заданного цвета несколько быстрее чем

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

// используется тот факт, что 320*у = 256*у + 64*у = у<<8 + у<<6

video_buffer[((y<<8) + (у<<6)} + х] = color;

} // конец Plot_Pixel_Fast ////////////////////////////////////////////

void Initialize_Atoms (void)

{

int index;

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

{

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

//и их фона

atoms[index].х     = 5 + rand()%300;

atoms [index] .у     = 20 + rand()%160;

atoms [index] .xv    = -5 + rand() %10;

atoms[index].yv    = -5 + rand()%10;

} // конец

цикла

} // конец Initialize_Atoms

////////////////////////////////////////////////

void Erase_Atoms(void)

{

int index;

// обрабатываем в цикле все атомы, стирая их изображения

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

{

Plot_Pixel_Fast( atoms[index].x, atoms[index].y, 0);

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

} // конец Erase_Atoms

////////////////////////////////////////////////////////////

void Move_Atoms(void)

{ int index;

// обрабатываем в цикле все атомы, перемещая каждый атом.

// Также проверяем столкновение атомов со стенками контейнера.

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

{

// переместить атомы

atoms[index].x+=atoms[index].xv;

atoms[index].y+=atoms[index].yv;

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

// на противоположный

if (atoms[index].х > 310 11 atoms[index].x <10)

{

atoms[index].xv =-atoms[index].xv;

atoms [index].x+=atoms [index].xv;

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

if (atoms[index].у > 190 11 atoms[index].у <30)

(

atoms[index].yv=-atoms[index].yv;

atoms[index].y+=atoms[index].yv;

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



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

} // конец Move_Atoms

////////////////////////////////////////

void Draw_Atoms(void)

{

int index;

// рисуем все атомы в цикле

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

{

Plot_Pixel_Fast (atoms[index] .x, atoms [index] .y, 10) ;

} // конец

цикла

} // конец Draw_Atoms

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

void main(void)

{

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

_setvideomode(_MRES256COLOR) ;

_settextposition[2,0);

printf("Hit any key to exit.");

// рисуем контейнер _setcolor(9);

_rectangle(_GBORDER,0,16,319,199);

// инициализируем атомы

Initialize_Atoms();

while(!kbhit())

{

// стираем все атомы

Erase_Atoms

() ;

// перемещаем все атомы

Move_Atoms();

// теперь рисуем атомы

Draw_Atoms();

// немного подождем

Timer(1);

} // конец while

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

_setvideoroode(_DEFAULTMODE) ;

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

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


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