Добавить в корзинуПозвонить
Найти в Дзене
Two Box Games

I Wanna To Fly: String Table.

09.03.2026 Исповедь разработчика: Как enum довёл меня до пива, нервного срыва и переписывания пол-игры Всем доброго времени суток, дорогие друзья! С вами снова команда разработки игры "I wanna to fly", и сегодня у нас на повестке день — история, которая началась с, казалось бы, простого и невинного решения, а закончилась... Ну, скажем так, закончилась она открытием бутылочки пива в три часа ночи и осознанием того, что иногда проще сломать всё до основания, чем пытаться чинить то, что трещит по швам. Как это обычно бывает: "О, enum? Да это же идеально!" Давайте совершим небольшой экскурс в прошлое. Когда мы только начинали реализовывать наш инвентарь — а это было довольно давно, когда код был проще, а нервы крепче, — перед нами встала стандартная задача: нужно как-то классифицировать предметы. Нож — это оружие? Или инструмент? Аптечка — это расходник? А квестовый предмет — это вообще отдельная история. И тут, как спасительная соломинка, появился он — enum. Для тех, кто не в курсе: enum

09.03.2026

Исповедь разработчика: Как enum довёл меня до пива, нервного срыва и переписывания пол-игры

Всем доброго времени суток, дорогие друзья! С вами снова команда разработки игры "I wanna to fly", и сегодня у нас на повестке день — история, которая началась с, казалось бы, простого и невинного решения, а закончилась... Ну, скажем так, закончилась она открытием бутылочки пива в три часа ночи и осознанием того, что иногда проще сломать всё до основания, чем пытаться чинить то, что трещит по швам.

Как это обычно бывает: "О, enum? Да это же идеально!"

Давайте совершим небольшой экскурс в прошлое. Когда мы только начинали реализовывать наш инвентарь — а это было довольно давно, когда код был проще, а нервы крепче, — перед нами встала стандартная задача: нужно как-то классифицировать предметы. Нож — это оружие? Или инструмент? Аптечка — это расходник? А квестовый предмет — это вообще отдельная история.

И тут, как спасительная соломинка, появился он — enum. Для тех, кто не в курсе: enum (перечисление) — это такой волшебный список, куда можно сложить все возможные категории предметов, а потом просто сравнивать их между собой. Казалось бы, что может пойти не так?

— enum? — спросите вы.

— Enum! — ответили мы и радостно зашагали в светлое будущее.

На тот момент это решение выглядело ультимативным. Удобно? Да. Просто? Безусловно. Быстро? Ещё бы! Мы создали несколько базовых категорий: оружие, броня, расходники, квестовые предметы. Всё работало как часы. Мы радовались, хлопали друг друга по плечу и двигались дальше, даже не подозревая, какую бомбу замедленного действия мы заложили в фундамент проекта.

Первые звоночки: "А давайте добавим ещё одну категорию?"

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

И всё это время enum исправно работал. Ну, по крайней мере, мы так думали. Да, были небольшие странности, но кто обращает на них внимание, когда горят дедлайны и нужно делать контент?

Но однажды случилось то, что должно было случиться. Я сидел, добавлял очередную, наверное, пятидесятую категорию, и тут меня как током ударило. Я вспомнил, что enum в Unreal Engine по умолчанию — это uint8. А это значит, что максимальное количество значений, которое он может хранить — 256. ВСЕГО 256! Вы понимаете? Для какой-нибудь казуальной игрушки с десятью предметами — это космос. Но мы делаем RPG. А в RPG предметов могут быть тысячи. И категорий — сотни.

-2

И тут до меня дошло: мы сидим на пороховой бочке. Шанс того, что мы перевалим за 256 категорий — может, и не 100%, но когда речь идёт о RPG с её бесконечным расширением, этот шанс стремится к фатальным 50 на 50. А если мы решим делать сиквелы или дополнения? Всё, пиши пропало.

Ад начинается: "Почему мой меч стал яблоком?!"

Но знаете, что было хуже всего? Не ограничение в 256, нет. Проблема была гораздо прозаичнее и от этого ещё более бесячая.

Когда я добавлял в enum новую категорию, мои предметы в таблицах данных начинали дружно съезжать. Представьте: у вас в игре 150 предметов. У каждого есть ID категории. В enum, скажем, «0 — Оружие», «1 — Броня», «2 — Расходники». И тут вы решаете добавить новую категорию «Инструменты» прямо между «Оружием» и «Бронёй». Вы вставляете её, и всё, что было «Бронёй» (ID 1), теперь становится «Расходниками» (ID 2), а «Расходники» (ID 2) улетают в «Квестовые предметы» (ID 3).

В лучшем случае у вас просто сломается логика крафта. В худшем — игрок зайдёт в игру, откроет инвентарь и обнаружит, что его легендарный двуручный меч почему-то числится как «Кусок хлеба». И это не шутка. У меня такое было. Не с хлебом, но было.

Сначала я думал: «Да что там чинить? Подумаешь, пару предметов руками поправить». Но таблиц становилось всё больше. Объектов данных — ещё больше. И каждый раз, когда требовалось добавить что-то новое, меня бросало в холодный пот. Я открывал проект, смотрел на enum и думал: «А не пофиг ли мне на эту новую категорию? Может, ну её нафиг?»

Финальным аккордом стала попытка навести порядок. Я решил отсортировать enum по алфавиту, чтобы в коде было удобнее искать. Я сел, выдохнул, начал перетаскивать строчки... И через пять минут, глядя на хаос, который творился в таблицах предметов, я понял: ВСЁ. ХВАТИТ. Я закрыл проект, пошёл на кухню, открыл бутылочку холодного пива и сказал себе: «С понедельника начинаем новую жизнь. Enum будет заменён».

Поиски идеала: от таблиц до забытого опыта

Итак, вооружившись пивом и решимостью, я начал думать. Вариантов было, как говорится, вагон и маленькая тележка.

Первый вариант — самый простой и ленивый. Можно ведь просто выставить enum не как uint8, а как int. Технически это снимает ограничение в 256 значений. Но, во-первых, это не решает проблему съезжающих ID при добавлении новых элементов. А во-вторых, это как заклеивать дыру в надувной лодке скотчем — вроде временно помогает, но в любой момент может рвануть в другом месте.

Второй вариант — использовать таблицы данных. Красиво, масштабируемо, по-взрослому. Но когда я начал копать в эту сторону, понял, что реализация выльется в такое количество телодвижений, что проще написать игру заново. Слишком много логики завязано на старые enum-ы, слишком много мест, где нужно будет переписывать код под работу с таблицами. Вариант отпал.

И тут, в глубинах памяти, всплыло кое-что интересное. Пять лет назад я работал в компании, которая делала VR-тренажёры. И там мы активно использовали одну штуку для локализации. Называлась она... f-строки. Точнее, в Unreal Engine это называется String Table, но суть та же.

-3

Мы тогда через них весь текст гоняли — и интерфейс, и подсказки, и описания. Было удобно: собрал все тексты в одну таблицу, локализаторы переводят, движок сам подставляет нужный язык. И главное — движок умеет собирать все текстовые переменные и выгружать их в дашборд с возможностью экспорта/импорта. Красота!

Я аж подскочил. Вот же оно! Почему я сразу не вспомнил? Ведь это идеально подходит не только для локализации, но и для наших категорий!

F-строки (они же String Table): Простота и бесконечность

Я залез в документацию, покопался в проекте и понял, как это работает. String Table — это, по сути, обычная таблица с двумя колонками: ключ и значение. Ключ — это уникальный идентификатор (мы решили использовать английские названия, чтобы проще было в коде). Значение — это то, что увидит игрок (на русском, английском или любом другом языке).

— Ключ: `Weapon_Sword`

— Значение: `Двуручный меч короля-лича`

— Ключ: `Armor_Helmet`

— Значение: `Рогатый шлем варвара`

И главное — никаких ограничений! Хоть тысячу пар "ключ-значение" добавляй. И порядок не важен, потому что поиск идёт по ключу, а не по индексу. Можно спокойно сортировать по алфавиту, группировать, переименовывать — предметы в игре от этого не пострадают, потому что у них в данных хранится не индекс, а строковый ключ.

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

Итак, таблица создана. Ключи прописаны. Значения переведены. Остался последний, самый ответственный шаг — встроить это всё в скелет игры.

Танец с бубном и нейросетью: "Почему оно не работает?!"

Тут-то и началось самое веселое.

Мне нужно было найти ноду (то есть, визуальный блок в Blueprints), которая умеет вытягивать значение из String Table по ключу. Я открыл контекстный поиск и начал перебирать варианты. Вариантов было много. Очень много. Я перепробовал всё, что хоть отдалённо напоминало "Get String", "Get Text", "From Table". Результат был нулевой.

В какой-то момент я уже хотел плюнуть и забить. Но потом вспомнил, что у современного разработчика есть один очень мощный инструмент. Нет, не кофе. И даже не Stack Overflow. А нейросеть. Дорогая, любимая, незаменимая нейросеть, которая терпеливо отвечает на все мои, порой идиотские, вопросы.

Я написал: "Как получить значение из String Table в Unreal Engine 5 Blueprints". И она выдала: "Используйте ноду `Make Text From String Table`".

Я нашёл эту ноду. Вроде она. Вставляю. Вижу входы: `Table ID` и `Key`. С `Key` всё понятно — туда мы пишем наше английское слово-ключ. А что писать в `Table ID`? Нейросеть говорит: "Название таблицы". Окей, логично. У меня таблица называется "ST_Enum". Пишу "ST_Enum". Запускаю. И вижу пустоту. Буквально — пустая строка: "".

— Ну почему? — взвыл я. — Что не так?

Начался период "танцев с бубном". Я перебирал варианты: может, кавычки нужны? Может, путь указать? Может, регистр важен? Я сидел, матерился, гуглил, снова матерился. Прошло, наверное, часа два, прежде чем до меня дошло. Нейросеть, конечно, умная, но иногда она выдаёт общую информацию. А движок — штука конкретная. Я просто скормил ей свой скриншот и спросил: "Что я делаю не так?"

Она посмотрела и сказала: "Дружище, `Table ID` — это не название таблицы. Это ID ассета. Тебе нужно взять саму таблицу как объект. Просто перетащи её из контент-браузера в ноду или укажи путь к ней".

-4

Я хлопнул себя по лбу. Ну конечно! Движок стандартизирует многие вещи, но иногда найти правильный путь без подсказки просто невозможно. Особенно когда ты уже час сидишь с красными глазами и не соображаешь. Бывают моменты, когда чувствуешь себя тупым как хлебушек. И это нормально. Главное — не останавливаться.

Я перетащил таблицу в ноду. Запустил. И...

Эврика! Или "Как я полюбил String Table"

Он вывел текст! Самый настоящий, русский текст, который я записал в значение для этого ключа!

Вы не представляете, какое это было облегчение. Сидеть в три часа ночи, смотреть на экран и видеть, как вместо пустой строки появляется "Оружие ближнего боя" — это, знаете ли, настоящий кайф разработчика. Тот самый момент, ради которого мы всё это и делаем.

Теперь у меня есть система, которая:

- Не имеет ограничений по количеству категорий.

- Не ломается при добавлении новых элементов.

- Позволяет сортировать, группировать и переименовывать что угодно, не трогая данные предметов.

- А в перспективе — легко адаптируется под локализацию.

Да, пришлось попотеть. Да, было много моментов, когда хотелось всё бросить и пойти спать. Да, без нейросетей сейчас вообще никуда — они реально выручают, когда ты застреваешь на, казалось бы, элементарной вещи. Но оно того стоило.

Что дальше?

Сейчас у нас в игре уже за сотню, если не за двести предметов. И все они теперь живут по новым правилам. Enum отправлен на заслуженный отдых (хотя местами ещё болтается в старом коде, но мы его постепенно выкорчёвываем).

Дальше — больше. В планах заточка предметов, унификация всего и вся, и, конечно, полноценная локализация с использованием тех самых String Table, которые теперь стали нашей палочкой-выручалочкой.

Тема заточки, кстати, отдельная песня. Там и проценты, и шансы, и визуальные эффекты — жесть, в общем. Как наша славянская разработка 💪

Вместо послесловия

Знаете, в разработке игр нет неважных мелочей. Каждое решение, которое ты принимаешь в самом начале, может вернуться и укусить тебя за одно место спустя годы. Иногда это укус комара, а иногда — укус акулы. Наш enum оказался акулой. Но мы выжили, мы справились, мы стали сильнее.

И знаете, что самое крутое? Мы делаем это не одни. Вы, наши подписчики, читаете, комментируете, ставите реакции. Это реально даёт нам энергию вставать после заводских смен, садиться за код и творить дальше.

Спасибо, что вы с нами. Дальше будет ещё интереснее, ещё жёстче и, надеюсь, ещё смешнее.

До скорых встреч! 🔥

---

*P.S. Если думаете, что разработка игр — это просто, попробуйте добавить одну строчку в enum и не сломать игру. Спойлер: у вас не получится 😂*