Mighty Final Fight. Vol.2

MFF_logo.png

volume 2

-3 :: -2 :: -1 :: 0 :: 1 :: 2 :: 3 

Предисловие

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

Тем не менее, мы слегка обойдем это правило, и начнем с реализации масштабирования, создания титульного меню и смены управления.

Чтобы не перегружать вас информацией, я решил разбить описание этих задач на два выпуска. Сегодня мы выведем увеличенный вдвое логотип capcom на экран. А в следующей статье реализуем смену управления, создадим меню и внедрим шрифт из спрайта.

Эти задачи не привязаны к конкретному жанру и будут полезны всем, особенно в предверии очередного гаминатора.

Подготовительные действия

Договоримся сразу, что я не буду давать дополнительных инструкций, если что-то делается по умолчанию.

То есть, если встречается фраза "добавить комнату", значит надо просто добавить комнату и ничего в ней не менять.

gm1.png

Начнем с глобальных настроек. По сравнению со стандартом нам надо поставить галочку напротив пункта don't draw a border in windowed mode. Не переживайте, в итоге у нас будет стандартное окно windows, но запускать игру придется именно так, чуть ниже я отвечу зачем.

gm2.png

Убираем галочки с Esc (многие pc'шники привыкли, что Esc вызывает паузу или меню), с F1 (на ваше усмотрение, как и все остальное, но, мне кажется, что информация об игре во всплывающем окне некрасива), ну и отменяем загрузку/сохранение по F6/F5.

Масштабирование

Пришло время создать первую игровую комнату:

rm_init_0

В свойствах комнаты указываем ее ширину и высоту -- один на один пиксель.

Эта комната специальная и практически невидимая (именно для этого мы убрали отображение окна в начальных настройках). В ней мы будем создавать служебные объекты (например, для масштабирования), подгрузим ini-файл с описанием управления и так далее.

Так как для масштабирования мы будем использовать приемы, описанные в этой статье -- обойтись без этой комнаты мы не сможем.

Создаем первый объект:

obj_scaling_0

Напоминаю, что числа в конце имени -- это уникальный индекс объекта (спрайта, комнаты и т.д.). По умолчанию объект носит имя "objectN", где N -- индекс. Оставляйте этот индекс в названии обязательно.

То есть если вы уже насоздавали и наудаляли объекты в исходнике, то объект надо будет назвать obj_scaling_N, где N - индекс, с которым создатся объект.

В свойствах объекта отмечаем, что он должнен быть persistent, то есть не удаляться при смене комнаты. По умолчанию, все экземпляры всех объектов удаляются при смене или перезапуске комнаты (то есть при событии Room End). Так как этот объект будет масштабировать всю графику во всей игре -- он удаляться не должен.

Скрипты мы возьмем из упомянутой статьи, однако немного модифицируем один из них.

(позднее я дам ссылки и на скрипты, и на исходник)

Менять мы будем скрипт screen_init()

//начальные установки 
screen_x = 0;
screen_y = 0;
screen_w = 256;
screen_h = 224;
screen_scale = 2;
checking_room = -1;// создаем поверхность
screen = surface_create(screen_w,screen_h);
draw_clear(0);/*
проверяем, сумела ли видеокарта создать поверхность.
если нет, то используем встроенное масштабирование ГМ,
при котором сглаживания происходить не будет, так как видеокарта не потянула
*/
if ( screen == -1 )
{
window_set_size(screen_w * screen_scale,screen_h * screen_scale);
window_set_region_scale(screen_scale,1);
window_center();
screen_scale = 1;
instance_destroy();
}//проходим по всем комнатам и задаем начальные установки
do
{
if ( checking_room == -1 ) { checking_room = room_first; } else { checking_room = room_next(checking_room); }
room_set_background_color(checking_room,c_black,false);
room_set_width(checking_room,screen_w);
room_set_height(checking_room,screen_h);
room_set_view_enabled(checking_room,true);
room_set_view(checking_room,0,1,0,0,screen_w,screen_h,0,0,screen_w * screen_scale,screen_h * screen_scale,100,32,-1,-1,-1);
}
until ( checking_room == room_last )

Логика осталась прежней, кроме последнего цикла.

В этом цикле мы проходим по всем игровым комнатам, устанавливаем ширину и высоту (а она будет меняться у нас только в игровом уровне, все остальные комнаты представляют из себя статичные в смысле движения по бэкграунду экраны), разрешаем использование view и задаем параметры view.

Обращаю внимание на два момента.

1. checking_room == -1, а не checking_room = -1

Проблема в том, что в ГМ и для операции присваивания, и для операции проверки тождества используется знак "равно" (=). При этом ГМ понимает, что "==" это проверка тождества. Рекомендую сразу использовать правильные знаки.

Приведу пример чем грозит нарушение этой рекомендации.

a = 0;
b = 0;
a = b = 1;

В обычных языках программирования в результате выполнения этого кода переменной a будет присвоено значение 1 (справа налево: b = 1; a = b).

ГМ интерпретирует это так: a = ( b == 1 ), что даст нам в результате a = 0. То есть второе "равно" он воспримет как проверку условия, в данном случае тождества.

2. Порядок комнат

В ГМ есть несколько функций, позволяющих работать со следующими или предыдущими комнатами. Но какая комната следующая?

Порядок определяется сортировкой в окне объектов.

gm3.png

На картинке выше вы видите, что комната rm_controls_3 будет следующей для комнаты rm_logos_1 и предыдущей для rm_title_2.

3. Цвет бэкграунда в комнате 

По умолчанию, комната заполняется цветом, который вы указываете в свойствах комнаты. 

Вот этой строкой

 room_set_background_color(checking_room,c_black,false);

мы отключаем рисование фона. Во-первых, потому что нам нужен черный цвет, а именно таким и будет фон, если убрать нужную галочку. Во-вторых, потому что заполнение комнаты цветом тоже довольно медленная функция, пользоваться которой без необходимости не стоит.

Итак. Объект добавлен в исходник, нужно создать его в комнате.

gm4.png

Открываем вкладку settings в окне комнаты и пишем в Creation Code:

if ( !instance_exists(obj_scaling_0) )
{
instance_create(0,0,obj_scaling_0);
)

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

Запись !instance_exists(obj_scaling_0) равноценна записи instance_exists(obj_scaling_0) != true или instance_exists(obj_scaling_0) == false.

Не забудем и о том, что мы отобрали у клавиши Esc возможность выходить из игры. Соответственно и "крестик" в окне работать не будем.

Для этого добавим в событии Close Button у объекта obj_scaling_0 следующий код и восстановим справедливость:

game_end();

Заставка

Мы условились, что в комнате заставки будем показывать логотипы компаний-разработчиков. В данном случае, у нас есть только один логотип capcom.

Создаем комнату rm_logos_1. Создаем объект obj_company_logos_1 и добавляем спрайт sprite1 (я не именую спрайты, вы же можете называть как угодно, не забывая про индекс).

capcom_logo.png

Тут идет небольшая несостыковка с повествованием и индексом спрайта. Я буду описывать по факту, чтобы не запутаться в дальнейшем самому. Вы же можете предположить, что до этого я уже создал какой-то спрайт с индексом 0.

В событии Create пишем:

sprite_index = 1;
alarm[0] = 60;

Этим кодом мы присваиваем спрайт с логотипом и заводим будильник на 2 секунды.

Еще один момент, который надо затронуть.

Все временные переменные можно задавать не жестко, в зависимости от цели. Например, в данном случае мы знаем, что room_speed комнаты равна 30. Поэтому для 2-х секунд мы жестко прописываем 60.

В игре мы можем манипулировать скоростью комнаты, при увеличении которой будильник сработает раньше 2-х секунд.

Если же мы не хотим ускорения игрового процесса и будем играться со скоростью комнаты в других целях, то можно написать так:

alarm[0] = room_speed * 2;

В этом случае будильник сработает через две секунды внезависимости от скорости комнаты.

Ну и главный вопрос, зачем мы используем здесь будильник? Что произойдет по его звонку?

Использовав поверхность (surface) для реализации масштабирования, мы добровольно отказались от эффектов перехода между комнатами. Однако, так как мы пытаемся сделать максимально близко к оригиналу (да и просто для красоты), хотя бы обычное затемнение (fade out) нужно реализовать.

Для этого мы создаем отдельный объект obj_fade_out_2 и присваиваем ему sprite0.

black_screen.png

Как вы видите, это сплошной черный прямоугольник размером с игровое окно (почему мы используем спрайт, а не рисуем черный прямоугольник в помощью функций draw -- я расскажу в следующих выпусках).

В событии Create пишем:

fade_var = 0; //начальная прозрачность
fade_speed = 0.03; //скорость затемнения
depth = -10000; //отрицательная глубина для того, чтобы объект отрисовывал ближе к экрану
sprite_index = 0; 
image_alpha = fade_var; //задаем начальную прозрачность

В событии Step:

x = view_xview; //рисуем всегда во view
fade_var += fade_speed;
if ( fade_var > 1 ) { instance_destroy(); }
image_alpha = fade_var;

Картинка будет затемняться приблизительно за одну секунду, а потом экземпляр объекта удалится. Так как мы ввели этот объект как затемнение перед сменой комнат, то функции переходов мы можем записать в событии Destroy:

switch(room)
{
case 0: room_goto(1); break;
case 1: room_goto(2); break;
//далее будут описываться все варианты переходов из комнаты в комнату
default: room_goto(2); break;
}

Соответственно, если нам нужно будет сменить комнату, мы создадим этот объект и он выполнит переход.

Возвращаемся к будильнику объекта obj_company_logos_1.

В событии Alarm0 пишем:

instance_create(0,0,obj_fade_out_2);

В событии Room End любого не постоянного объекта я предлагаю писать instance_destroy();, хотя ГМ обещает удалять все экземпляры при смене комнат, перестраховаться и убрать за собой мусор будет не лишним.

Так. Объекты мы описали, надо добавить объект с логотипом в соответствующую комнату.

rm_logos_1 Creation Code:

window_set_showborder(true); //возвращаем окно windows
instance_create(87,95,obj_company_logos_1); //добавляем картинку сразу в нужное место

Теперь игра автоматически перейдет из комнаты с логотипами в титульное меню (эту комнату мы еще не создавали). Однако надо еще попасть в комнату с логотипами, для этого в коде комнаты инициализации пишем:

(instance_create(0,0,obj_fade_out_2)).fade_speed = 1;

Хитрость в том, что мы создаем экземпляр объекта и тут же меняем ему значение переменной (в данном случае скорость затемнения).

В общем случае, чтобы обратиться к переменным конкретного экземпляра нужно написать:

(id_экземпляра).переменная

Если бы нам надо было поменять сразу несколько переменных, то мы воспользовались бы таким кодом:

fade_id = instance_create(0,0,obj_fade_out_2);
(fade_id).fade_speed = 1;
(fade_id).fade_var = 0.5;
 

Итого

Главный вопрос следующий -- нужна ли кому-нибудь подобная скурпулезность или за ней мы не увидим общей логики?

  • yeo
  • 10 февраля 2012, 16:55