Создание интерактивного приложения немыслимо без обработки событий, будь-то опрос клавиатуры или работа таймера. Реализация этих важных действий зависит от платформы, языка программирования. В этой статье я хочу рассказать о некоторых особенностях использования событий для WebGL-движка Blend4Web. К сожалению, информации по этой теме совсем немного.
![](https://habrastorage.org/files/f0b/0ac/439/f0b0ac4395544cffa35fb20dc412c69c.jpg)
В любом приложении есть главный цикл, который, например, опрашивает клавиатуру, выполняет некие логические действия или отвечает за движение объектов. Так, в популярном движке Unity имеется специальное событие Update (), которое разработчики используют для большинства указанных выше задач. Причем, если продолжать рассматривать подходы разработчиков Unity к этому вопросу, то наиболее важной будет парадигма “объект — событие”, т.е. когда для каждого объекта в сцене имеются управляющие скрипты и индивидуальная “подписка“ на события.
Blend4Web предлагает аналогичный подход. Однако, он больше основан на событийной модели, которая также может быть привязана к конкретным объектам в сцене.
Вернёмся к началу статьи и рассмотрим механизм “главного цикла”.
Для не желающих изучать событийную модель, есть простейший вариант — функция append_loop_cb() из модуля main.
Пользоваться ей просто. Вызываете эту функцию, например, после инициализации движка и указываете имя обработчика в качестве параметра:
Теперь функция cb_update () будет вызываться каждый кадр. Причем это событие не привязано к какому-либо объекту. Его можно использовать для логических построений, но не опроса устройств ввода, как следовало бы ожидать. Вообще опрос клавиатуры, мыши и тому подобного — это отдельная тема, которую я рассмотрю чуть позже.
Функция append_loop_cb () проста в использовании и годится для несложной логики. Собственно, она считается устаревшей и оставлена в API для совместимости с ранее созданными приложениями. Сейчас появился более крутой механизм, основанный на событиях.
Событийная модель Blend4Web требует осмысления и некоторого привыкания. Но это только в начале. Главное понять принципы её работы, тогда всё становится ясным и удобным.
В основе ее находится понятие “сенсор”. Сенсор — это отдельная программная единица, ответственная за генерацию события на конкретное действие. Так, есть сенсоры клавиатуры, мыши, таймера, физики и т.д. Для создания каждого сенсора имеется своя функция. Кстати, вся подноготная событийной модели заключена в модуле controls.
Продолжим рассмотрение создания замкнутого цикла, но уже с помощью событий. Для этого в наборе сенсоров имеется специальная функция create_elapsed_sensor(). Например, так можно оформить ее вызов:
Сущность создана, но толка нет. И правильно, ведь на событие еще нужно подписаться. В API движка есть одна, универсальная функция, с большим количеством параметров: create_sensor_manifold(obj, id, type, sensors, logic_fun, callback, callback_param).
Весь код целиком:
После этого, каждый кадр будет вызываться функция main_cb, где можно разместить необходимую логику. Результат напоминает работу append_loop_cb, не правда ли? Вот только выглядит это действие несколько сложнее, но не спешите с выводами…
Вся изюминка работы с событиями заключена в параметрах, что передается в create_sensor_manifold. Разберем ключевые моменты ранее приведенного кода:
Итак, что мы имеем. А есть у нас три очень мощных рычага для работы с событиями. Во-первых, события могут быть привязаны к конкретному объекту в сцене. Во-вторых, каждый объект может быть подписан на несколько разных событий. Именно поэтому ссылка на созданный сенсор передается в виде единицы массива. В-третьих, можно управлять типом генерации событий. На самом деле, их не два варианта указанных выше, а гораздо больше. К тому же имеется специальный механизм, позволяющий использовать сложные логические построения для реагирования на события.
Лучший способ изучения чего-то нового — это практическая работа. Можно сколь-угодно сотрясать воздух теорией, но результат должен быть осязаем. Меня всегда умиляла непосредственность юных дарований, вопрошающих на форумах: “какой игровой движок выбрать, какой лучший язык программирования, сколько можно заработать…” Совет — сядьте и делайте. Нет ничего ценнее, чем собственноручно набитые шишки.
Вот и в этом случае, я предпочел проверить теорию на практике. Для полноценного изучения возможностей событийной модели Blend4Web было решено создать простенькое приложение и максимально использовать эти самые возможности. Но работа оказалась настолько интересной, что в результате получилась симпатичная мини-игра/новогодняя открытка, которая я даже разместил на главной странице моего сайта с играми. В конце статьи вы найдете ссылки на приложение и на все исходники. Пользуйтесь на здоровье.
Идея новогодней открытки проста и навеяна детскими воспоминаниями. Помните ключевую фразу “Раз, два, три — ёлочка гори”? Нечто подобное я решил воплотить в жизнь. Есть три кнопки, каждая из которых зажигает лампочки на елке соответствующего цвета. При этом звучит звук определенной высоты. Задача — найти правильную последовательность включения ламп. После нажатия кнопок, программа выполняет проверку результата и выводит сообщение: либо подсказку, либо переход на финал. Так как это все же больше новогодняя открытка, нежели игра, после нескольких неудачных попыток дается правильный вариант.
![](https://habrastorage.org/files/46b/72c/83f/46b72c83ff1c4a27bee173d8dcc9865e.jpg)
Движок Blend4Web не содержит встроенного GUI, поэтому предлагается два варианта: использовать объекты в Blender или работать через стандартные возможности HTML. Мне проще работать с Blender. В редакторе были созданы плоскости с текстурами кнопок и добавлены к ним анимации “выхода”. Вы можете открыть файл проекта и посмотреть, как всё сделано.
В API движка имеется специальный сенсор create_selection_sensor, который способен генерировать событие при выделении объекта мышью. Например, можно использовать такой код для отслеживания нажатия:
Здесь к объекту btRed (кнопка, включающая красный свет) добавлен сенсор sel_sensor. Генерация события ограничена типом CT_SHOT, т.е. происходит однократно. Теперь при каждом щелчке мышью по кнопке будет вызываться функция-обработчик sel_sensor_cb(), в которой сосредоточена вся магия: анимация кнопки, включение подсветки вокруг нее, активация ламп в сцене.
Кто-то, наверное, уже заметил, что предложенный код имеет один существенный недостаток — это событие только выделения объекта, поэтому кнопка всегда получается включенной. Конечно, особых проблем здесь нет. Достаточно добавить в программу переменную-триггер и переключать режимы ON/OFF в зависимости от ее значения. Но задача состоит немного в другом. Необходимо однократное нажатие кнопки с последующей её блокировкой. А вот после того, как пользователь прощелкает все кнопки в сцене, нужно проверить правильность порядка и принять решение — дать ему еще шанс (выключить свет и кнопки) или включить финальный этап.
Именно здесь приходит на помощь ещё один мощный инструмент событийной модели — логическая функция:
В предыдущих примерах на её месте стоял null. Таким образом, наступившее событие всегда вызывало обработчик callback. В действительности, такое поведение обуславливается результатом работы логической функции в совокупности с выбранным типом. А так как в одном манифолде может быть сразу несколько сенсоров, то с помощью этого инструмента можно создавать очень интересные модели поведения.
В состав API входит множество готовых сенсоров, но есть один, смысл которого заключается в хранении некой переменной: create_custom_sensor(value). Пользователь с помощью специальных функций может изменять и контролировать значение value в любое время. Например, ничего не стоит создать на его основе событие, которое будет генерироваться при наступлении взрыва. При этом переменная будет хранить какое-то важное значение, ту же силу взрыва. А уже на основании полученных данных, подписавшиеся объекты, могут так или иначе реагировать. К тому же, значение value можно учитывать при построении логической функции. Именно этим свойством я воспользовался, чтобы блокировать в нужный момент выполнение сенсора create_selection_sensor.
Идея заключается в том, что после выделения кнопки, вызов обработчика события наступает только в том случае, если значение custom_sensor равно нулю. Стоит только изменить его на другую цифру и обработчик блокируется.
Создание custom_sensor ничем не отличается от ранее рассмотренных сенсоров. При этом нужно сразу указать начальное значение value:
Так как необходимо отслеживать еще и сенсор выделения, то конструкция инициализации становится несколько иной — все сенсоры объединяются в массив:
И уже массив передается в манифолд:
Все действие заключено в логической функции logic_fun (её нужно описать до вызова в коде):
Вызов обработчика sensor_cb произойдет, если функция logic_fun вернет оба результата работы сенсоров, как true. Логическая функция выполняется непрерывно и привязана к обновлению кадра. Поэтому считывание параметров сенсоров происходит непрерывно. Если пользователь выделит объект-кнопку на экране, то сенсор s[0] вернет true. Однако, пока равенство s[1]==0 недействительно, обработчик выполняться не будет.
Рассмотрим еще один пример использования сенсоров в приложении. Итак, пользователь нажал все три кнопки на экране. Программа должна решить, в той ли последовательности это было сделано и как отреагировать.
Я решил назначить каждой кнопке свою букву (string) и в специальной переменной хранить сумму этих букв. Понятное дело, что сложение букв приводит к уникальным комбинациям, где всего-лишь одна является правильной. Таким образом, программа узнает о последовательности нажатия кнопок. Но вот сама эта логика выполняется в специальном обработчике, который вызывается при выполнении некоторых требований.
Ранее был показан код блокирования кнопок на экране с помощью сенсора custom_sensor. Для этого достаточно было в обработчике поменять значение сенсора на иное с помощью специальной функции:
Таким образом, сенсоры всех кнопок получали значение 1, что собственно и блокировало выделение объектов в дальнейшем. Но эти же значения учитываются и дальше.
В сцене Blender было подготовлено несколько объектов, содержащих подсказки, на тот случай, если игрок неправильно угадывает комбинацию звуков. Именно они “ожидают” установки custom_sensor кнопок в 1, после чего срабатывает их собственный обработчик, выводящий подсказки на экран.
Вот код, который отвечает за проверку:
Здесь используется только custom_sensor. При инициализации его значение устанавливается как false, что позволяет включить своего рода режим ожидания. Функция logic_wrong проверяет нажатие всех кнопок на экране (суммируется значение value сенсоров объектов), а также сравнивается последовательность нажатия кнопок с эталоном “ABC”. И только при соответствии всех требований, логическая функция вернет в манифолд значение true, что в свою очередь запустит обработчик wrong_cb.
Если при изучении этих примеров, вы свыклись с мыслью, что всё с событийной моделью b4w понятно, то спешу “обрадовать” — есть еще несколько нюансов, которые нужно учитывать.
В примерах создания манифолда вы уже встречали параметр type. Обычно я использовал значения CT_SHOT или CT_CONTINUOUS. Всего их в Blend4Web пять вариантов. Однако, главное здесь то, что они работают в связке с логической функцией. Так, чуть выше, функция с типом CT_SHOT вызывала обработчик st_btWrong только, если логическая функция logic_wrong возвращала true.
Список возможных значений type и их реакция на результат возвращаемый логической функцией:
Как видите, событийная модель Blend4Web очень гибкая и позволяет стоить весьма сложные логические конструкции. В одной статье невозможно охватить все аспекты работы с ней, но базовые особенности были раскрыты. Для знакомства с полными возможностями событийной модели, советую заглянуть в официальную документацию API.
Ссылка на приложение
Архив с исходниками
![](https://habrastorage.org/files/f0b/0ac/439/f0b0ac4395544cffa35fb20dc412c69c.jpg)
Простой подход
В любом приложении есть главный цикл, который, например, опрашивает клавиатуру, выполняет некие логические действия или отвечает за движение объектов. Так, в популярном движке Unity имеется специальное событие Update (), которое разработчики используют для большинства указанных выше задач. Причем, если продолжать рассматривать подходы разработчиков Unity к этому вопросу, то наиболее важной будет парадигма “объект — событие”, т.е. когда для каждого объекта в сцене имеются управляющие скрипты и индивидуальная “подписка“ на события.
Blend4Web предлагает аналогичный подход. Однако, он больше основан на событийной модели, которая также может быть привязана к конкретным объектам в сцене.
Вернёмся к началу статьи и рассмотрим механизм “главного цикла”.
Для не желающих изучать событийную модель, есть простейший вариант — функция append_loop_cb() из модуля main.
Пользоваться ей просто. Вызываете эту функцию, например, после инициализации движка и указываете имя обработчика в качестве параметра:
var m_main= b4w.require ("main");
m_main. append_loop_cb(update_cb);
…
function update_cb() {
//логика приложения
}
Теперь функция cb_update () будет вызываться каждый кадр. Причем это событие не привязано к какому-либо объекту. Его можно использовать для логических построений, но не опроса устройств ввода, как следовало бы ожидать. Вообще опрос клавиатуры, мыши и тому подобного — это отдельная тема, которую я рассмотрю чуть позже.
Функция append_loop_cb () проста в использовании и годится для несложной логики. Собственно, она считается устаревшей и оставлена в API для совместимости с ранее созданными приложениями. Сейчас появился более крутой механизм, основанный на событиях.
Событийная модель Blend4Web требует осмысления и некоторого привыкания. Но это только в начале. Главное понять принципы её работы, тогда всё становится ясным и удобным.
В основе ее находится понятие “сенсор”. Сенсор — это отдельная программная единица, ответственная за генерацию события на конкретное действие. Так, есть сенсоры клавиатуры, мыши, таймера, физики и т.д. Для создания каждого сенсора имеется своя функция. Кстати, вся подноготная событийной модели заключена в модуле controls.
Продолжим рассмотрение создания замкнутого цикла, но уже с помощью событий. Для этого в наборе сенсоров имеется специальная функция create_elapsed_sensor(). Например, так можно оформить ее вызов:
var m_ct= b4w.require ("controls");
var _elapsed_sensor = m_ctl.create_elapsed_sensor();
Сущность создана, но толка нет. И правильно, ведь на событие еще нужно подписаться. В API движка есть одна, универсальная функция, с большим количеством параметров: create_sensor_manifold(obj, id, type, sensors, logic_fun, callback, callback_param).
Весь код целиком:
var m_ct= b4w.require ("controls");
//создали сенсор
var elapsed_sensor = m_ctl.create_elapsed_sensor();
//”привязали”
m_ctl.create_sensor_manifold(null, "MAIN", m_ctl.CT_CONTINUOUS, [elapsed_sensor], null, main_cb);
…
//вызываемая функция при наступлении события
function main_cb () {
//логика приложения
}
После этого, каждый кадр будет вызываться функция main_cb, где можно разместить необходимую логику. Результат напоминает работу append_loop_cb, не правда ли? Вот только выглядит это действие несколько сложнее, но не спешите с выводами…
Вся изюминка работы с событиями заключена в параметрах, что передается в create_sensor_manifold. Разберем ключевые моменты ранее приведенного кода:
- null. Первый параметр — это объект, который “подписывается” на событие. Здесь указан null, т.е. объектом является вся сцена.
- “MAIN”. Уникальный идентификатор, который присваивается созданному множеству.
- m_ctl.CT_CONTINUOUS. Тип генерации события. Вариантов несколько и в данном случае обозначает, что событие будет генерироваться постоянно. Например, если указать m_ctl.CT_SHOT, то оно произойдет однократно (упрощенно, дальше будут пояснения).
- [_elapsed_sensor]. Ранее созданный сенсор.
- main_cb. Функция-обработчик, вызываемая при наступлении события.
Итак, что мы имеем. А есть у нас три очень мощных рычага для работы с событиями. Во-первых, события могут быть привязаны к конкретному объекту в сцене. Во-вторых, каждый объект может быть подписан на несколько разных событий. Именно поэтому ссылка на созданный сенсор передается в виде единицы массива. В-третьих, можно управлять типом генерации событий. На самом деле, их не два варианта указанных выше, а гораздо больше. К тому же имеется специальный механизм, позволяющий использовать сложные логические построения для реагирования на события.
События в действии
Лучший способ изучения чего-то нового — это практическая работа. Можно сколь-угодно сотрясать воздух теорией, но результат должен быть осязаем. Меня всегда умиляла непосредственность юных дарований, вопрошающих на форумах: “какой игровой движок выбрать, какой лучший язык программирования, сколько можно заработать…” Совет — сядьте и делайте. Нет ничего ценнее, чем собственноручно набитые шишки.
Вот и в этом случае, я предпочел проверить теорию на практике. Для полноценного изучения возможностей событийной модели Blend4Web было решено создать простенькое приложение и максимально использовать эти самые возможности. Но работа оказалась настолько интересной, что в результате получилась симпатичная мини-игра/новогодняя открытка, которая я даже разместил на главной странице моего сайта с играми. В конце статьи вы найдете ссылки на приложение и на все исходники. Пользуйтесь на здоровье.
Идея новогодней открытки проста и навеяна детскими воспоминаниями. Помните ключевую фразу “Раз, два, три — ёлочка гори”? Нечто подобное я решил воплотить в жизнь. Есть три кнопки, каждая из которых зажигает лампочки на елке соответствующего цвета. При этом звучит звук определенной высоты. Задача — найти правильную последовательность включения ламп. После нажатия кнопок, программа выполняет проверку результата и выводит сообщение: либо подсказку, либо переход на финал. Так как это все же больше новогодняя открытка, нежели игра, после нескольких неудачных попыток дается правильный вариант.
![](https://habrastorage.org/files/46b/72c/83f/46b72c83ff1c4a27bee173d8dcc9865e.jpg)
Движок Blend4Web не содержит встроенного GUI, поэтому предлагается два варианта: использовать объекты в Blender или работать через стандартные возможности HTML. Мне проще работать с Blender. В редакторе были созданы плоскости с текстурами кнопок и добавлены к ним анимации “выхода”. Вы можете открыть файл проекта и посмотреть, как всё сделано.
В API движка имеется специальный сенсор create_selection_sensor, который способен генерировать событие при выделении объекта мышью. Например, можно использовать такой код для отслеживания нажатия:
m_ctr = b4w.require("controls");
...
sel_sensor = m_ctr.create_selection_sensor(_btRed, true);
m_ctr.create_sensor_manifold (_btRed, "btRed", m_ctr.CT_SHOT, [sel_sensor],null, sel_sensor_cb, null);
function sel_sensor_cb () {
//обработка нажатия кнопки
}
Здесь к объекту btRed (кнопка, включающая красный свет) добавлен сенсор sel_sensor. Генерация события ограничена типом CT_SHOT, т.е. происходит однократно. Теперь при каждом щелчке мышью по кнопке будет вызываться функция-обработчик sel_sensor_cb(), в которой сосредоточена вся магия: анимация кнопки, включение подсветки вокруг нее, активация ламп в сцене.
Кто-то, наверное, уже заметил, что предложенный код имеет один существенный недостаток — это событие только выделения объекта, поэтому кнопка всегда получается включенной. Конечно, особых проблем здесь нет. Достаточно добавить в программу переменную-триггер и переключать режимы ON/OFF в зависимости от ее значения. Но задача состоит немного в другом. Необходимо однократное нажатие кнопки с последующей её блокировкой. А вот после того, как пользователь прощелкает все кнопки в сцене, нужно проверить правильность порядка и принять решение — дать ему еще шанс (выключить свет и кнопки) или включить финальный этап.
Именно здесь приходит на помощь ещё один мощный инструмент событийной модели — логическая функция:
create_sensor_manifold(obj, id, type, sensors, LOGIC_FUN, callback)
В предыдущих примерах на её месте стоял null. Таким образом, наступившее событие всегда вызывало обработчик callback. В действительности, такое поведение обуславливается результатом работы логической функции в совокупности с выбранным типом. А так как в одном манифолде может быть сразу несколько сенсоров, то с помощью этого инструмента можно создавать очень интересные модели поведения.
В состав API входит множество готовых сенсоров, но есть один, смысл которого заключается в хранении некой переменной: create_custom_sensor(value). Пользователь с помощью специальных функций может изменять и контролировать значение value в любое время. Например, ничего не стоит создать на его основе событие, которое будет генерироваться при наступлении взрыва. При этом переменная будет хранить какое-то важное значение, ту же силу взрыва. А уже на основании полученных данных, подписавшиеся объекты, могут так или иначе реагировать. К тому же, значение value можно учитывать при построении логической функции. Именно этим свойством я воспользовался, чтобы блокировать в нужный момент выполнение сенсора create_selection_sensor.
Идея заключается в том, что после выделения кнопки, вызов обработчика события наступает только в том случае, если значение custom_sensor равно нулю. Стоит только изменить его на другую цифру и обработчик блокируется.
Создание custom_sensor ничем не отличается от ранее рассмотренных сенсоров. При этом нужно сразу указать начальное значение value:
m_ctr.create_custom_sensor(0);
Так как необходимо отслеживать еще и сенсор выделения, то конструкция инициализации становится несколько иной — все сенсоры объединяются в массив:
var m_ctr = b4w.require("controls");
...
var sel_sensor = m_ctr.create_selection_sensor(_btRed, true);
_st_sens1= m_ctr.create_custom_sensor(0);
var sens = new Array ();
sens = [sel_sensor, _st_sens1];
И уже массив передается в манифолд:
m_ctr.create_sensor_manifold (_btRed, "btRed", m_ctr.CT_SHOT, sens, logic_fun, sensor_cb, null);
Все действие заключено в логической функции logic_fun (её нужно описать до вызова в коде):
var logic_fun = function(s) {
return s[0]&& s[1]==0
}
Вызов обработчика sensor_cb произойдет, если функция logic_fun вернет оба результата работы сенсоров, как true. Логическая функция выполняется непрерывно и привязана к обновлению кадра. Поэтому считывание параметров сенсоров происходит непрерывно. Если пользователь выделит объект-кнопку на экране, то сенсор s[0] вернет true. Однако, пока равенство s[1]==0 недействительно, обработчик выполняться не будет.
Рассмотрим еще один пример использования сенсоров в приложении. Итак, пользователь нажал все три кнопки на экране. Программа должна решить, в той ли последовательности это было сделано и как отреагировать.
Я решил назначить каждой кнопке свою букву (string) и в специальной переменной хранить сумму этих букв. Понятное дело, что сложение букв приводит к уникальным комбинациям, где всего-лишь одна является правильной. Таким образом, программа узнает о последовательности нажатия кнопок. Но вот сама эта логика выполняется в специальном обработчике, который вызывается при выполнении некоторых требований.
Ранее был показан код блокирования кнопок на экране с помощью сенсора custom_sensor. Для этого достаточно было в обработчике поменять значение сенсора на иное с помощью специальной функции:
m_ctr.set_custom_sensor(_st_sens1 , 1);
Таким образом, сенсоры всех кнопок получали значение 1, что собственно и блокировало выделение объектов в дальнейшем. Но эти же значения учитываются и дальше.
В сцене Blender было подготовлено несколько объектов, содержащих подсказки, на тот случай, если игрок неправильно угадывает комбинацию звуков. Именно они “ожидают” установки custom_sensor кнопок в 1, после чего срабатывает их собственный обработчик, выводящий подсказки на экран.
Вот код, который отвечает за проверку:
var _result_buttons = “”;
...
var logic_wrong= function(s) {
var sum = m_ctr.get_custom_sensor(_st_sens1)+m_ctr.get_custom_sensor(_st_sens2)+m_ctr.get_custom_sensor(_st_sens3);
s[0] = false;
if (sum==3 && _result_buttons !="ABC") s[0] = true;
return s[0];
}
st_btWrong = m_ctr.create_custom_sensor(false);
m_ctr.create_sensor_manifold (_btWrong, "btWrong", m_ctr.CT_SHOT, [st_btWrong], logic_wrong, wrong_cb, null);
function wrong_cb () {
//выводим подсказку
}
Здесь используется только custom_sensor. При инициализации его значение устанавливается как false, что позволяет включить своего рода режим ожидания. Функция logic_wrong проверяет нажатие всех кнопок на экране (суммируется значение value сенсоров объектов), а также сравнивается последовательность нажатия кнопок с эталоном “ABC”. И только при соответствии всех требований, логическая функция вернет в манифолд значение true, что в свою очередь запустит обработчик wrong_cb.
Если при изучении этих примеров, вы свыклись с мыслью, что всё с событийной моделью b4w понятно, то спешу “обрадовать” — есть еще несколько нюансов, которые нужно учитывать.
В примерах создания манифолда вы уже встречали параметр type. Обычно я использовал значения CT_SHOT или CT_CONTINUOUS. Всего их в Blend4Web пять вариантов. Однако, главное здесь то, что они работают в связке с логической функцией. Так, чуть выше, функция с типом CT_SHOT вызывала обработчик st_btWrong только, если логическая функция logic_wrong возвращала true.
Список возможных значений type и их реакция на результат возвращаемый логической функцией:
- CT_SHOT и CT_CONTINUOUS срабатывают при смене значения возвращаемого логической функцией с false на true.
- CT_LEVEL будет постоянно вызывать обработчик с pulse=1, когда логическая функция возвращает true.
- CT_TRIGGER вызовет обработчик, если функция изменила свое значение с false на true (вернет pulse=1) или наоборот (тогда pulse=-1). В остальных случаях обработчик не вызывается.
- CT_CHANGE генерирует импульс при любом изменении значения сенсора, без учета результатов работы логической функции.
Как видите, событийная модель Blend4Web очень гибкая и позволяет стоить весьма сложные логические конструкции. В одной статье невозможно охватить все аспекты работы с ней, но базовые особенности были раскрыты. Для знакомства с полными возможностями событийной модели, советую заглянуть в официальную документацию API.
Ссылка на приложение
Архив с исходниками
gfxdevrus
Замечательное новогоднее приложение и очень полезная статья! Спасибо!