Управляємо купою таймерів в JavaScript

У минулому пості було про те, як я писав гру для конкурсу js13kGames, мета якого — вмістити свою саморобку на стеку відкритих web-технологій в 13 кілобайт.
Крім хитрощів з минификацией, гра надихнула мене на створення інструменту для управління великою кількістю таймерів шляхом обертання їх у зручний інтерфейс і об'єднання в групи. Код та кейси, в яких це може стати в нагоді — під катом.

Демо, де можна позапускать ракети і зацінити пару прикладів коду
Де це можна використовувати?
Часто, логіка в іграх розбивається на стану (states) — невеликі незалежні частини, наприклад: меню, рівень, екран перемоги. Якщо припустити, що гравець може в процесі гри перейти в меню, поставивши відбувається в рівні на паузу, нам потрібно перейматися збереженням контексту таймерів.
В моєму, порівняно небагату, досвід, я зустрічав таке рішення цієї проблеми. Всередині кожного об'єкта з логікою, зав'язаної на час, створюються свої таймери, а при знищенні об'єкта вони очищаються. Недолік цього рішення в тому, що всі таймери потрібно очищати вручну.
Ще один приклад. Якщо ігровий простір складається з безлічі невеликих локацій (згадаймо, наприклад, The Binding of Isaac), ми можемо захотіти зберігати стану об'єктів і одиниць на деяких з них. Знову ж таки, потрібно прописати можливість поставити лічильники на паузу для кожного юніта.
timestore
Основна ідея — створити "простір таймерів", яке можна цілком ставити на паузу, відновлювати, очищати і переиспользовать. Перша, примітивна версія з'явилася вже в процесі розробки гри, а за минулі вихідні я написав більш адекватний варіант — timestore.
як таймерів використовуються два класи:
Timeout
та
Interval
. Всередині обидва використовують
setTimeout()
. В обох класів є методи з промовистими назвами:
.clear()
,
.pause()
,
.resume()
та ще деякі. Таймери можна використовувати безпосередньо, але основна фішка — в класі
Timestore
.
Коли ми створюємо таймер через timestore, він зберігається в колекцію, і після нього можна звернутися не тільки безпосередньо, але і по ID:
var gameTimers = new timestore.Timestore(),
simpleTimeout = gameTimers.setTimeout(callback, 5000),
timeoutWithCustomId = gameTimers.setTimeout('customId', callback, 5000);

someButton.on('click', function () {
simpleTimeout.clear();
gameTimers.clearTimeout('customId');
});

Важливо: не можна використовувати числа (і рядки на кшталт
'10'
) як кастомних ID, оскільки числа використовуються як стандартні ідентифікатори всередині timestore.
Керування цілими колекціями:
gameTimers.pauseAll();
menuTimers.resumeAll();

Якщо таймеру передати прапор
fireBeforeClear
, то в момент очищення він спрацює:
var lightBulb = new Interval(toggleLight, 100, true);

function switchOff() {
lightBulb.clear(); // У цей момент світло переключиться в останній раз.
}

Ще один корисний метод —
.getTimeLeft()
. Він повертає кількість мілісекунд до наступного спрацювання таймера.
Що далі?
У планах на найближчі два-три дні:
  • додати перевірку користувальницьких ID, щоб не можна було передати число;
  • написати методи типу
    .clearIntervals(['id1', 'id3', 'id5'])
    , щоб було зручніше керувати частинами колекції;
  • додати
    Interval.fireCounter
    — властивість, що показує, скільки раз спрацював таймер;
  • і споріднений метод
    Interval.clearIn(times)
    , що дозволяє очистити таймер через кілька спрацьовувань.
Також найближчим часом викладу пару прикладів на JSFiddle і CodePen.
В даний момент є сторінка з прикладами.
PS. До речі, роблячи timestore, я вперше використав тести і, для тих хто ще сумнівається, скажу: тести — це круто!
PPS. Перш ніж почати писати код, я спробував знайти аналоги, але не зустрів нічого схожого. Може бути, я просто погано шукав, а вони є? Поділіться, подалуйста, якщо зустрічали щось подібне.
PPPS. Про стани, які states, можна детальніше почитати тут.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.