Читайте также:
|
|
// ВКЛЮЧАЕМЫЕ ФАЙЛЫ ///////////////////////////////////////////
#include <stdio.h>
#include <graph.h>
#include <math.h>
//ОПРЕДЕЛЕНИЯ ///////////////////////////////////////////////
#define NUM__ASTEROIDS 10
#define ERASE 0
#define DRAW 1
#defineX_COMP 0
#define Y_COMP 1
#define N_COMP 2
// СТРУКТУРЫ ДАННЫХ ////////////////////////////////////
// новая структура, описывающая вершину
typedef struct vertex_typ
{
float p[3]; // единичная точка в двумерном пространстве
//и фактор нормализации
} vertex, *vertex_ptr;
// общая структура матрицы
typedef struct matrix_typ
{
float elem[3] [3]; // массив для хранения элементов
// матрицы
} matrix, *matrix_ptr;
// определение структуры "объект",.
typedef struct object_typ
{
int num_vertices; // количество вершин в объекте
int color; // цвет объекта
float xo,yo; // позиция объекта
float x_velocity; // скорости пермещения по осям Х float y_velocity;
// и Y matrix scale; // матрица масштабирования
matrix rotation; // матрицы поворота и перемещения
vertex vertices[16]; // 16 вершин
} object, *object_ptr;
// ГЛОБАЛЬНЫЕ.ПЕРЕМЕННЫЕ /////////////////////////////////
object asteroids [NUM_ASTEROIDS];
// ФУНКЦИИ ////////////////////////////////////////////
void Delay(int t)
{
// функция выполняет некоторую задержку
float x = 1;
while(t—>0)
x=cos(x);
} // конец функции
///////////////////////////////////////////////////////
void Make_Identity(matrix_ptr i)
{
// функция формирует единичную матрицу
i->elem[0][0] = i->elem[l][1] = i->elem[2][2] = 1;
i->elem[0][1] = i->elem[l][0] = i->elem[l][2] = 0;
i->elem[2][0] = i->elem[01[2] = i->eiem[2][1] = 0;
} // конец функции ///////////////////////////////////////////////////////
void Clear_Matrix(matrix_ptr m) {
// функция формирует нулевую матрицу
m->е1еm[0][0] = m->elem[l][1] = m->е1еm[2][2] = 0;
m->elem[0][1] = m->elem[l] [0] = m->е1еm[1] [2] = 0;
m->elem[2][0] = m->elem[0] [2] = m->elem[2][1] = 0;
} // конец функции
///////////////////////////////////////////////////////
void Mat_Mul (vertex_ptr v, matrix_ptr m)
{
// функция выполняет умножение матрицы 1х3 на матрицу 3х3
// элемента. Для скорости каждое действие определяется "вручную",
// без использования циклов. Результат операции - матрица 1х3
float x new, у new;
x_new=v->p[0]*m->elem[0][0] + v->p[1]*m->elem[1][0] + m->elem[2][0];
y_new=v->p[0]*m->elem[0][1] + v->p[1]*m->elem[1][1] + m->elem[2][1];
// N_COMP - всегда единица
v->p[X_COMP] = x_new;
v->p[Y_COMP] = y_new;
} // конец функции
///////////////////////////////////////////////////////
void Scale_Object_Mat(object_ptr obj)
{
// функция выполняет масштабирование объекта путем умножения на
// матрицу масштабирования
int index;
for (index=0; index<obj->num_vertices; index++)
{
Mat_Mul ((vertex_ptr) &obj->vertices [index],
(matrix_ptr)&obj->scale);
} // конец цикла for
} // конец функции
///////////////////////////////////////////////////////
Rotate_Object_Mat(object_ptr obj)
{
// функция выполняет.поворот объекта путем умножения на
// матрицу поворота
int index;
for (index=0; index<obj->num_yertices; index++)
{
Mat_Mul((vertex_ptr)&obj->vertices[index],(matrix_ptr)&obj->rotation);
} // конец цикла for
} // конец функции ///////////////////////////////////////////////////////
void Create_Field(void)
{
int index;
float angle,c,s;
// эта функция создает поле астероидов
for (index=0; index<NUM_ASTEROIDS; index++)
{
asteroids[index].num vertices = 6;
asteroids [index].color = 1 + rand() % 14; //астероиды
// всегда видимы
asteroids[index].xo = 41 + rand() % 599;
asteroids[index].yo = 41 + rand() % 439;
asteroids [index].,x_velocity = -10 +rand() % 20;
asteroids[index].y_velocity = -10 + rand() % 20;
// очистить матрицу
Make_Identity((matrix_ptr)&asteroids[index].rotation);
// инициализировать матрицу вращений
angle = (float) (- 50 + (float) (rand ()\ % 100)) / 100;
c=cos(angle);
s=sin(angle);
asteroids[index].rotation.elem[0][0] = с;
asteroids[index].rotation.elem[0][1] = -s;
asteroids[index].rotation.elem[l][0] = s;
asteroids[index].rotation.elem[l][1] = с;
// формируем матрицу масштабирования
// очистить матрицу и установить значения коэффициентов
Make_Identity((matrix ptr)&asteroids[index].scale);
asteroids[index].scale.elem[0][0] = (float) (rand() % 30) / 10;
asteroids[index].scale.elem[1][1] = asteroids[index].scale.elem[0][0];
asteroids[index].vertices[0].p[X_COMP] = 4.0;
asteroids[index].vertices[0].p[Y_COMP] = 3.5;
asteroids[index].vertices[0].p[N_COMP] = l;
asteroids[index].vertices[1].p[X_COMP] = 8.5;
asteroids[index].vertices[l].p[Y_COMP) = -3.0;
asteroids[index].vertices[1].p[N_COMP] = l;
asteroids[index].vertices[2].p[X_COMP] = 6;
asteroids[index].vertices[2].p[Y_COMP] = -5;
asteroids[index].vertices[2].p[N_COMP] = l;
asteroids[index].vertices[3].p[X_COMP] = 2;
asteroids[index].vertices[3].p[Y_COMP] = -3;
asteroids[index].vertices[3].p[N_COMP] = l;
asteroids[index].vertices[4].p[X_COMP] = -4;
asteroids[index].vertices[4].p[Y_COMP] = -6;
asteroids[index].vertices[4].p[N_COMP] = 1;
asteroids[index].vertices[5].p[X_COMP] = -3.5;
asteroids[index].vertices[5].p[Y_COMP] = 5.5;
asteroids[index],vertices[5].p[N_COMP] = 1;
// теперь масштабировать астероиды
Scale_Object_Mat((object_ptr)&asteroids[index]);
} // конец цикла for
} // конец функции
///////////////////////////////////////////////////////
void Draw Asteroids (int erase)
{
int index,vertex;
float xo,yo;
// эта функция в зависимости от переданного флага рисует или стирает
// астероиды
for (index=0; index<NUM_ASTEROIDS; index++)
{
// нарисовать астероид
if (erase==ERASE)
_setcolor(0);
else
_setcolor(asteroids[index].color);
// получаем позицию объекта
xo = asteroids[index].xo;
yo = asteroids [index].yo;
// перемещаемся в позицию первой вершины астероида
_moveto((int)(xo+asteroids[index]-vertices[О].p[X_COMP]), (int)(yo+asteroids[indexl.vertices[0].p[Y_COMP]));
for (vertex=l; vertex<asteroids[index].num_vertices; vertex++)
{
_lineto((int)(xo+asteroids[index].vertices[vertex].p[X_COMP]), (int)(yo+asteroids[index].vertices[vertex].p[Y_COMP]));
} // конец цикла по вершинам
_lineto((int)(xo+asteroids[index].vertices[0],p[X_COMP]), (int)(yo+asteroids[index].vertices[0].p[Y_COMP]));
} // замкнуть контур объекта
} // конец функции
///////////////////////////////////////////////////////
void Translate_Asteroids(void)
{
// функция перемещает астероиды
int index;
for (index=0; index<NUM_ASTEROIDS; index++)
{
// перемещать текущий астероид
asteroids[index].xo += asteroids[index].x velocity;
asteroids[index].yo += asteroids[index].y_velocity;
// проверка на выход за границы экрана
if (asteroids[index].xo > 600 || asteroids[index].xo < 40)
{
asteroids[index].x_velocity = -asteroids[index].x_velocity;
asteroids[index].xo += asteroids[index].x_velocity;
}
if (asteroids[index].yo > 440 1) asteroids[index].yo < 40)
{
asteroids[index].y_velocity = -asteroids[index].y_velocity;
asteroids[index].yo += asteroids[index].у_velocity;
)
} // конец цикла for
} // конец функции
////////////////////////////////////////////////////////
void Rotate__Asteroids()
{
int index;
for (index=0; index<NUM_ASTEROIDS; index++)
{
// вращать текущий астероид
Rotate_Object_Mat((object_ptr)&asteroids[index]);
} // конец цикла for
} // конец функции
///////////////////////////////////////////////////////
void main(void)
{
// перевести компьютер в графический режим
_setvideomode(_VRES16COLOR); // 640х480, 16 цветов
// инициализация
Create_Field();
while(!kbhit())
{
// стереть поле
Draw_Asteroids(ERASE);
// преобразовать поле Rotate_Asteroids();
Translate_Asteroids();
// нарисовать поле
Draw_Asteroids(DRAW);
// немного подождем...
Delay(500);
)
// перевести компьютер в текстовый режим
_setvideomode(_DEFAULTMODE);
} // конец функции
Вам потребуется время, чтобы изучить эту программу. Уделите внимание способу, которым астероиды масштабируются и поворачиваются. Если вы сравните время исполнения программ из Листингов 4.8 и 4.11, то не найдете никакой разницы. Если же вы потратите время и поместите все повороты, масштабирование и перемещение в одну матрицу, то программа из Листинга 4,11 будет работать значительно быстрее.
Итак, умножение матриц особенно эффективно, когда вы производите множественные трансформации объекта с помощью единой, заранее подготовленной матрицы, включающей в себя все операции преобразования над этим объектом.
К вашему сведению
Запомните, что все это мы делаем для того, чтобы научиться писать видеоигры, особенно трехмерные. И делаем мы это постепенно. Если я вам сразу покажу, как писать игры типа Wolfenstein 3-D или DOOM, то мы многое потеряем в тактике и философии разработки и создания игр.
Если же вы настоящий разработчик игр, то уже в данный момент должны думать,о том, как переделать те программы, которые я дал, в настоящую игру. Не беспокойтесь! Мне и в голову не приходит сказать: «В качестве упражнения перепишите программу из Листинга 4.11 в настоящую игру». Мы напишем игру вместе, но чуть позже. Лучше поговорим о том, какие изменения нам надо внести, чтобы игра получилась.
Если вы хотите узнать, как я бы переделывал Листинг 4.8 в настоящую игру, то я бы сделал следующее:
§ Оснастил программу всем необходимым для выполнения миссии. Ведь нужно иметь маневренный космический корабль, несколько ракет для уничтожения астероидов, а чтобы игра приняла «товарный» вид, нужна система подсчета очков, заставка и, конечно, возможность выхода из программы. Кроме перечисленного потребуется создать систему оповещения о столкновениях с астероидами (такая система будет рассмотрена чуть позже, в этой же главе);
§ Далее я подготовил бы свой корабль. Для этого описал бы объект, который позаимствовал, например, из игры Star Trek;
§ Написал бы подпрограмму для рисования и уничтожения корабля (типа функции DrawAsteroids из Листинга 4.11);
§ Разработал бы функции, позволяющие игроку управлять кораблем;
§ Написал бы функции, которые, в зависимости от состояния клавиатуры, вращали корабль по или против часовой стрелки. Это я сделал бы, изменяя величину angles в структуре, описывающей объект корабля;
§ Потом мне понадобился бы способ передвижения корабля. Для этого я бы изменял значения x_velocity и y_velocity в соответствующих функциях:
x_veiocity = cos(angle)*speed
y_velocity = sin(angle)*speed
§ где скорость изменялась бы при нажатии на определенные клавиши;
§ Чтобы ввести корабль в игру, я поместил бы вызов функции стирания всех графических объектов - корабля и астероидов - в одно и то же место. Затем поместил бы функцию движения, которая опрашивает клавиатуру, в середину программы (между вызовом функций стирания и рисования);
§ В самый конец цикла вставил бы функцию рисования корабля.
Вот, собственно, и все. Выглядит не очень сложно. Посмотрим, что дальше.
Основы контроля столкновений
Давайте забудем про оружие и посмотрим, что произойдет, если нас заденет астероид (я слышал, они очень твердые). Для этого нам надо создать систему контроля столкновений.
Это значит, что нам надо сделать зримым соударение космического корабля с астероидом. Правильнее всего будет поступить так:
1. Забыть о реальных границах астероида и корабля и создать виртуальную граничную область вокруг каждого объекта исключительно для тестовых целей. Не надо ее рисовать, достаточно, чтобы она присутствовала внутри программы. Рисунок 4.10 показывает, что я имею в виду.
2. Если мы увидим, что граничная область нашего судна вошла в граничную область астероида, мы можем сказать, что наш корабль уничтожен. Это довольно просто сделать: достаточно сравнить каждую вершину области с вершиной другой области. Если есть вхождение, значит произошло столкновение.
3. Теперь, когда столкновение зафиксировано, нужно произвести на экране какой-нибудь красочный эффект: 500-тонный корабль с двигателем на антивеществе врезается в астероид. Например, вспышки случайных точек. Это будет неплохо смотреться.
4. Подсчет очков прост: создайте переменную score и печатайте ее на экране каждый раз, проходя через главный цикл. Вы можете использовать функцию _outtext () из библиотеки Си.
Кроме этого, нам надо набирать очки. Значит, нужно иметь оружие. Например, корабль в игре может использовать фотонные торпеды — маленькие точки. Чтобы их получить, нам надо:
1. Назначить одну из клавиш (лучше Пробел ) для стрельбы.
2. Когда будет обнаружено нажатие на клавишу, определить несколько переменных, в которых запомнить направление движения корабля (miss_xv и miss_yv).
3. Затем запомнить в двух переменных исходную позицию ракеты, то есть координаты центра корабля (miss_x и miss_y).
4. Присвоить переменной life значение 100.
5. В цикле начать перемещение ракеты параллельно курсу корабля и каждый раз сравнивать координаты торпеды с границами астероидов. Если столкновение обнаружено, увеличить счетчик очков и удалить астероид.
6. Уменьшить значение переменной life, и если она стала равна нулю, то уничтожить ракету и дать возможность игроку выстрелить следующий раз.
ИТОГ
Эта глава могла бы оказаться в сотни раз больше. У меня действительно была задача провести краткий курс двухмерной графики. Мы узнали много новых концепций и терминов. Теперь мы знаем, как производить простые преобразования объектов и точек на плоскости. Мы также узнали о матрицах (даже несколько больше, чем нужно) и как они могут быть использованы в компьютерной графике и видеоиграх для упрощения геометрических преобразований и придания им более компактной формы. В конце мы даже увидели поле астероидов на экране. Я уверен, что у вас есть много вопросов. Наверное, даже больше, чем в начале изучения этой книги. Я постараюсь ответить на них в дальнейшем. Но прошу вас помнить, что не существует простых и быстрых правил для написания игр. Это не сортировка массива. Нам, как программистам, нужен набор различных трюков для реализации наших идей, а посему продолжим работу.
Дата добавления: 2015-07-12; просмотров: 104 | Нарушение авторских прав
<== предыдущая страница | | | следующая страница ==> |
Листинг 4.9. Определение и умножение двух матриц. | | | СЕКРЕТЫ VGA-КАРТ |