Коротко
Лист событий и граф узлов — две записи одной и той же логики «событие → проверка → действие». Лист читается сверху вниз как список правил и хорош для десятков коротких реакций, а граф показывает поток выполнения проводами и хорош для одной разветвлённой системы. Ниже — как это устроено в Construct, GDevelop, Unreal Blueprints и Scratch, таблица выбора и одна механика (двойной прыжок) в обеих записях с кодом, проверенным в Node.js 24.
Содержание
- Два способа записать одно игровое правило
- Как устроен лист событий: Construct и GDevelop
- Как устроен граф узлов: Blueprints в Unreal Engine
- Где здесь Scratch
- Таблица: что выбрать под задачу
- Одна механика в двух записях: код на JavaScript
- Практический сценарий: переносим правило из листа в граф
- Когда вам это не подойдёт
Два способа записать одно игровое правило
Одно и то же правило можно записать строкой списка или схемой из узлов, и движок выполнит их одинаково. Возьмём правило «если игрок нажал прыжок и у него остались прыжки — подбросить вверх и уменьшить счётчик». В листе событий это одна строка: слева условия, справа действия. В графе это цепочка: узел события «нажат прыжок» соединён проводом с узлом ветвления «прыжков больше нуля?», а выход «да» ведёт к двум узлам действий.
Разница не в возможностях, а в том, что лучше видно глазом. Лист показывает все правила игры сразу и их порядок. Граф показывает путь выполнения одной цепочки и откуда берутся данные для каждого шага.
Как устроен лист событий: Construct и GDevelop
В листе событий каждое правило состоит из условий и действий, а правила проверяются в порядке сверху вниз. В документации Construct событие описано так: условия отбирают экземпляры объектов, которые им соответствуют, и действия выполняются только для этих экземпляров; если пуля столкнулась с пришельцем, действие «уничтожить» затронет только этого пришельца. Там же сказано, что события можно смешивать с JavaScript: например, поставить строку кода на место действия.
В GDevelop та же модель: событие состоит из условий и действий, события выполняются по порядку сверху вниз, а событие без условий срабатывает каждый кадр — по документации, обычно 60 раз в секунду. Отдельно документация GDevelop предупреждает о частоте кадров: на медленном компьютере игра может идти с частотой 25 кадров в секунду, на быстром — 60, поэтому изменения во времени нужно умножать на TimeDelta(). Это общее свойство листов событий: порядок строк — часть логики, и правило выше по листу может изменить данные для правила ниже в том же кадре.
Как устроен граф узлов: Blueprints в Unreal Engine
В графе узлов логика — это сеть узлов, соединённых проводами, и выполнение идёт по проводам от узла события. Документация Unreal Engine описывает Blueprints как систему визуального программирования на основе узлов: Event Graph реагирует на игровые события через узлы событий и вызовы функций, а каждая функция — отдельный граф с одной точкой входа. Для ветвлений есть узел Branch: он получает логическое значение и отправляет выполнение по выходу «истина» или «ложь».
Важная особенность графа — два вида связей: по документации Unreal Engine, контакты выполнения (execution pins) задают порядок, в котором выполняются узлы, а контакты данных (data pins) передают значения между узлами. Поэтому граф хорошо показывает, откуда взялось число, но при сотне коротких реакций превращается в клубок, который приходится раскладывать по нескольким графам и функциям.
Где здесь Scratch
Scratch ближе к листу событий, но записывает правила не строками, а стопками блоков-пазлов. По Scratch Wiki, каждый скрипт начинается с блока-шапки, например «когда щёлкнут по зелёному флагу» или «когда я получу сообщение», а блоки разной формы (команды, условия, значения, циклы) вставляются только в подходящие по форме гнёзда, поэтому синтаксическую ошибку сделать нельзя. В Scratch 3.0 вики насчитывает 124 обычных блока без расширений.
Для школьника это мягкий вход: событие сверху, действия под ним, как в листе. Отличие от листа в том, что скрипты не выстроены в единый порядок проверки — каждая стопка запускается своим событием независимо.
Таблица: что выбрать под задачу
Выбор зависит от того, что вам важнее видеть: много мелких правил или одну сложную цепочку.
| Признак задачи | Лист событий | Граф узлов |
|---|---|---|
| Много коротких реакций: монеты, двери, урон | читается как список, удобно | десятки маленьких графов |
| Одна система с 5+ ветвлениями: диалог, ИИ босса | длинные вложенные подсобытия | ветви видны сразу |
| Откуда взялось значение | приходится искать по строкам | провод данных показывает источник |
| Порядок выполнения в кадре | задан порядком строк | задан проводами выполнения |
| Сравнение версий в системе контроля версий | проще, если лист хранится как текст | сложнее: важны координаты узлов |
| Вход для новичка | правило читается как предложение | нужно привыкнуть к проводам |
Рабочее правило: если правило умещается в одну строку «условия → действия», пишите его листом; если у него больше трёх ветвлений и общих данных — граф или функция в коде читаются лучше.
Одна механика в двух записях: код на JavaScript
Код ниже исполняет двойной прыжок двумя способами — как лист правил и как граф узлов с рёбрами — и проверяет, что результат одинаковый. Он проверен в Node.js 24.
// Одна механика — двойной прыжок — в двух записях: лист событий и граф узлов.
// Обе записи исполняются над одним и тем же состоянием и должны дать одинаковый результат.
// 1. Лист событий: правила читаются сверху вниз, «КОГДА → ЕСЛИ → СДЕЛАТЬ»
const sheet = [
{ when: "landed", if: function () { return true; },
do: function (s) { s.jumpsLeft = 2; } },
{ when: "jumpPressed", if: function (s) { return s.jumpsLeft > 0; },
do: function (s) { s.vy = -520; s.jumpsLeft -= 1; } },
];
function runSheet(state, eventName) {
for (const rule of sheet) {
if (rule.when === eventName && rule.if(state)) rule.do(state);
}
}
// 2. Граф узлов: событие → ветвление → действия, связи заданы рёбрами
const graph = {
nodes: {
onLanded: { type: "event", name: "landed" },
onJump: { type: "event", name: "jumpPressed" },
reset: { type: "action", run: function (s) { s.jumpsLeft = 2; } },
canJump: { type: "branch", test: function (s) { return s.jumpsLeft > 0; } },
jump: { type: "action", run: function (s) { s.vy = -520; s.jumpsLeft -= 1; } },
},
edges: { onLanded: ["reset"], onJump: ["canJump"], canJump: { yes: ["jump"], no: [] } },
};
function runGraph(state, eventName) {
const visit = function (id) {
const node = graph.nodes[id];
if (node.type === "action") node.run(state);
const next = graph.edges[id];
if (!next) return;
const list = node.type === "branch" ? (node.test(state) ? next.yes : next.no) : next;
for (const n of list) visit(n);
};
for (const id in graph.nodes) {
const node = graph.nodes[id];
if (node.type === "event" && node.name === eventName) visit(id);
}
}
// Один и тот же сценарий: приземление, три нажатия прыжка подряд
const events = ["landed", "jumpPressed", "jumpPressed", "jumpPressed"];
const a = { jumpsLeft: 0, vy: 0 }, b = { jumpsLeft: 0, vy: 0 };
let jumpsA = 0, jumpsB = 0;
for (const e of events) {
const before = [a.jumpsLeft, b.jumpsLeft];
runSheet(a, e); runGraph(b, e);
if (a.jumpsLeft < before[0]) jumpsA++;
if (b.jumpsLeft < before[1]) jumpsB++;
}
console.log("прыжков по листу:", jumpsA, "по графу:", jumpsB); // 2 2 — третье нажатие отклонено
console.log("правил в листе:", sheet.length, "узлов в графе:", Object.keys(graph.nodes).length); // 2 5
Обе записи разрешили ровно 2 прыжка из 3 нажатий. Лист описал механику двумя правилами, граф — пятью узлами: в этом и состоит цена наглядности графа на простых правилах.
Практический сценарий: переносим правило из листа в граф
Перенос между записями идёт по одной схеме: событие становится узлом-входом, условия — узлом ветвления, действия — цепочкой узлов. Порядок для правила «игрок коснулся шипов → если нет неуязвимости → минус жизнь, мигание 1 с»:
- Событие. Строка листа начинается с события столкновения — в графе это узел события «столкновение с шипами».
- Условия. Каждое условие строки превращается в узел ветвления; несколько условий через «И» — цепочка ветвлений по выходу «да».
- Действия. Действия в строке выполняются слева направо — в графе соедините их проводами выполнения в том же порядке.
- Данные. Переменную «жизни» в листе читают по имени; в графе протяните провод данных от узла чтения переменной к узлу вычитания.
- Порядок кадра. Если в листе правило стояло после правила «подбор аптечки», проверьте, что в графе аптечка тоже обрабатывается раньше, иначе в одном кадре результат может отличаться.
- Проверка. Прогоните одинаковую последовательность событий через обе записи, как в коде выше, и сравните итоговое состояние.
Когда вам это не подойдёт
Визуальная логика — не единственный правильный путь, и в ряде случаев сравнение листа и графа неактуально:
- Вы уже уверенно пишете код: алгоритмы вроде поиска пути или генерации уровней короче и проще проверять тестами в тексте на JavaScript или TypeScript, чем в любой визуальной записи.
- Большая команда с ревью кода: если правки проходят через сравнение текстовых изменений, бинарные или зависящие от координат графы неудобны для ревью.
- Нужна 3D-игра: примеры этой статьи — про 2D; в 3D к логике добавляются задачи камеры и физики в пространстве, которые здесь не разобраны.
Мы разрабатываем J2DRU — браузерный 2D игровой движок с событийной логикой и путём к TypeScript; визуальное программирование на графах в нём пока в разработке. Движок публично не открыт, заявку можно оставить на странице раннего доступа (подача заявки не гарантирует приглашение).
Источники (проверено: ноябрь 2026): Construct 3 — Events, GDevelop — Events, Unreal Engine — Blueprints Visual Scripting Overview, Unreal Engine — Nodes, Unreal Engine — Flow Control, Scratch Wiki — Blocks.
Читайте также: