---
title: "Листы событий против графов узлов в 2D-движках: как устроены и что выбрать"
description: "Сравнение двух способов визуальной логики в 2D-движках: лист событий (Construct, GDevelop) и граф узлов (Unreal Blueprints), плюс блоки Scratch. Одна механика в двух записях, таблица выбора и код на JavaScript, проверенный в Node.js 24."
slug: listy-sobytij-protiv-grafov-uzlov-v-2d-dvizhkah
date: 2026-10-30
updated: 2026-11-02
scheduled_at: "2026-10-30T19:00:00+03:00"
rubric: logika-i-arhitektura
tags: ["tablicy-sobytij", "grafy-logiki", "vizualnoe-programmirovanie", "novichkam", "arhitektura"]
reading_time: 9
draft: false
summary: "Визуальную логику игры записывают двумя основными способами: листом событий, где правила «условия → действия» читаются сверху вниз (так устроены Construct и GDevelop), и графом узлов, где поток выполнения идёт по проводам между узлами (так устроены Blueprints в Unreal Engine). Лист компактнее для множества коротких реакций на события и удобен новичку, граф нагляднее показывает ветвления и поток данных одной сложной системы. Scratch занимает промежуточное место: скрипты из блоков-пазлов, которые запускаются событиями."
---

## Коротко

Лист событий и граф узлов — две записи одной и той же логики «событие → проверка → действие». Лист читается сверху вниз как список правил и хорош для десятков коротких реакций, а граф показывает поток выполнения проводами и хорош для одной разветвлённой системы. Ниже — как это устроено в 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.

```js
// Одна механика — двойной прыжок — в двух записях: лист событий и граф узлов.
// Обе записи исполняются над одним и тем же состоянием и должны дать одинаковый результат.

// 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; визуальное программирование на графах в нём пока в разработке. Движок публично не открыт, заявку можно оставить на [странице раннего доступа](/early-access?utm_source=blog&utm_medium=article&utm_campaign=listy-sobytij-protiv-grafov-uzlov-v-2d-dvizhkah) (подача заявки не гарантирует приглашение).

**Источники (проверено: ноябрь 2026):** [Construct 3 — Events](https://www.construct.net/en/make-games/manuals/construct-3/project-primitives/events), [GDevelop — Events](https://wiki.gdevelop.io/gdevelop5/events/), [Unreal Engine — Blueprints Visual Scripting Overview](https://dev.epicgames.com/documentation/en-us/unreal-engine/overview-of-blueprints-visual-scripting-in-unreal-engine), [Unreal Engine — Nodes](https://dev.epicgames.com/documentation/en-us/unreal-engine/nodes-in-unreal-engine), [Unreal Engine — Flow Control](https://dev.epicgames.com/documentation/en-us/unreal-engine/flow-control-in-unreal-engine), [Scratch Wiki — Blocks](https://en.scratch-wiki.info/wiki/Blocks).

**Читайте также:**
- [Идеальный прыжок и секрет Coyote Time](/blog/idealnyj-pryzhok-i-sekret-coyote-time)
- [Игровой цикл и физика столкновений AABB](/blog/igrovoj-cikl-i-fizika-stolknovenij-aabb)
- [От первых аркад и «Змейки» до инди-хитов: история ключевых механик 2D-игр](/blog/istoriya-klyuchevyh-mehanik-2d-igr)
