Листы событий против графов узлов в 2D-движках: как устроены и что выбрать

Коротко

Лист событий и граф узлов — две записи одной и той же логики «событие → проверка → действие». Лист читается сверху вниз как список правил и хорош для десятков коротких реакций, а граф показывает поток выполнения проводами и хорош для одной разветвлённой системы. Ниже — как это устроено в Construct, GDevelop, Unreal Blueprints и Scratch, таблица выбора и одна механика (двойной прыжок) в обеих записях с кодом, проверенным в Node.js 24.

Содержание

  1. Два способа записать одно игровое правило
  2. Как устроен лист событий: Construct и GDevelop
  3. Как устроен граф узлов: Blueprints в Unreal Engine
  4. Где здесь Scratch
  5. Таблица: что выбрать под задачу
  6. Одна механика в двух записях: код на JavaScript
  7. Практический сценарий: переносим правило из листа в граф
  8. Когда вам это не подойдёт

Два способа записать одно игровое правило

Одно и то же правило можно записать строкой списка или схемой из узлов, и движок выполнит их одинаково. Возьмём правило «если игрок нажал прыжок и у него остались прыжки — подбросить вверх и уменьшить счётчик». В листе событий это одна строка: слева условия, справа действия. В графе это цепочка: узел события «нажат прыжок» соединён проводом с узлом ветвления «прыжков больше нуля?», а выход «да» ведёт к двум узлам действий.

Разница не в возможностях, а в том, что лучше видно глазом. Лист показывает все правила игры сразу и их порядок. Граф показывает путь выполнения одной цепочки и откуда берутся данные для каждого шага.

Как устроен лист событий: 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 с»:

  1. Событие. Строка листа начинается с события столкновения — в графе это узел события «столкновение с шипами».
  2. Условия. Каждое условие строки превращается в узел ветвления; несколько условий через «И» — цепочка ветвлений по выходу «да».
  3. Действия. Действия в строке выполняются слева направо — в графе соедините их проводами выполнения в том же порядке.
  4. Данные. Переменную «жизни» в листе читают по имени; в графе протяните провод данных от узла чтения переменной к узлу вычитания.
  5. Порядок кадра. Если в листе правило стояло после правила «подбор аптечки», проверьте, что в графе аптечка тоже обрабатывается раньше, иначе в одном кадре результат может отличаться.
  6. Проверка. Прогоните одинаковую последовательность событий через обе записи, как в коде выше, и сравните итоговое состояние.

Когда вам это не подойдёт

Визуальная логика — не единственный правильный путь, и в ряде случаев сравнение листа и графа неактуально:

  • Вы уже уверенно пишете код: алгоритмы вроде поиска пути или генерации уровней короче и проще проверять тестами в тексте на 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.

Читайте также:

Частые вопросы

Чем лист событий отличается от графа узлов?

Лист событий — это список правил, которые проверяются сверху вниз; у каждого правила есть условия и действия. Граф узлов — это схема, где узлы соединены проводами, и выполнение идёт от узла события по связям через ветвления к действиям. Логика одна и та же, отличается способ её записи и чтения.

Какие известные движки используют лист событий, а какие граф?

Лист событий из условий и действий используют Construct и GDevelop. Граф узлов с проводами выполнения и данных используют Blueprints в Unreal Engine. Scratch собирает скрипты из блоков-пазлов, которые запускаются блоками-событиями.

Что проще для новичка?

Обычно лист событий, потому что одно правило помещается в одну строку и читается как предложение «если столкнулся с монетой — добавить очко». Граф требует держать в голове направление проводов, зато лучше показывает сложные ветвления одной системы.

Можно ли совмещать оба подхода с обычным кодом?

Да. В документации Construct описано смешивание событий и JavaScript, в GDevelop есть события JavaScript, а Blueprints в Unreal Engine работают поверх классов на C++. Визуальная логика и текстовый код дополняют друг друга.