Структуры (Structs, Structure) в Unreal Engine Blueprint

Друзья, приветствую, с Вами Будуев Антон. В данной статье мы изучим, что такое Structs, Structure или по-русски структуры в Unreal Engine Blueprint (UE4, UE5). Но сначала разберёмся с вводной информацией, чтобы подвести к той проблематике, которую могут решить структуры.

Также рекомендую скачать мою бесплатную книгу по Blueprint для Unreal Engine.

Основная цель книги — дать общий, целостный и понятный обзор системы Blueprint. Книга предлагает взгляд с высоты «птичьего полёта» — ясную и чёткую карту игрового мира Unreal Engine, которая поможет увидеть ключевые взаимосвязи и понять, как устроена система визуального скриптинга.

Зарегистрироваться в ЛК и скачать книгу [Blueprints. Взгляд с высоты «птичьего полёта»]

Какие проблемы позволяют решать структуры

Создадим новый Blueprint класса Actor и назовём его BP_Pickup — подбираемый предмет. Таким предметом может быть что угодно: аптечка, бутылка воды, хлеб, меч — не важно. Главное, что это будет объект, который игрок может поднять.

Добавим к нему компонент Cube (чтобы визуально наблюдать Актор на уровне), а также коллизию в виде сферы для взаимодействия с игроком.

Подбираемый предмет BP_Pickup
Подбираемый предмет BP_Pickup

Когда игрок подбирает предмет, он должен передать игроку информацию о себе — например, имя, описание и количество. Допустим, наш BP_Pickup — это коробка с бутылками воды, в которой находится 10 бутылок, и игрок может поднимать их по одной. Всю эту информацию нужно где-то хранить внутри BP_Pickup.

Создадим для этого несколько Blueprint-переменных:

  • NamePickup (название предмета) — типа String;
  • Amount (количество) — типа Integer.
Переменные поднимаемого предмета NamePickup (название предмета), Amount (количество)
Переменные поднимаемого предмета NamePickup (название предмета), Amount (количество)

Позднее можно добавить другие переменные, характеризующие поднимаемый предмет: описание, вес, стоимость, редкость и так далее. Также в классе могут появиться и другие переменные, не относящиеся к характеристикам предмета — например, для анимации или логики движения.

К чему я веду: со временем количество переменных в классе может значительно вырасти, и они будут относиться к разным аспектам объекта. Да, переменные можно распределять по категориям, что немного упорядочит структуру, но это не решит всех проблем.

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

Другая проблема: переменные NamePickup и Amount описывают один логический объект — подбираемый предмет. Все эти данные должны передаваться игроку при взаимодействии, то есть нам придётся передавать каждую переменную отдельно, например через параметры интерфейсных функций. Да, мы можем передавать каждую переменную по отдельности. Но что будет, если позже понадобится изменить набор данных? Добавить третий параметр, потом четвертый? Придётся заходить в каждый класс, где эти данные используются, добавлять новые переменные, обновлять интерфейсы, править параметры — и так по всей игре.

Давайте посмотрим это на практике.

В BP_Pickup у нас есть две переменные, характеризующие поднимаемый предмет. Опишем скрипт взаимодействия игрока с этим предметом через интерфейс, который вернёт игроку информацию о нём, то есть значение этих 2 переменных, а игрок выведет эту информацию на экран.

Создадим интерфейс. Тему интерфейсов в этой статье разбирать мы не будем, с ними вы можете ознакомиться в отдельной статье (Blueprint Interface). Здесь же просто всё сделаем по шагам.

  1. Итак, перейдём в Content Browser, через контекстное меню правой кнопкой мыши выберем меню Blueprint / Blueprint Interface и создадим интерфейс. Дадим ему название BPI_Pickup. Откроем файл интерфейса и добавим функцию GetPickupData с возвращаемыми параметрами NamePickup (типа String) и Amount (типа Integer).
Интерфейс BPI_Pickup и интерфейсная функция GetPickupData с возвращаемыми параметрами NamePickup (типа String) и Amount (типа Integer)
Интерфейс BPI_Pickup и интерфейсная функция GetPickupData с возвращаемыми параметрами NamePickup (типа String) и Amount (типа Integer)
  1. Перейдём в BP_Pickup и подключим интерфейс BPI_Pickup в настройках класса.
Подключение интерфейса BPI_Pickup в классе BP_Pickup
Подключение интерфейса BPI_Pickup в классе BP_Pickup
  1. После этого в панели My Blueprint в разделе Interfaces появится функция GetPickupData. Откроем её двойным кликом: функция должна возвращать значения переменных NamePickup и Amount, поэтому передадим соответствующие переменные, которые у нас находятся в классе.
Передача переменных NamePickup и Amount в возвращаемые параметры интерфейсной функции GetPickupData
Передача переменных NamePickup и Amount в возвращаемые параметры интерфейсной функции GetPickupData
  1. Теперь откроем Blueprint персонажа. Добавим на график событие Event Begin Overlap, которое срабатывает при пересечении Актора персонажа с коллизиями других объектов. Для Other Actor, то есть для пересекающегося с игроком Актора, проверим функцией Does Implement Interface, реализует ли он BPI_Pickup. Если да — вызовем интерфейсную функцию GetPickupData, которая возвращает информацию о поднимаемом предмете. А функцией Print String выведем эту информацию (возвращаемые интерфейсной функцией параметры), предварительно объединив её в одну строку функцией Append.
Если Other Actor реализует интерфейс BPI_Pickup, то вызываем в нем функцию GetPickupData и после выводим на печать возвращаемые функцией параметры
Если Other Actor реализует интерфейс BPI_Pickup, то вызываем в нем функцию GetPickupData и после выводим на печать возвращаемые функцией параметры

Перенесём BP_Pickup на уровень и проверим взаимодействие в игре. Запустим игру, подойдём к предмету — в результате взаимодействия мы видим информацию о поднимаемом предмете. Напоминаю: код вывода у нас находится в игроке, а сама выводимая информация — в BP_Pickup.

В результате взаимодействия игрока с поднимаемым предметом выводится информация: "water = 10"
В результате взаимодействия игрока с поднимаемым предметом выводится информация: «water = 10»

Теперь представим такую ситуацию: прошло время, и мы решили, что поднимаемый предмет должен передавать ещё некоторую дополнительную информацию — например, описание. Создадим в BP_Pickup переменную Description типа Text, скомпилируем и зададим значение, например «A box with cans of water» (коробка с банками воды).

Переменная Description
Переменная Description

Как передать эту информацию игроку? Придётся менять интерфейс. Откроем BPI_Pickup, добавим в возвращаемые параметры Description (Text).

Добавление параметра Description в интерфейсную функцию GetPickupData
Добавление параметра Description в интерфейсную функцию GetPickupData

В BP_Pickup в функции GetPickupData подключим переменную Description к выходу.

Подключаем переменную Description к соответствующему возвращаемому параметру функции
Подключаем переменную Description к соответствующему возвращаемому параметру функции

А в Blueprint игрока добавим Description в Append для вывода.

Добавляем в Blueprint игрока печать параметра Description
Добавляем в Blueprint игрока печать параметра Description

Проверим в игре — теперь на экране выводится расширенная информация, включая описание.

При взаимодействии игрока с поднимаемым предметом на экран выводится расширенная информация, включая Description
При взаимодействии игрока с поднимаемым предметом на экран выводится расширенная информация, включая Description

Так вот, к чему я всё веду: если у нас есть набор переменных одного или разных типов, объединённых общим смыслом, и нужно работать с ними как с единой группой — как в текущем примере с BP_Pickup, — гораздо проще использовать специальный объект, содержащий все эти данные. Таким инструментом в Unreal Engine является тип данных Struct (Structure) — структура.

Что такое Struct, Structure (структуры) в Unreal Engine

Struct, Structure (структура) — это специальный тип данных в Unreal Engine, который позволяет объединять несколько переменных разных типов, но связанных между собой общей логикой. Другими словами, структуру можно представить как контейнер, внутри которого хранятся разнообразные данные, описывающие один объект или понятие.

Структуры решают сразу несколько задач:

  1. Логическое объединение данных: если свойства относятся к одной сущности — логично хранить их вместе. Например, свойства подбираемого предмета (имя, описание, количество) образуют единую логическую группу, и удобнее держать их внутри одной структуры, а не как несколько разрозненных переменных.
  2. Упрощение передачи данных между объектами: когда игрок подбирает предмет, гораздо проще передать только одной переменной всю структуру со всеми свойствами, чем передавать каждое значение отдельно — имя, описание, количество и так далее.
  3. Масштабируемость и удобство поддержки: если позже нужно добавить новое свойство — например, «вес», «редкость» или «тип материала» — достаточно внести его в структуру. Оно автоматически станет доступным везде, где эта структура используется. Не нужно менять десятки функций и классов.
  4. Удобство хранения наборов данных: структуры часто используются в массивах, где каждая структура описывает отдельный объект. Это особенно полезно при создании инвентаря, таблиц предметов, списков квестов и так далее.

Кроме того, структуры являются основой таблиц данных (Data Tables) — такие таблицы используют структуру в качестве шаблона, определяющего столбцы и их типы.

Один из самых известных примеров структур — это встроенный тип Transform.

Тип Transform - структура, состоящая из 3 переменных: Location, Rotation, Scale
Тип Transform — структура, состоящая из 3 переменных: Location, Rotation, Scale

Внешне это всего одна переменная, но внутри неё содержатся:

  • Location — координаты объекта в пространстве,
  • Rotation — его ориентация,
  • Scale — масштаб.

Каждый из этих параметров тоже представляет собой структуру из трёх значений: X, Y и Z.

Каждый из параметров Transform также представляет собой структуру из трёх значений: X, Y и Z
Каждый из параметров Transform также представляет собой структуру из трёх значений: X, Y и Z

Получается, что Transform — это структура, которая внутри содержит другие вложенные структуры. Это даёт гибкость: можно работать с Transform целиком, как с одной переменной, так и с его отдельными частями (например, только со Scale или с Location.X).

Transform — это лишь один пример. В Unreal Engine есть множество встроенных структур, например:

  • Color — хранит компоненты R, G, B, A;
  • Vector — объединяет координаты X, Y, Z;
  • Rotator, LinearColor, HitResult, InputAxisKeyMapping — и многие другие.

Все они демонстрируют, как удобно объединять данные по смыслу, а не по типу.

Также, несмотря на большое количество встроенных структур, в реальном проекте часто требуется объединять свои какие-то данные под конкретные задачи. В таких ситуациях создаются пользовательские структуры, которые используются, например, для:

  • свойств поднимаемого предмета,
  • параметров оружия,
  • описания квеста,
  • характеристик врага,
  • настроек способностей.

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

Создание пользовательской структуры (User Struct)

Итак, вернемся к нашему примеру — к поднимаемому предмету BP_Pickup. Наши разрозненные переменные — NamePickup, Description и Amount — сейчас существуют каждая отдельно, но ведь по смыслу они описывают одно и то же — свойства подбираемого предмета. Поэтому логично объединить их в одну общую переменную, собрать все эти данные в один тип, чтобы хранить и передавать их вместе.

Создадим собственный тип данных, который объединит эти значения, — и этот тип данных как раз и будет структура.

  1. Откроем Content Browser.
  2. Кликнем правой кнопкой мыши и в списке Blueprints выберем пункт Structure. Дадим имя новой структуре. Негласное правило Unreal Engine — для структур в Blueprint использовать префикс S_ / Struct_. Мы создаём структуру, описывающую свойства поднимаемого предмета, поэтому назовём её, например, S_PropertiesPickup. Важно понимать: сейчас мы создаём не переменную, а новый тип данных. Это значит, что мы пока не сохраняем значение, а просто определяем «форму» данных — то есть шаблон, по которому затем будем создавать переменные этого типа.
Создание пользовательской структуры в Unreal Engine: Content Browser / Add / Blueprints / Structure
Создание пользовательской структуры в Unreal Engine: Content Browser / Add / Blueprints / Structure

Итак, откроем файл созданной структуры. Перед нами откроется окно редактирования структуры, в котором имеются две вкладки:

  • Structure — это место, где мы определяем состав структуры, то есть какие переменные она будет хранить;
  • Default Value — вкладка, где можно задать значения по умолчанию для каждой переменной.
Вкладки Structure и Default Value редактора структур
Вкладки Structure и Default Value редактора структур

Начнём со вкладки Structure. Вверху можно задать описание в поле Tool Tip — это подсказка, которая будет отображаться при наведении на структуру. В данном случае заполнять мы ее не будем.

По кнопке Add Variable создадим внутри структуры пока только 2 переменные:

  • Name (название предмета) — тип данных String;
  • Amount (количество) — тип данных Integer.
Добавление переменных Name (String) и Amount (Integer) в пользовательской структуре S_PropertiesPickup
Добавление переменных Name (String) и Amount (Integer) в пользовательской структуре S_PropertiesPickup

Рядом с каждой переменной есть маленький треугольник — раскрывающий меню с дополнительными настройками.

Меню дополнительных настроек переменных в структуре
Меню дополнительных настроек переменных в структуре

Здесь для каждой создаваемой переменной внутри структуры можно задать настройки:

  • ToolTip — описание, всплывающая подсказка;
  • Editable — возможность редактирования переменной в Blueprint-классе;
  • SaveGame — участие данной переменной в системе сохранения игры;
  • MultiLine Text — использование многострочного текста.

Перейдём на вкладку Default Values. Здесь можно задать стандартные значения по умолчанию для каждой переменной. Но можно оставить эти поля и пустыми, в данном случае так и сделаем.

Вкладка Default Values редактора структур
Вкладка Default Values редактора структур

После этого нажимаем Save — структура готова.

Теперь в проекте есть новый тип данных — S_PropertiesPickup, объединяющий два поля (Name и Amount) и позволяющий создавать переменные этого типа в любых других Blueprints. Фактически, мы создали «компактный контейнер» для данных предмета, избавившись от множества отдельных переменных и сделав описание предмета логичным и удобным.

Таким образом, пользовательские структуры дают возможность гибко моделировать собственные сущности, создавать вложенные данные и поддерживать в проекте аккуратную и понятную архитектуру данных.

Как работать со структурой

Теперь перейдём в наш BP_Pickup, скомпилируем его, чтобы внутри класса обновилась информация о созданной нами структуре, и создадим новую переменную, которая в качестве типа данных будет использовать только что созданную структуру. Назовём её, например, PropertiesPickup, а в качестве типа данных выберем нашу структуру S_PropertiesPickup через поиск по имени.

Переменная PropertiesPickup с типом данных пользовательской структуры S_PropertiesPickup
Переменная PropertiesPickup с типом данных пользовательской структуры S_PropertiesPickup

Нажимаем Compile, и в Details появится блок Default Value со всеми полями структуры: Name и Amount. Заполним их:

  • Name: «water»,
  • Amount: «10».

Также изменим интерфейс BPI_Pickup. Теперь функция GetPickupData должна возвращать не разрозненные переменные, а одну — структуру типа S_PropertiesPickup.

Изменение возвращаемых параметров интерфейсной функции GetPickupData
Изменение возвращаемых параметров интерфейсной функции GetPickupData

Обновим реализацию этой функции в BP_Pickup: подключим к Return Node переменную PropertiesPickup.

Подключение переменной PropertiesPickup к выходному параметру функции GetPickupData
Подключение переменной PropertiesPickup к выходному параметру функции GetPickupData

Старые разрозненные переменные (NamePickup, Description, Amount) теперь больше не нужны — их можно смело удалить. Вся информация о поднимаемом предмете сейчас содержится в одной переменной типа структура S_PropertiesPickup, что гораздо удобнее и нагляднее. Причём этим типом данных можно пользоваться уже не только в BP_Pickup, но и в любых других классах проекта. Если ещё где-то нужен объект, у которого есть имя и количество, достаточно создать там переменную типа S_PropertiesPickup и работать с ней как с единым целым.

Чтение значений структуры (Get Struct, Get Structure)

Итак, в блюпринте персонажа у нас теперь в интерфейсной функции возвращаются не несколько переменных, а одна — наша структура.

Возвращаемый параметр PropertiesPickup функции GetPickupData, вызываемой внутри блюпринта персонажа игрока
Возвращаемый параметр PropertiesPickup функции GetPickupData, вызываемой внутри блюпринта персонажа игрока

Если структура не отображается, можно нажать на функции правой клавишей мыши и выбрать пункт Refresh Nodes, чтобы обновить ноду в соответствии с новым возвращаемым значением.

Для удобства сохраним возвращаемый параметр в переменную через Promote to variable и дадим имя переменной PropertiesPickup.

Сохранение возвращаемого параметра в переменную через Promote to variable
Сохранение возвращаемого параметра в переменную через Promote to variable

Переменная структуры в Blueprint ведёт себя точно так же, как и обычная переменная: если перетащить PropertiesPickup в Event Graph, можно, как и для любой переменной в Blueprint, выбрать функцию Get (получить значения структуры) или Set (установить новое значение).

Функции Get и Set переменной со структурным типом данных
Функции Get и Set переменной со структурным типом данных

Выберем Get. После этого в переменной будет доступен один пин, который возвращает нам нашу структуру.

Функция Get переменной со структурным типом данных
Функция Get переменной со структурным типом данных

Формально, работая со структурой, мы работаем с одной переменной, но внутри неё скрыт целый набор полей. Чтобы получить доступ к отдельным полям, можно использовать два способа.

Split Struct Pin (Get)

Первый способ — Split Struct Pin. Данная команда вызывается кликом правой кнопки мыши по пину структуры. После вызова структура «распадётся» на отдельные выходы: Name и Amount. Теперь к каждому полю можно обращаться напрямую.

Разделение структуры через Split Struct Pin ноды Get
Разделение структуры через Split Struct Pin ноды Get

Воспользуемся данными значениями и выведем их на экран через Print String, предварительно объединив их значения в одну строку функцией Append.

Вывод значений структуры через Print String внутри блюпринта персонажа игрока
Вывод значений структуры через Print String внутри блюпринта персонажа игрока

Проверим всё в игре. Запускаем игру, подходим к поднимаемому предмету — да, всё работает, имя и количество выводится на экран.

Вывод значения структуры на экран при взаимодействии игрока с поднимаемым предметом в игре
Вывод значения структуры на экран при взаимодействии игрока с поднимаемым предметом в игре

Теперь на практике почувствуем преимущество структур. Откроем файл нашей структуры и добавим в нее новое поле Description типа Text.

Новая переменная Description в структуре S_PropertiesPickup
Новая переменная Description в структуре S_PropertiesPickup

В таком случае нам остается просто зайти в BP_Pickup и заполнить само значение Description внутри переменной PropertiesPickup. Всё. Больше ничего делать нам не надо.

Заполнение поля Description в переменной PropertiesPickup
Заполнение поля Description в переменной PropertiesPickup

Перейдем в блюпринт игрока, скомпилируем его, обновим ноду интерфейсной функции и видим, что поле Description теперь доступно и в игроке. Подключим его к Append и проверим в игре вывод информации. Запускаем игру, подходим к поднимаемому предмету — да, все работает, имя, количество и описание выводится на экран.

Вывод значения Description через Append и Print String внутри персонажа игрока
Вывод значения Description через Append и Print String внутри персонажа игрока

Таким образом, при использовании структуры и передачи связанных по смыслу данных через неё, мы упрощаем управление этими данными. В текущей ситуации нам уже не нужно было заходить в интерфейс и что-то там менять. А у нас самый простейший пример…

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

И одним из таких инструментов и являются структуры, которые дают возможность структурировать данные в объектах. Но, как я говорил ранее, использование структур этим не ограничивается, они также используются с массивами и являются неотъемлемой частью таблиц данных. Об этом мы подробнее поговорим в конце данного видео. А сейчас давайте вернёмся обратно к рассматриваемому вопросу — напомню, мы разбирали вопрос получения данных из структуры. А конкретно рассмотрели первый метод — Split Struct Pin, разбивающий пин структуры в ноде Get на отдельные пины внутренних значений структуры.

Метод Split Struct Pin удобен, если полей немного. Но если структура большая, «гребёнка» пинов захламляет граф. Например, перейдем в файл нашей структуры и добавим несколько полей для теста. Возвращаемся в блюпринт персонажа, компилируем и видим, что функция Get разрослась в размере.

Большое количество пинов структуры в ноде Get
Большое количество пинов структуры в ноде Get

Чтобы вернуть пин структуры в исходный вид, можно снова кликнуть правой кнопкой по любому из разделённых пинов и выбрать Recombine Struct Pin.

Recombine Struct Pin
Recombine Struct Pin

Break Struct

Второй альтернативный способ получения отдельных данных структуры — Break Struct.

Вытянем провод из пина структуры и введём в поиск Break. Выберем Break S_PropertiesPickup. Появится нода Break Struct, у которой:

  • во-первых, есть кнопка-стрелка для сворачивания/разворачивания, которая позволяет визуально уменьшить ноду;
  • а во-вторых, в Details есть опция Hide Unconnected Pins, позволяющая скрывать неиспользуемые выходы.
Нода Break Struct и её настройки Details
Нода Break Struct и её настройки Details

Таким образом, если нам нужны только выходы Name, Description и Amount, остальные можно скрыть, и граф останется аккуратным.

Перезапись значений структуры (Set Struct, Set Structure)

Перейдем в блюпринт BP_Pickup. В нашем примере BP_Pickup — это коробка с бутылками воды. При взаимодействии игрок может брать из коробки по одной бутылке. Следовательно, сделаем так, чтобы при взаимодействии уменьшалось количество бутылок и обновлённое значение сохранялось внутри поля Amount структуры PropertiesPickup.

Перед началом убедимся, что в структуре S_PropertiesPickup остались только нужные поля: Name, Description и Amount. Все тестовые переменные, которые создавались ранее «для примера», можно удалить. Работать будем только с полем Amount внутри переменной PropertiesPickup.

Для изменения значений структуры также имеется несколько способов. Разберём их по порядку.

Split Struct Pin (Set)

1 способ — через Split Struct Pin. Этот способ полностью аналогичен тому, как мы получали значения из структуры. Только теперь мы разбиваем входящий пин на ноде Set, чтобы изменить отдельные поля структуры прямо внутри графа.

Для реализации логики уменьшения количества возьмём переменную PropertiesPickup и создадим для неё ноду Break S_PropertiesPickup — это позволит получить доступ к полю Amount. Из выхода Amount вытянем ноду Substract, которая производит вычитание. Внутри нее укажем 1, чтобы вычесть из Amount единицу.

Теперь нужно передать это новое значение обратно в структуру. Подключаем результат вычитания (наш новый Amount) к соответствующему пину в Set. Однако, если мы в ноде Set оставим остальные пины просто пустыми, они перезапишутся значениями по умолчанию, то есть Name и Description обнулятся.

Чтобы этого избежать, из ноды Break возьмем текущие значения Name и Description и подключим их к соответствующим входам в Set.

В результате мы создаём полное обновление структуры, где поля Name и Description сохраняются в прежнем виде, а поле Amount уменьшается на единицу.

Разделение структуры через Split Struct Pin ноды Set
Разделение структуры через Split Struct Pin ноды Set

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

Make Struct

2 способ — через Make Struct. Этот метод концептуально похож на первый, только вместо разбивки Set используется отдельная нода Make Struct.

Для начала в ноде Set отключим все параметры и соберем их обратно в пин структуры. Далее из этого пина вытянем провод и введем в поиск Make S_PropertiesPickup. В получившейся ноде Make у нас также будут три входа — Name, Description, Amount.

Как и в первом методе, новое значение (уменьшенное Amount) подаём на соответствующий вход. Чтобы не обнулять остальные поля, передаём их текущие значения из Break. После этого результат из Make Struct будет передан во вход Set PropertiesPickup — таким образом структура перезапишется целиком.

Нода Make Struct (создание структуры)
Нода Make Struct (создание структуры)

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

Set Members in Struct

3 способ — использовать для переменной структуры отдельную ноду Set Members in Struct. Это наиболее удобный и практичный способ обновления данных в структуре. Он позволяет изменять только определённые поля, не трогая остальные.

Вытянем провод из переменной PropertiesPickup и введём в поиск Set Members in S PropertiesPickup.

Появится нода, которая по умолчанию имеет все поля выключенными. В её параметрах Details можно отметить галочками, какие конкретно поля структуры нужно изменить. В нашем случае нам нужно обновить только Amount, поэтому включаем соответствующее поле Amount. Остальные пины при этом даже не появятся на графе — структура сохранит их прежние значения автоматически.

Из Break S_PropertiesPickup берём текущее значение Amount, вычитаем 1 и результат подключаем в пин Amount в Set Members in Struct.

Теперь весь код стал чище: никаких обнулений, никаких «лишних» пинов и проводов, только логика изменения нужного поля.

Нода Set Members in Struct
Нода Set Members in Struct

Проверим, как всё работает в игре. Запускаем игру, пересекаемся с пикапом, значение Amount уменьшается, а Name и Description остаются прежними. Нода Set Members изменила только указанное поле, не затронув остальные.

Итак, в итоге, при работе со структурами в Blueprint мы всегда оперируем одной переменной, внутри которой хранится набор полей. Для доступа к отдельным значениям структуры при чтении используются или Split Struct Pin, или Break Struct (второй — предпочтительнее для сложных структур). Для записи значений удобнее всего использовать отдельную ноду Set Members in Struct, так как она позволяет обновлять только нужные поля, не трогая остальные.

Другие сценарии использования структур

Массив структур (Array Structure)

Помимо всего, что мы уже обсудили ранее, структуры часто применяются в роли типа данных для массива. Это особенно удобно при создании различных систем, где несколько однотипных объектов должны храниться вместе, а также содержать дополнительную характеризующую их информацию — например, в инвентаре персонажа.

Итак, перейдём в Blueprint персонажа и создадим простой инвентарь.

Для начала создадим переменную InventoryArray, тип данных установим String, а тип контейнера — Array.

Скомпилируем Blueprint. После этого в Details для переменной InventoryArray откроем поле Default Value и заполним массив, например, такими элементами:

  • «bread»,
  • «water»,
  • «apple»,
  • «apple».
Простейший инвентарь InventoryArray на основе массива типа String
Простейший инвентарь InventoryArray на основе массива типа String

Таким образом, мы получили самый простой пример инвентаря, который можно динамически изменять во время игры — добавлять, удалять или искать элементы прямо в процессе игры. Поскольку массив в Unreal Engine позволяет хранить повторяющиеся значения, мы можем использовать эту особенность для подсчёта количества одного и того же предмета (например, в нашем массиве «apple» встречается дважды, и при переборе элементов массива мы можем подсчитать повторы Apple).

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

Решение очевидно — использовать массив структур. Каждый элемент такого массива будет не просто строкой, а структурой, которая описывает один предмет инвентаря. Структура, в свою очередь, может хранить любое количество связанных свойств: название, количество, вес, тип и другие характеристики.

Создадим простую структуру, которая будет описывать элемент инвентаря. В Content Browser кликнем правой кнопкой мыши, перейдем в меню Blueprints / Blueprint Structure и назовём новую структуру S_Inventory. Внутри неё создадим два поля:

  • Name (название предмета) — тип String;
  • Amount (количество) — тип Integer.
Структура S_Inventory для массива инвентаря
Структура S_Inventory для массива инвентаря

Сохраним структуру.

Теперь перейдём обратно в Blueprint персонажа и скомпилируем его — это нужно для обновления списка доступных структур. Создадим в нём переменную Inventory, в качестве типа данных выберем структуру S_Inventory, а тип контейнера укажем Array.

После компиляции во вкладке Details, в разделе Default Value переменной Inventory мы можем задать начальное состояние инвентаря — добавить несколько структурных записей. Например:

  • Name: «bread», Amount: 2,
  • Name: «water», Amount: 1,
  • Name: «apple», Amount: 5.
Массив структур Inventory
Массив структур Inventory

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

Далее можно работать с массивом структур так же, как с любым обычным массивом. Например, использовать цикл For Each Loop — для перебора всех элементов инвентаря и доступа к каждому предмету, или многочисленные функции для работы с массивами, например Add, Remove, Set Array Element — для добавления, удаления или перезаписи элементов.

Подробнее про массивы и функции для работы с массивами вы можете узнать в отдельной статье: массивы в Unreal Engine Blueprint.

При этом при обращении к элементу массива мы получим структуру S_Inventory. Чтобы работать с её полями, можно использовать те же инструменты, что мы рассматривали ранее:

  • Split Struct Pin — чтобы разложить структуру на отдельные пины в нодах Get или Set;
  • Break S_Inventory — для чтения отдельных значений, таких как Name и Amount;
  • Make S_Inventory или Set Members in S_Inventory — для создания или обновления значений внутри структуры.
Цикл For Each Loop и функция Add для работы с массивом структур
Цикл For Each Loop и функция Add для работы с массивом структур

Таким образом, массив структур позволяет строить инвентарные системы любого уровня сложности — от простых наборов предметов до полноценных хранилищ с весом, типами, редкостью и описаниями элементов инвентаря.

Структуры и таблицы данных (Data Tables)

Как уже упоминалось в самом начале, структуры также используются при создании таблиц данных (Data Tables). Работу с таблицами данных мы подробно рассмотрим в отдельной статье. А сейчас, поскольку тема этой статьи — структуры, давайте просто посмотрим, каким образом они определяют столбцы в таблице данных.

Создадим новую структуру и назовём её S_Pickup.

Добавим в неё следующие поля:

  • Name (название предмета) — тип String;
  • Description (описание предмета) — тип Text;
  • Mesh (3D‑модель объекта) — тип Static Mesh Object.
Структура S_Pickup, предназначенная для таблицы данных (Data Table)
Структура S_Pickup, предназначенная для таблицы данных (Data Table)

Сохраним структуру.

Теперь перейдём снова в Content Browser и в контекстном меню по правой клавиши мыши выберем пункт Miscellaneous / Data Table для создания таблицы данных.

Создание таблицы данных (Data Table)
Создание таблицы данных (Data Table)

После этого Unreal Engine предложит выбрать структуру, на основе которой будут сформированы столбцы таблицы. Укажем нашу структуру S_Pickup.

Выбор структуры S_Pickup при создании таблицы данных (Data Table)
Выбор структуры S_Pickup при создании таблицы данных (Data Table)

После выбора структуры она будет использована как шаблон, и таблица данных создастся автоматически. Дадим ей имя, например DT_PickupItems, и откроем данный файл.

В результате мы увидим, что столбцы таблицы соответствуют полям структуры — Name, Description и Mesh.

Столбцы таблицы данных (Data Table), основанные на структуре S_Pickup
Столбцы таблицы данных (Data Table), основанные на структуре S_Pickup

Также, помимо этих столбцов, в начале таблицы присутствует ещё один служебный столбец — Row Name. Он не входит в нашу структуру, а создаётся системой автоматически. Его задача — хранить уникальные имена строк таблицы, чтобы по ним можно было обращаться к строкам таблицы из кода.

Далее, нажав Add, мы можем добавлять новые строки таблицы и заполнять каждую строку данными. Наша структура при этом определяет состав и типы всех столбцов, обеспечивая строгую согласованность данных.

Заполненная строка таблицы данных
Заполненная строка таблицы данных

Краткий итог статьи

В этой статье мы разобрали, что такое структуры (Struct, Structure) в Unreal Engine Blueprint. Напомню, структуры — это тип данных, который объединяет несколько связанных переменных (полей) в один логический контейнер. Структура позволяет описывать один объект или сущность целиком: например, предмет пикапа, элемент инвентаря или строку таблицы данных.

Структуры используются, когда нужно:

  • хранить свойства одного объекта в одном месте;
  • передавать набор данных между объектами одной переменной вместо множества параметров;
  • организовывать коллекции данных — иначе говоря, массивы структур, например для инвентаря;
  • а также для определения столбцов и их типов данных в Data Tables.

Напоминаю про мою бесплатную книгу по Blueprint для Unreal Engine.

Основная цель книги — дать общий, целостный и понятный обзор системы Blueprint. Книга предлагает взгляд с высоты «птичьего полёта» — ясную и чёткую карту игрового мира Unreal Engine, которая поможет увидеть ключевые взаимосвязи и понять, как устроена система визуального скриптинга.

Зарегистрироваться в ЛК и скачать книгу [Blueprints. Взгляд с высоты «птичьего полёта»]

Оцените статью
( 1 оценка, среднее 5 из 5 )
Поделитесь этой статьей со своими знакомыми в социальных сетях, возможно, эта статья кому-то будет полезна
Unreal Engine - это просто
Добавить комментарий

Нажимая на кнопку "Отправить комментарий", я даю согласие на обработку персональных данных и принимаю политику конфиденциальности.