← Январь 2002 → | ||||||
1
|
2
|
3
|
4
|
5
|
6
|
|
---|---|---|---|---|---|---|
7
|
8
|
9
|
10
|
11
|
12
|
13
|
14
|
15
|
16
|
17
|
18
|
19
|
20
|
21
|
22
|
23
|
24
|
26
|
27
|
|
28
|
29
|
30
|
31
|
За последние 60 дней ни разу не выходила
Открыта:
25-10-2000
Адрес
автора: comp.soft.prog.gamemaker-owner@subscribe.ru
Статистика
0 за неделю
Как самому создать компьютерную игру #13
Адрес для обратной связи - gamemaker@pisem.net 2. Виртуальная реальность: ray casting. 3. Вопросы и ответы. 4. Требуются... 5. Послесловие. 25 рублей за сайт. Была бы это реклама, да еще и на телевидении, непременно в самом начале фразы поставили бы слово "всего". Звучит ведь! Всего 13% налог, всего 4 рубля за новый экономичный Тайд, да и в Макдональдсе гамбургеры с чизбургерами теперь стоят всего "ничего". И вообще, много еще за что с нас хотят содрать всего 99 долларов, да еще и 99 центов. Но здесь немного другая ситуация. Когда я в первый раз захотел разместить свой сайт на платном хостинге, меня ждало разочарование. Цены за месяц "обслуживания" были в среднем 10$. Дороговато. Пускай даже сейчас они упали до 8-6$, все равно для обычного пользователя интернета, захотевшего разместить свой сайт в сети это неприемлемо. Но сейчас появилось новое решение, которое, возможно, Вас заинтересует. Разместить свой сайт в сети за 25 рублей в месяц могут ТОЛЬКО подписчики рассылки "Как самому создать компьютерную игру", т.е. Вы. Кстати, одноименный сайт тоже выбрал этот хостинг и очень скоро, после тестирования, появиться на новом месте. Если Вас хоть немного это заинтересовало, пишите мне (gamemaker@pisem.net?Subject=Host). В любом случае Вы ничего не потеряете, если узнаете об этой акции немного больше. (Если Вы не знаете, как узнать подробнее про это, ;) можно прислать мне пустое письмо с темой Host. Сразу же вышлю Вам подробности акции) PS: Но лично я считаю, что для любого интернет проекта 25 рублей - это не такие большие деньги. Поэтому для маленького некоммерческого проекта "Как самому создать компьютерную игру" выбран именно этот хостинг. :) Да и вообще, на мой взгляд, очень хороший способ сэкономить деньги себе и своей фирме. PPS: Жалко только, что с возможностью размещения Perl и PHP скриптов появился большой соблазн их разместить. :) Поэтому готовый и отлаженный сайт на новом месте можно будет увидеть только к концу февраля. Но он намного лучше, чем старый. Так что скоро новоселье. :) Сегодня попытаемся сделать еще один шаг к освоению алгоритмов работы компьютерных игр. Может быть, у кого-то из Вас возникнет вопрос, зачем рассматривать "доисторические" алгоритмы, зачем учиться работать киркой и лопатой, если есть более мощные средства, позволяющие все сделать за короткое время и без особых усилий. Зачем возвращаться назад, в прошлое, и заново изобретать колесо. На все это могу сказать, что перед тем, как создавать очень красивые игровые шедевры, необходимо разобраться в алгоритмах. Это основа для всего остального. Многие начинающие программисты сетуют на то, что в современных книжках подробно написано, на какие кнопочки нажимать, а вот об алгоритмах совсем мало. Сейчас не уделяют особенного внимания азам. Еще более обидно то, что старые книжки, где все эти премудрости были расписаны, уже не переиздаются. Итак, начнем с самого главного - фундаментальных алгоритмов для создания игр. Ну и коль скоро мы заговорили о виртуальной реальности, давайте рассмотрим устройство классической игры Wolfenstein 3D фирмы IDSoftware. Будет лишним сказать, что игра эта имела в свое время огромный успех. Начнем с рассмотрения основ. Наверное, одно из самых ключевых понятий Wolfenstein 3D - игровое поле. Оно представляет собой лабиринт. Конечно в общем случае для игр виртуальной реальности нет ограничений на высоту стен, на угол, под которым эти стены расположены, на наличие потолка и на замкнутость лабиринта. В нашем же случае смело можно отображать все игровое поле на клетчатой бумаге. :) Кроме того, в игре приняты следующие допущения. Стены могут быть только вертикальные и горизонтальные. Расстояние между потолком и полом и высота "глаз" над уровнем пола везде одинаковы. Еще немного упростим модель и будем рассматривать матрицу, элементами которой являются числа, полностью определяющие игровое поле, положение игрока и противников, а также все предметы. Каким образом это можно сделать. Ну, во-первых, каждый элемент - либо стена, либо нет. Для стены указана текстура всех четырех боковых граней. На элементе "не стена" может находиться либо игрок, либо противник, либо ничего. Дополнительно в любом таком элементе (клетке) может находиться какой-нибудь предмет. Теперь рассмотрим способы отображения игрового поля. Для этого нам понадобиться алгоритм трассировки лучей (ray casting), а точнее его двумерная интерпретация. Вот зачем он нужен. Рано или поздно при отрисовки некоторого числа поверхностей возникает вопрос о том, а действительно ли необходимо выводить на экран все из них. Оказывается, что нет. Некоторые стены можно выводить не до конца, т.к. они перекрываются другими, некоторые вообще не видны. Как же проверить, видно стену или нет. Для этого есть множество способов, но сначала определим одно важное понятие. Под камерой будем понимать объект, который имеет вектор направления (ракурс, угол зрения итд) и размерами этого объекта можно пренебречь. Можно также считать, что вместо камеры сидит наблюдатель и смотрит в сторону, заданную вектором направления. Посмотрим и мы на то, что видит этот наблюдатель. Заметим, что на экран как раз это и нужно выводить. Есть метод вывода на экран путем построчного сканирования. (вывод по строкам) В данной конкретной ситуации удобнее будет вывод на экран по столбцам. Пока что попробуем свести задачу определения ближайших граней а пространстве, к аналогичной задаче на плоскости. Посмотрим на игровое поле сверху. Получилась задача на плоскости 0XY. (см. рисунок) Двумерная интерпретация алгоритма трассировки лучей заключается в следующем. Из точки, где находиться наблюдатель, выпускается пучок лучей. Далее для каждого луча находиться его ближайшее (первое) пересечение с другими объектами (в нашем случае со стенами). Но это еще не все. Просто алгоритм, где проверяются все стены на пересечение с лучом малоэффективен. Приятнее воспользоваться тем, что стены являются сегментами линий сетки. Для этого пересекаемые лучом клетки отслеживаются в порядке распространения от начала до того, пока не будет встречена непустая клетка. (см. рисунок) Реализуем все это следующим образом. Проверим на пересечение с лучом сначала вертикальные, а потом горизонтальные стены. Далее выберем ближайшее пересечение. Теперь попробуем реализовать всю эту теорию на практике. Для этого будут использованы уже готовые наработки, по которым сам учился всем этим алгоритмам в университете. (для кодов использовался язык Си; компилировать можно на borland c, watcom с или любом другом, который близок к стандарту ANSI) Пусть наблюдатель находиться в точке с координатами (x*,y*), а угол между лучом и положительным направлением оси 0X равен alpha. Будем считать, что клетка, содержащая игрока, имеет индекс (i*,j*), а шаг сетки равен h. Найдем точки пересечения луча с вертикальными линиями x=ih. Если направляющий вектор луча имеет положительную х-составляющую (cos(alpha)>0 => alpha от -pi/2 до pi/2), то ближайшая точка будет иметь координаты x1=(i*+1)h, y1=y*+(h-(x*-i*h))tan(alpha)=y*+(x1-x*)tan(alpha). При этом эта точка лежит на границе клетки (i*+1,[y1/h]). Если эта клетка занята стеной, то пересечение сразу получается в точке (x1,y1). При этом расстояние до точки пересечения будет равно d=(x1-x*)/cos(alpha). Если же эта клетка пуста, то проверяем следующие точки: Xi+1=Xi+h, Yi+1=Yi+htan(alpha). Здесь i-ая точка соответствует клетке (i*+i,[Yi/h]). Проверка происходит до тех пор, пока мы либо не наткнемся на занятую клетку, либо не выйдем за пределы лабиринта. Если клетка, соответствующая точке пересечения (Xi,Yi), занята, то расстояние до этой точки будет d=(Xi-x*)/cos(alpha). Теперь рассмотрим случай, когда cos(alpha)<0 (alpha то pi/2 до 3pi/2). Ближайшая точка пересечения луча с линией вида x=ih описывается формулами: x1=i*h, y1=y*-(x*-x1)tan(alpha)=y*+(x1-x*)tan(alpha). Первой проверяется клетка (i*-1,[y1/h]). Если она занята, то пересечение найдено и расстояние до точки пересечения равно d=(x1-x*)/cos(alpha). Иначе необходимо проверить остальные точки пересечения: Xi+1=Xi-h, Yi+1=Yi-htan(alpha). Для точки (Xi,Yi) следует проверить клетку (i*-i,[Yi/h]). Если она занята, то расстояние до точки пересечения составит d=(Xi-x*)/cos(alpha). Итак, получаем следующий код. float checkVWalls ( float angle ) { int xTile = (int) locX; // cell indices int yTile = (int) locY; float xIntercept; // intercept point float yIntercept; float xStep; // intercept steps float yStep; int dxTile; if ( fabs ( cos ( angle ) ) < 1e-7 ) return 10000.0; if ( angle >= ANGLE_270 || angle <= ANGLE_90 ) { xTile ++; xStep = 1; yStep = tan ( angle ); xIntercept = xTile; dxTile = 1; } else { xTile --; xStep = -1; yStep = -tan ( angle ); xIntercept = xTile + 1; dxTile = -1; } // find interception point yIntercept = locY + (xIntercept - locX) * tan ( angle ); for ( ; ; ) { yTile = yIntercept; if ( xTile < 0 || xTile > 52 || yTile < 0 || yTile > 9 ) return 10000.0; if ( worldMap [yTile][xTile] != ' ' ) return ( xIntercept - locX ) / cos ( angle ); xIntercept += xStep; yIntercept += yStep; xTile += dxTile; } } Точно так же находиться ближайшая точка пересечения луча с горизонтальными линиями сетки y=ih. При alpha от 0 до pi: x1=x*+(y1-y*)ctg(alpha), y1=(j*+1)h, Xi+1=Xi+hctg(alpha), Yi+1=Yi+h. При alpha от pi до 2pi x1=x*+(y1-y*)ctg(alpha), y1=(j*-1)h, Xi+1=Xi-hctg(alpha), Yi+1=Yi-h. Код для горизонтальной проверки выглядит так. float checkHWalls ( float angle ) { int xTile = (int) locX; int yTile = (int) locY; float xIntercept; float yIntercept; float xStep; float yStep; int dyTile; if ( fabs ( sin ( angle ) ) < 1e-7 ) return 10000.0; if ( angle <= ANGLE_180 ) { yTile ++; xStep = 1 / tan ( angle ); yStep = 1; yIntercept = yTile; dyTile = 1; } else { yTile --; yStep = -1; xStep = -1 / tan ( angle ); yIntercept = yTile + 1; dyTile = -1; } xIntercept = locX + (yIntercept - locY) / tan ( angle ); for ( ; ; ) { xTile = xIntercept; if ( xTile < 0 || xTile > 52 || yTile < 0 || yTile > 9 ) return 10000.0; if ( worldMap [yTile][xTile] != ' ' ) return ( yIntercept - locY ) / sin ( angle ); xIntercept += xStep; yIntercept += yStep; yTile += dyTile; } } После того, как найдены ближайшие точки пересечения луча как с вертикальными, так и с горизонтальными стенами, среди них выбирается наименее удаленная от игрока. Теперь нужно понять, как по найденному расстоянию d строиться столбец пикселей. Для этого нам нужно ввести расстояние f до картинной плоскости. (см. рисунок) Найдем величины отрезков AB, BC и CD через f и d. Условимся, что камера находиться посередине между потолком и полом. Т.е. AB=CD. Тогда BC=(SCREEN_HEIGHT*f)/d, AB=(SCREEN_HEIGHT-BC)/2. Реализация этого в коде ниже. void drawView () { float phi; float distance; totalFrames++; for ( int col = 0; col < 320; col++ ) { phi = angle + rayAngle [col]; if ( phi < 0 ) phi += 2 * M_PI; else if ( phi >= 2 * M_PI ) phi -= 2 * M_PI; float d1 = checkVWalls ( phi ); float d2 = checkHWalls ( phi ); distance = d1; if ( d2 < distance ) distance = d2; distance *= cos ( phi - angle ); // adjustment for fish-eye drawSpan ( distance, WALL_COLOR, col ); } } Для задания углов лучей здесь используется массив rayAngle - для каждого луча указан угол между направлением луча и направлением взгляда игрока. Наиболее простым способом описания этого массива является задание углов с постоянным шагом, но, т.к. этот способ вызывает некоторые искажения, применим следующий алгоритм: reyAngle[i]=arctg(tg(VIEW_ANGLE/2)*(1+i/(SCREEN_WIDTH-1))) Ниже приводиться полный текст программы. #include <bios.h> #include <dos.h> #include <math.h> #include <stdio.h> #include <time.h> #define VIEW_WIDTH ( M_PI / 3 ) // viewing angle ( 60 degrees ) #define SCREEN_WIDTH 320 // size of rendering window #define SCREEN_HEIGHT 200 // basic colors #define FLOOR_COLOR 8 #define CEILING_COLOR 3 #define WALL_COLOR 1 // angles #define ANGLE_90 M_PI_2 // 90 degrees #define ANGLE_180 M_PI // 180 degrees #define ANGLE_270 (M_PI*1.5) // 270 degrees // bios key defintions #define ESC 0x011b #define UP 0x4800 #define DOWN 0x5000 #define LEFT 0x4b00 #define RIGHT 0x4d00 // labyrinth char * worldMap [] = { "****************************************************", "* * *", "* * * * * * * * * * ************************** *", "* * *", "* * * * * * * * * * * ************************ *", "* * *", "* * * * * * * * * * ************************** *", "* * *", "****************************************************" }; float swing; float locX; float locY; float angle; long totalFrames = 0l; char far * screenPtr = (char far *) MK_FP ( 0xA000, 0 ); float rayAngle [SCREEN_WIDTH]; // angles for rays from main viewing direction ////////////////////////// Functions //////////////////////////////////// void drawSpan ( float dist, char wallColor, int x ) { char far * vptr = screenPtr + x; int h = dist >= 0.5 ? (int)(SCREEN_HEIGHT*0.5 / dist ) : SCREEN_HEIGHT; int j1 = ( SCREEN_HEIGHT - h ) / 2; int j2 = j1 + h; for ( int j = 0; j < j1; j++, vptr += 320 ) * vptr = CEILING_COLOR; if ( j2 > SCREEN_HEIGHT ) j2 = SCREEN_HEIGHT; for ( ; j < j2; j++, vptr += 320 ) * vptr = wallColor; for ( ; j < SCREEN_HEIGHT; j++, vptr += 320 ) * vptr = FLOOR_COLOR; } float checkVWalls ( float angle ) { int xTile = (int) locX; // cell indices int yTile = (int) locY; float xIntercept; // intercept point float yIntercept; float xStep; // intercept steps float yStep; int dxTile; if ( fabs ( cos ( angle ) ) < 1e-7 ) return 10000.0; if ( angle >= ANGLE_270 || angle <= ANGLE_90 ) { xTile ++; xStep = 1; yStep = tan ( angle ); xIntercept = xTile; dxTile = 1; } else { xTile --; xStep = -1; yStep = -tan ( angle ); xIntercept = xTile + 1; dxTile = -1; } // find interception point yIntercept = locY + (xIntercept - locX) * tan ( angle ); for ( ; ; ) { yTile = yIntercept; if ( xTile < 0 || xTile > 52 || yTile < 0 || yTile > 9 ) return 10000.0; if ( worldMap [yTile][xTile] != ' ' ) return ( xIntercept - locX ) / cos ( angle ); xIntercept += xStep; yIntercept += yStep; xTile += dxTile; } } float checkHWalls ( float angle ) { int xTile = (int) locX; int yTile = (int) locY; float xIntercept; float yIntercept; float xStep; float yStep; int dyTile; if ( fabs ( sin ( angle ) ) < 1e-7 ) return 10000.0; if ( angle <= ANGLE_180 ) { yTile ++; xStep = 1 / tan ( angle ); yStep = 1; yIntercept = yTile; dyTile = 1; } else { yTile --; yStep = -1; xStep = -1 / tan ( angle ); yIntercept = yTile + 1; dyTile = -1; } xIntercept = locX + (yIntercept - locY) / tan ( angle ); for ( ; ; ) { xTile = xIntercept; if ( xTile < 0 || xTile > 52 || yTile < 0 || yTile > 9 ) return 10000.0; if ( worldMap [yTile][xTile] != ' ' ) return ( yIntercept - locY ) / sin ( angle ); xIntercept += xStep; yIntercept += yStep; yTile += dyTile; } } void drawView () { float phi; float distance; totalFrames++; for ( int col = 0; col < 320; col++ ) { phi = angle + rayAngle [col]; if ( phi < 0 ) phi += 2 * M_PI; else if ( phi >= 2 * M_PI ) phi -= 2 * M_PI; float d1 = checkVWalls ( phi ); float d2 = checkHWalls ( phi ); distance = d1; if ( d2 < distance ) distance = d2; distance *= cos ( phi - angle ); // adjustment for fish-eye drawSpan ( distance, WALL_COLOR, col ); } } void setVideoMode ( int mode ) { asm { mov ax, mode int 10h } } void initTables () { for ( int i = 0; i < SCREEN_WIDTH; i++ ) rayAngle [i] = atan ( -swing + 2 * i * swing / ( SCREEN_WIDTH - 1 ) ); } main () { int done = 0; angle = 0; locX = 1.5; locY = 1.5; swing = tan ( VIEW_WIDTH / 2 ); initTables (); setVideoMode ( 0x13 ); int start = clock (); while ( !done ) { drawView (); if ( bioskey ( 1 ) ) { float vx = cos ( angle ); float vy = sin ( angle ); float x, y; switch ( bioskey ( 0 ) ) { case LEFT: angle -= 5.0*M_PI/180.0; break; case RIGHT: angle += 5.0*M_PI/180.0; break; case UP: x = locX + 0.3 * vx; y = locY + 0.3 * vy; if ( worldMap [(int) y][(int) x] == ' ' ) { locX = x; locY = y; } break; case DOWN: x = locX - 0.3 * vx; y = locY - 0.3 * vy; if ( worldMap [(int) y][(int) x ] == ' ' ) { locX = x; locY = y; } break; case ESC: done = 1; } if ( angle < 0 ) angle += 2 * M_PI; else if ( angle >= 2 * M_PI ) angle -= 2 * M_PI; } } float totalTime = ( clock () - start ) / CLK_TCK; setVideoMode ( 0x03 ); printf ( "\nFrames rendered : %ld", totalFrames ); printf ( "\nTotal time ( sec ) : %7.2f", totalTime ); printf ( "\nFPS : %7.2f", totalFrames / totalTime ); } Можно также с программой побаловаться и получить каркасное изображение. #include <bios.h> #include <dos.h> #include <math.h> #include <stdio.h> #include <time.h> #define VIEW_WIDTH ( M_PI / 3 ) // viewing angle ( 60 degrees ) #define SCREEN_WIDTH 320 // size of rendering window #define SCREEN_HEIGHT 200 // basic colors #define FLOOR_COLOR 0 #define CEILING_COLOR 0 #define WALL_COLOR 1 // angles #define ANGLE_90 M_PI_2 // 90 degrees #define ANGLE_180 M_PI // 180 degrees #define ANGLE_270 (M_PI*1.5) // 270 degrees // bios key defintions #define ESC 0x011b #define UP 0x4800 #define DOWN 0x5000 #define LEFT 0x4b00 #define RIGHT 0x4d00 // labyrinth char * worldMap [] = { "****************************************************", "* * *", "* * * * * * * * * * ************************** *", "* * *", "* * * * * * * * * * * ************************ *", "* * *", "* * * * * * * * * * ************************** *", "* * *", "****************************************************" }; float swing; float locX; float locY; float angle; long totalFrames = 0l; char far * screenPtr = (char far *) MK_FP ( 0xA000, 0 ); float rayAngle [SCREEN_WIDTH]; // angles for rays from main viewing direction int lastCell = -1; int lastJ1 = 0; int lastJ2 = 0; ////////////////////////// Functions //////////////////////////////////// void drawSpan ( float dist, char wallColor, int x, int cell ) { char far * vptr = screenPtr + x; int h = dist >= 0.5 ? (int)(SCREEN_HEIGHT*0.5 / dist ) : SCREEN_HEIGHT; int j1 = ( SCREEN_HEIGHT - h ) / 2; int j2 = j1 + h; if ( x > 0 && cell != lastCell ) // check for wall boundary { if ( lastJ1 > j1 ) lastJ1 = j1; if ( lastJ2 < j2 ) lastJ2 = j2; for ( int j = 0; j < lastJ1; j++, vptr += 320 ) * vptr = CEILING_COLOR; for ( ; j < lastJ2; j++, vptr += 320 ) * vptr = wallColor; for ( ; j < SCREEN_HEIGHT; j++, vptr += 320 ) * vptr = CEILING_COLOR; } else { for ( int j = 0; j < SCREEN_HEIGHT; j++, vptr += 320 ) * vptr = CEILING_COLOR; * ( screenPtr + x + 320*j1 ) = wallColor; * ( screenPtr + x + 320*j2 ) = wallColor; } lastCell = cell; lastJ1 = j1; lastJ2 = j2; } float checkVWalls ( float angle, int& cell ) { int xTile = (int) locX; int yTile = (int) locY; float xIntercept; float yIntercept; float xStep; float yStep; int dxTile; if ( fabs ( cos ( angle ) ) < 1e-7 ) return 10000.0; if ( angle >= ANGLE_270 || angle <= ANGLE_90 ) { xTile ++; xStep = 1; yStep = tan ( angle ); xIntercept = xTile; dxTile = 1; } else { xTile --; xStep = -1; yStep = -tan ( angle ); xIntercept = xTile + 1; dxTile = -1; } yIntercept = locY + ( xIntercept - locX ) * tan ( angle ); for ( ; ; ) { yTile = yIntercept; if ( xTile < 0 || xTile > 52 || yTile < 0 || yTile > 9 ) return 10000.0; if ( worldMap [yTile][xTile] != ' ' ) { cell = xTile; return ( xIntercept - locX ) / cos ( angle ); } xIntercept += xStep; yIntercept += yStep; xTile += dxTile; } } float checkHWalls ( float angle, int& cell ) { int xTile = (int) locX; int yTile = (int) locY; float xIntercept; float yIntercept; float xStep; float yStep; int dyTile; if ( fabs ( sin ( angle ) ) < 1e-7 ) return 10000.0; if ( angle <= ANGLE_180 ) { yTile ++; xStep = 1 / tan ( angle ); yStep = 1; yIntercept = yTile; dyTile = 1; } else { yTile --; yStep = -1; xStep = -1 / tan ( angle ); yIntercept = yTile + 1; dyTile = -1; } xIntercept = locX + ( yIntercept - locY ) / tan ( angle ); for ( ; ; ) { xTile = xIntercept; if ( xTile < 0 || xTile > 52 || yTile < 0 || yTile > 9 ) return 10000.0; if ( worldMap [yTile][xTile] != ' ' ) { cell = 1000 + yTile; return ( yIntercept - locY ) / sin ( angle ); } xIntercept += xStep; yIntercept += yStep; yTile += dyTile; } } void drawView () { float phi; float distance; totalFrames++; lastCell = -1; for ( int col = 0; col < 320; col++ ) { phi = angle + rayAngle [col]; if ( phi < 0 ) phi += 2 * M_PI; else if ( phi >= 2 * M_PI ) phi -= 2 * M_PI; int c1, c2; float d1 = checkVWalls ( phi, c1 ); float d2 = checkHWalls ( phi, c2 ); distance = d1; if ( d2 < distance ) { distance = d2; c1 = c2; } distance *= cos ( phi - angle ); // adjustment for fish-eye drawSpan ( distance, WALL_COLOR, col, c1 ); } } void setVideoMode ( int mode ) { asm { mov ax, mode int 10h } } void initTables () { for ( int i = 0; i < SCREEN_WIDTH; i++ ) rayAngle [i] = atan ( -swing + 2 * i * swing / ( SCREEN_WIDTH - 1 ) ); } main () { int done = 0; angle = 0.61; locX = 2.25; locY = 4.85; swing = tan ( VIEW_WIDTH / 2 ); initTables (); setVideoMode ( 0x13 ); int start = clock (); while ( !done ) { drawView (); if ( bioskey ( 1 ) ) { float vx = cos ( angle ); float vy = sin ( angle ); float x, y; switch ( bioskey ( 0 ) ) { case LEFT: angle -= 5.0*M_PI/180.0; break; case RIGHT: angle += 5.0*M_PI/180.0; break; case UP: x = locX + 0.3 * vx; y = locY + 0.3 * vy; if ( worldMap [(int) y][(int) x] == ' ' ) { locX = x; locY = y; } break; case DOWN: x = locX - 0.3 * vx; y = locY - 0.3 * vy; if ( worldMap [(int) y][(int) x ] == ' ' ) { locX = x; locY = y; } break; case ESC: done = 1; } if ( angle < 0 ) angle += 2 * M_PI; else if ( angle >= 2 * M_PI ) angle -= 2 * M_PI; } } float totalTime = ( clock () - start ) / CLK_TCK; setVideoMode ( 0x03 ); printf ( "\nFrames rendered : %ld", totalFrames ); printf ( "\nTotal time ( sec ) : %7.2f", totalTime ); printf ( "\nFPS : %7.2f", totalFrames / totalTime ); } А теперь посмотрим, как же сейчас применяются все эти алгоритмы. (правда, они уже оптимизированы для конкретных целей) Возьмем игру "Дальнобойщики". Спрашивается, почему нельзя описать игровое поле такой же матрицей? Вот. Так что классические алгоритмы применялись, и еще будут применяться. :) Дальше будем рассматривать не другие интересные алгоритмы, которые нашли свое применение в компьютерных играх. Наверняка, в процессе чтения рассылки или разработки компьютерных игр у Вас появляются какие-то вопросы или проблемы. Их решение можно поискать всем вместе. Пока сайт рассылки не обоснуется на новом месте, Ваши вопросы можно будет задать на старом форуме по адресу http://www.gmaker.f2s.com/forums/. (Все Ваши вопросы и ответы никуда не пропадут и благополучно переедут вместе с сайтом) Если по каким-либо причинам Вас это не устраивает, то можете задать вопросы по e-mail gamemaker@pisem.net. Задавая свой вопрос на форуме, посмотрите, (если есть такая возможность) может какая-то из уже открытых тем Вам знакома больше, чем тем, кто там уже что-то написал. Почему бы не помочь друг другу. :) В связи с переходом на платный хостинг, требуются опытные промоутеры, которые могут помочь с продвижением проекта (хотя бы советом). А то совсем не хочется платить деньги за сайт, о котором так никто и не узнает! Адрес для связи: gamemaker@pisem.net?Subject=Promo. Вот Вы и прочитали очередной выпуск рассылки. Может быть он не самый удачный или же Вы не нашли в нем много, что искали. Не расстраивайтесь! Просто напишите мне о том, какой бы Вы хотели видеть рассылку. Что понравилось, что нет, чего не хватает. Также обо всем, что осталось непонятным можно спросить по, уже знакомому Вам, адресу: gamemaker@pisem.net. Все картинки выпуска, если по каким-то причинам Вам потребуется их скачать, (вместо того, чтобы посмотреть страничку в браузере) находятся по адресу http://www.gmaker.f2s.com/gmimg/013_001.jpg-013_008.jpg. И последнее. Следующий выпуск не придется ждать так долго, как этот. Прошу прощения за перерыв в выходе рассылки (она не выходила уже долго, если кто не знает). До скорой встречи! SlyMagic.
Использование любых материалов рассылки возможно только с разрешения автора. Тираж 6000 экземпляров. |
http://subscribe.ru/
E-mail: ask@subscribe.ru |
Отписаться
Убрать рекламу |
В избранное | ||