Главная страница Случайная страница КАТЕГОРИИ: АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника |
Листинг 4.11. Супер Астероиды (FIELD.C).
// ВКЛЮЧАЕМЫЕ ФАЙЛЫ /////////////////////////////////////////// #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, и если она стала равна нулю, то уничтожить ракету и дать возможность игроку выстрелить следующий раз. ИТОГ Эта глава могла бы оказаться в сотни раз больше. У меня действительно была задача провести краткий курс двухмерной графики. Мы узнали много новых концепций и терминов. Теперь мы знаем, как производить простые преобразования объектов и точек на плоскости. Мы также узнали о матрицах (даже несколько больше, чем нужно) и как они могут быть использованы в компьютерной графике и видеоиграх для упрощения геометрических преобразований и придания им более компактной формы. В конце мы даже увидели поле астероидов на экране. Я уверен, что у вас есть много вопросов. Наверное, даже больше, чем в начале изучения этой книги. Я постараюсь ответить на них в дальнейшем. Но прошу вас помнить, что не существует простых и быстрых правил для написания игр. Это не сортировка массива. Нам, как программистам, нужен набор различных трюков для реализации наших идей, а посему продолжим работу.
|