Пишу без особой охоты, дабы, наконец, разъяснить тему трехмерности в Game Maker, поставить на ней жирнейшую точку и более никогда к ней не возвращаться. В этом, с позволения сказать, уроке я постараюсь наиболее понятно и наглядно объяснить, что такое 3D и с чем его едят на примере банальнейшего First Person Shooter (FPS). Ну что же, настроения у меня особо распинаться нет, так что погнали.
Для начала, пару слов о том, почему 3D игра на Game Maker - очень плохая затея. Во-первых, это дичайшая проблема с коллизиями. Все тематические функции в Game Maker проверяют столкновения объектов исключительно в двух плоскостях, а тут их три. Могли бы помочь трехмерные массивы, да вот только их давно уже выпилили. Хотя и без того они работали на соплях: количество ячеек ограничивалось довольно весомо для трехмерного проекта, оттого создать объемную локацию было затруднительно. В итоге оставалось и остается либо писать себе dll-ку для нормальной проверки коллизий, либо бегать с шаманским бубном вокруг компьютера. Собственно, вот и ответ на вопрос, почему сегодня я НЕ расскажу как нормально реализовать запрыгивание на объекты.
Вторая проблема не менее важная - это построение уровня. Опять же заключается она в том, что редактор комнат Game Maker работает исключительно в двух плоскостях. И тут никак не выкрутишься: придется писать свой собственный редактор уровней, дабы мочь нормально работать. В общем, кратко подводя итоги, качественное 3D в Game Maker более чем возможно, но никому не нужно: легче использовать специализированный софт. Есть, конечно, люди, пишущие трехмерные движки для Game Maker, но делают они это чисто из интереса, как некоторые пользователи квестовых-движков пытаются сделать на них что-то иное просто ради фана.
Что ж, а теперь перейдем к разработке нашего шутера от первого лица. И, что нам надо во-первых, так это включить трехмерное отображение объектов. Для этого создаем любой объект (например, obj_camera) и для него в событии Create пишем:
Пусть этот объект и будет у нас игроком. Создадим какой-нибудь спрайт, который в дальнейшем будет являться маской столкновений нашего игрока, и, собственно, присваиваем его нашему объекту. У меня такой спрайт будет представлять собой обыкновенный квадратик размером 16x16.
Далее продолжим настраивать игру для отображения графики. Для того в событии Create нашего пока единственного объекта после предыдущего кода пишем следующее:
Create
z=32; //Высота нашего объекта
draw_set_color(c_white); //Цвет отрисовки
room_speed=60; //Для плавного движения
depth=1000; //Устанавливаем глубину объекта |
Собственно, первой строчкой мы создали переменную z, в которой будет храниться координата нашего игрока по известной оси (оси Z, коль кто не догнал). Говорю сразу, дабы не было недопонимания, что эта переменная условна, служит только для построения перспективы и не является реальным положением игрока в комнате (а то в прошлый раз замучали со своими вопросами, а-ля, «я изменил координату z до over 9999; почему я не могу перепрыгнуть через ящик?»). Второй строчкой мы попросту изменили цвет отрисовки, дабы все объекты в комнате не были выкрашены в матово черный цвет. Третьей строкой я просто изменил скорость комнаты для более плавного движения. Ну и последней строкой мы устанавливаем глубину объекта, дабы он всегда выполнялся первым (надо для корректной проекции в комнате).
Далее нам устанавливаем эту самую проекцию. Для того в событие Draw пишем:
Draw
toX= x+lengthdir_x(1,direction);
toY= y+lengthdir_y(1,direction);
toZ= z;
d3d_set_projection(x, y, z, toX, toY, toZ, 0, 0, 1); |
Отчего-то все авторы уроков предпочитают понапихать в код установки проекции всяких синусов и косинусов, оставляя читателя разгадывать свои мотивы, как круги на кукурузных полях. Я же попытаюсь, наконец, объяснить как оно работает. Итак, для того, чтобы построить перспективную проекцию нам надо всего-то указать координаты самой камеры и координаты той точки, куда эта камера смотрит. Собственно, x, y и z - это и есть координаты нашей камеры. toX, toY и toZ - координаты той точки, куда мы смотрим. Ну а 0, 0 и 1… Да, совсем забыл сказать, что надо указать еще и верхнюю (главную) ось. Для того нам надо всего-то в одном из этих трех пунктов (x,y,z) поставить единицу. В нашем случае верхняя ось - z.
Ну а теперь пару слов о том, как мы наши ту точку, куда смотрит наш игрок. Хотя, чего тут говорить? Взгляните на рисунок. При помощи функций lengthdir_x и lengthdir_y мы ищем на окружности O координаты точки, находящиеся на расстоянии в 1 у.е. и под углом, равным direction. А затем к найденной точке просто прибавляем текущую координату объекта. Вот и вся хитрость.
А теперь о том, как можно реализовать горизонтальный обзор мышью. Прямо перед предыдущим кодом в событии Draw пишем:
Draw
direction -= (display_mouse_get_x() - display_get_width()/2)/10;
display_mouse_set(display_get_width()/2,display_get_height()/2); |
Суть скрипта проста: ищем, на сколько пикселей отклонился курсор от центра экрана, и изменяем переменную direction на полученное значение. После возвращаем курсор на место. Формула довольно тривиальна - кто хочет, может самостоятельно ее разобрать.
Далее учим игрока двигаться. Для начала реализуем движение вперед:
Key UP ("W")
xx=x+lengthdir_x(2,direction);
if place_free(xx,y) x=xx;
yy=y+lengthdir_y(2,direction);
if place_free(x,yy) y=yy; |
Тут всё работает примерно так же: ищем точку впереди нас и двигаемся туда. Чтобы не было залипаний в стенах, проверяем коллизию по осям X и Y отдельно. Переходим к движению назад. И здесь все абсолютно идентично. Единственно отличие от движения вперед - нам надо двигаться не прямо, а в обратную сторону. Для того попросту от переменной direction отнимаем 180 (градусов):
Key Down ("S")
xx=x+lengthdir_x(2,direction-180);
if place_free(xx,y) x=xx;
yy=y+lengthdir_y(2,direction-180);
if place_free(x,yy) y=yy; |
Думаю, вы уже догадались, как можно реализовать стрейф. Писать код не буду: лишь скажу, что для движения влево надо прибавить к direction 90 градусов, а для движения вправо - напротив отнять 90.
Ну а теперь добавим какие-нибудь иные объекты в комнату, дабы было вокруг чего походить. Создаем «твердый» объект obj_wall и для него присваиваем любой квадратный спрайт, размером равным степени двойки (32x32, 64x64, 128x128 и т.д). Это важно для того, чтобы текстура накладывалась корректно. Далее в событии Create пишем скрипт для получения этой самой текстуры:
Create
tex=sprite_get_texture(sprite_index,0); |
Стоит помнить, что в комнате объект будет того же размера, что и наша текстурка. Если вам надо сделать так, чтобы в игре объект занимал площадь равную, к примеру, 32x32, но при этом имел текстурку, размером, эдак, 256x256, то будет более разумно загрузить эту самую текстуру из бэкграунда, а спрайт оставить нужного размера (32x32):
Create
tex=background_get_texture( background ); //загрузка текстуры из бэкграунда |
Ну а теперь переходим в событие Draw объекта obj_wall и пишем код, отображающий «коробку»:
Draw
d3d_draw_block(x,y,0,x+sprite_width,y+sprite_height,32,tex,1,1); //Подробнее о функциях рисования смотрим в справке |
Вот и все! Надеюсь, все непонятные моменты, касательно 3D FPS, я смог уяснить. Говорю сразу, что не продлеваю тему и не объясняю, как можно реализовать врагов, пули и т.д, только потому, что смысла в этом не вижу: всё реализуется абсолютно идентично обычному TDS (просто не так наглядно). Однако если программирование пуль и прочей ерунды нужно - пишите в комментах: я ленивый, но примерчик на досуге-таки могу накатать.
|