Pull to refresh

Comments 21

В играх часто удобнее обходиться одним таймером с самым высоким разрешением для выбранного FPS (например, 60 тиков в секунду), который дёргает все модели _активной сцены_, которые внутри себя сами принимают решение о том, на какой тик и как реагировать. Так игровая логика получается более детерминированной (что особенно важно для игр «с физикой»). Т.е., лучше абстрагироваться не до «пространств таймеров», а до «сцен».
Да, для реализации этого единственного таймера нужно использовать requestAnimationFrame, в хэндлере проверяя, сколько прошло времени и пора ли уже «тикать».

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


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

Скорее всего получится наоборот незначительно меньше операций процессора, если тик всё равно отправляется в «общий аспект объекта» (проверки на наступление нужного тика в объекте versus проверка внутри евентлупа JS-движка + вызов асинхронной функции). Нативные таймеры в JS вовсе не «бесплатные». В любом случае, в этом месте вы вряд ли упрётесь в производительность (а если упрётесь, то будете вообще менять архитектуру движка или упрощать игровую логику).
Возможно, и так. Думаю, стоит написать бенчмарк, чтобы проверить. Займусь этим после выходных.

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


Ну и да, в отличие от requestAnimationFrame с подсчетом прошедшего времени руками, setTimeout крайне не стабильный по времени. Если так хочется — можно сделать подобную библиотеку в виде обертки над самим requestAnimationFrame.


Ну и да, все игровые движки, и книги, и вообще мануалы по созданию игр крутятся поверх так называемой game loop, в котором даже если и параллельно, однако обрабатывается все синхронно

В схемотехнике по фронту происходят не действия — а фиксация изменений в триггерах.


Хотелось бы увидеть пример "жуткого рассинхрона и потери данных" в js.

Даже если будут потери в 5-10 мс, это не критично для поведения, описанного выше. Естественно, все движения юнитов и коллизии должны считаться в game loop. Что касается путаницы, то можете глянуть код моего проекта из предыдущего поста. Мне кажется, там всё достаточно ясно.

А зачем создавать таймеры для каждого объекта? Вижу в этом смысл, только при разном интервале обновления объектов, но мне кажется, что в этом мало смысла. Не проще ли текущей сцене (меню или уровень) при обновлении сцены запускать функции обновления своих объекты, если они активны, а обновление текущей сцены запускать из одного единственного таймера? Если объект или сцена удалены, он уже никак не обновится, до кучи можно добавить им свойство enable и проверку на него сделать.

Переключение гл. меню/настройки/игра/уровни легко реализовать самописным сценменеджером, в нем же хранить ссылку на текущую сцену, лучше на две сцены, текущую и предыдущую используемую (возможна ситуация, что они обе должны обновляться, например скролинг он уровня к меню, когда мы видим сперва кусочек уровня и меню, а только потом меню целиком). И тогда вместо
gameTimers.pauseAll();
menuTimers.resumeAll();

будет что-то типа
sceneManager.SetScene(menu);

где
SetScene = 
function(scene) {
this.OldScene = this.CurrentScene;
this.CurrentScene = scene;
}

А в таймере вызывать обновление sceneManager, который обновит нужные сцены.
В том-то и дело, что определённое поведение повторяется через разные промежутки времени. Допустим, есть юнит-матка, который генерирует юнита-пчелу каждые 10 секунд и выстреливает вокруг себя кислотой каждые 4 секунды. Если засунуть эти действия в функцию обновления сцены, то при 60 fps в 10-секундном цикле будет 599 холостых проверок вида «а не пора ли нам сгенерировать пчелу». Если же поведение юнита ещё сложнее, «пустых» проверок будет ещё больше.

Да, я тоже за один общий таймер. А скорость объектов не дискретная.

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

Почему вам не нравятся обещания (Promises)? С ними же удобнее делать сложные последовательности анимаций.

Если честно, я просто не хочу усложнять. Возможно, позже, через пару версий, добавлю методы, возвращающие Promise. Может, сделаете форк? Что-то в духе timestore-promises?

Заметил у вас еще одну проблему: ваша функция setTimeout не является совместимой со стандартной.


То есть если захочется добавить возможность "заморозки" в существующий код — не получится просто пройтись по коду и заменить все setTimeout на какие-нибудь ts.setTimeout


Из отличий — ваша функция не умеет принимать параметры для функции обратного вызова и не умеет выполнять строку.

Вы про эти — setTimeout(calback, delay, [arg1], [arg2], [arg3]) — аргументы?

Каюсь, тут я опирался на старый стандарт. Скорее всего, добавлю метод .callWith(arg1, arg2, ...), который можно будет использовать в цепочке:


var t = ts.setTimeout(callback, 100).callWith(arg1, arg2);
t.pause(); // Метод .callWith() возвращает объект таймера.
Хорошо, что конкретно для анимации можно использовать WebAnimations, где можно управлять всеми состояниями

Я бы для анимации использовал что-то вроде PixiJS.

Sign up to leave a comment.

Articles