Article
OO
Oliver Orangers
04/04/25
Урок 09. Таймер для комнаты

Добрый день, уважаемый друг!

Мы продолжаем наш цикл статей про разработку своего рогалика на Gamemaker!

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

Для начала мы создадим новый объект. Назовем его oTimer.

Добавим событие “создать”, в котором заинициализируем несколько необходимых для нас переменных.

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

global.TimerMax и global.Timer - так я их назвал.

Напишем следующее:

global.TimerMax = 60*60;

global.Timer = global.TimerMax;

timerPointsPrev = global.TimerMax;

followUpTimerMax = 60;

followUpTimer = 0;


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


На второй строчке я сказал, что текущее время таймера равно максимальному, каким бы он не был. Это логично, что мы начинаем комнату с максимальным запасом времени.

Затем добавим переменную для отображения количества текущих поинтов у таймера.

А также “шаги” для изменения значений у таймера.

Затем добавим событие “шаг”, в котором скажем, что таймер уменьшается со временем:


//Таймер

if global.Timer > 0 global.Timer--;

followUpTimer = clamp(followUpTimer - 1, 0, followUpTimerMax);

if (followUpTimer == 0) timerPointsPrev = lerp(timerPointsPrev, global.Timer,0.2);

if global.Timer <= 0 and !instance_exists(oEnemy)

{

//Появляется торговец и заканчивается этот уровень

}


Выходит, мы написали, что если таймер больше 0, то он уменьшается на 1 каждый кадр. Затем мы добавили, что followUpTimer с помощью функции clamp будет поддерживаться в диапазоне от 0 до его максимума, но будет постоянно уменьшаться.

Если followUpTimer равен 0, то timerPoinsPrev возьмет функцию lerp, которая вернет 20 процентов от суммы timerPointsPrev и текущего значения таймера. Грубо говоря - мы создаем возможность рисовать разность от последнего значения до текущего в виде еще одной линии.

Это будет особенно полезно при рисовании урона по существам.

В конце напишем, что если global.Timer <= 0(то есть таймер закончился) и монстров нет, то в дальнейшем появится торговец и мы сможем покинуть этот уровень.

Это мы допишем позже, нам пока это не надо.

А сейчас отобразим наш таймер.

Добавим событие “изобразить”.

В нем для начала заинициализируем необходимые нам переменные, которые не нужны были в “создать”:

var spr = sHealthBar;

var spriteWidth = sprite_get_width(spr);

var spriteHeight = sprite_get_height(spr);

var xPosition = room_width/2;

var yPosition = 40;

var xOffSet = (xPosition - round(spriteWidth/2) + 1);

var yOffSet = (yPosition - round(spriteHeight/2));


Напишем, что переменная spr равна спрайту, который мы нарисуем для изобразить. Я его назову sHealthBar, так как эта полоска будет не только таймером, но и полоской жизни у боссов.

Как выглядит этот спрайт?

У него есть 4 кадра:


0 кадр - это фон для нашей полоски,

1 кадр - это подложка, которая показывает урон, полученный за последний промежуток,

2 кадр - это текущее количество урона,

3 кадр - это рамочка, она не обязательна, рисует поверх всей полоски и не меняется никак.

Спрайт называется sHealthBar. Лично у меня ее размер в ширину 240, в высоту 20. Точка основания находится слева.


Когда дорисуете полоску - продолжим:

var spr = sHealthBar;

var spriteWidth = sprite_get_width(spr);

var spriteHeight = sprite_get_height(spr);

var xPosition = room_width/2;

var yPosition = 40;

var xOffSet = (xPosition - round(spriteWidth/2) + 1);

var yOffSet = (yPosition - round(spriteHeight/2));


Далее напишем что ширина спрайта равна sprite_get_width(название спрайта), как и высота sprite_get_height(название спрайта). Это позволит нам автоматизировать эти данные и не вписывать вручную.

Добавим еще переменные для позиций:

xPosition = ширина комнаты делим на 2, получается в середине комнаты.

А высота 40 пикселей сверху.

Также добавим две переменные для обозначения середины спрайта;

Далее пишем:

draw_sprite_ext(spr,0,xOffSet,yOffSet,1,1,0,c_white,1);

draw_sprite_ext(spr,1,xOffSet,yOffSet,timerPointsPrev/global.TimerMax,1,0,c_white,1)

draw_sprite_ext(spr,2,xOffSet,yOffSet,global.Timer/global.TimerMax,1,0,c_white,1);

draw_sprite_ext(spr,3,xOffSet,yOffSet,1,1,0,c_white,1);

draw_sprite(sTimer,0,xOffSet-10,yOffSet-5);

if (global.Timer <= 0 and !instance_exists(oEnemy))

{

layer_set_visible("Pointer", true);

}


Используем draw_sprite_ext вместо draw_sprite, так как это позволит менять x_scale - то есть ширину спрайта, позволит сжимать по горизонтали, а это нам и нужно.

По порядку пишем что рисуются кадры, сначала 0, потом 1, и так далее.

Меняется то, меняется ли xscale и от чего зависит.

Так самое тут интересное это 1 и 2 кадр.


1 кадр - это полоска разницы урона(времени), поэтому - timerPointsPrev/global.TimerMax

а 2 кадр - это текущий показатель таймера, поэтому - global.Timer/global.TimerMax


Продолжим:

draw_sprite(sTimer,0,xOffSet-10,yOffSet-5);

if (global.Timer <= 0 and !instance_exists(oEnemy))

{

layer_set_visible("Pointer", true);

}


Рисуем sTimer, это будет мини значок, его можно не рисовать.


А если таймер кончился и врагов нет, то появится слой, который мы можем нарисовать и скрыть глазиком.


Теперь мы можем перенести этот объект в комнату и посмотреть, как это работает.


Круто! Но есть одно но, у нас монстры всё равно идут после конца таймера. Давайте просто удалим все спаунеры, если таймер кончился. А если спаунеров нет и монстров нет, то наш переход, который сначала был недоступно станет доступен, например, станет выше:

Зайдем в шаг oTimer вновь.

Добавим внизу:

//Блок Таймера игры

if global.Timer <= 0

{

if instance_exists(oSpawner) instance_destroy(oSpawner);

if !instance_exists(oEnemy) and (instance_exists(oRoomExit))

{

oRoomExit.y = 300;

}

}


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

Вот так вот! Теперь мы можем подумать и о создании торговца между уровнями и о прочих мелочах ;)

В следующем туториале мы добавим активные и пассивные предметы, а затем и торговца!


Подписывайся на мою группу ВК: https://vk.com/oliora.

И не пропускай следующие мои туториалы!


Ссылка на следующий урон: https://cr5.space/h/ru-gamemaker/post/12


Comments