Pawn в Unreal Engine — разбираем класс Пешки

Друзья, приветствую, с Вами Будуев Антон. В этой статье мы обсудим Blueprint-класс Pawn в Unreal Engine (UE4, UE5) или, как его еще называют по-русски, Павн, Пон или Пешка.

Вскоре выйдет моя бесплатная книга по Blueprints для Unreal Engine в PDF формате. Как она выйдет, рекомендую её скачать, чтобы Вы детально изучили блюпринты Анрил Энджин.

Unreal Engine Pawn

В Unreal Engine класс Pawn представляет собой основу для создания управляемых игровых объектов, которые могут контролироваться как игроком, так и искусственным интеллектом (ИИ). Это базовый класс для всего, что ходит, бегает, прыгает, ездит, плавает, летает и так далее.

Pawn (пешка) — это подкласс ActorPawn (Пешка) — это подкласс Actor, обладающий расширенным функционалом, который связывает объект Пешки с управляющим контроллером. Стоит отметить, что по умолчанию Pawn не отвечает за логику управления. Он лишь предоставляет возможность управлять собой. Как правило, логика управления лежит на контроллере. Основная задача Pawn — служить контролируемым телом для контроллера.

Класс Pawn в Unreal Engine
Класс Pawn в Unreal Engine

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

Pawn и Controller

Важно понимать, что к каждому объекту Pawn по умолчанию всегда подключён один из контроллеров: либо Player Controller, либо AI Controller. При этом между контроллерами и Пешками существует отношение один к одному: каждый контроллер управляет только одной Пешкой в любой момент времени. Однако в некоторых случаях это правило можно изменить.

При создании объекта класса Pawn по умолчанию им уже владеет ИИ, который основан на базовом классе AI Controller, встроенном в движок Анрил Энджин.

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

Еще раз повторюсь, по умолчанию Пешкой всегда владеет базовый или пользовательский AI-контроллер. Но во время игры текущий Player-контроллер может перехватить владение этой Пешкой, используя функцию Posses. Контроллер игрока будет владеть Пешкой до тех пор, пока она не будет удалена или пока функция UnPosses не отделит контроллер от неё. После отсоединения Пешкой вновь будет владеть AI-контроллер, указанный в параметрах Details.

Также Player Controller может владеть конкретной Пешкой сразу после начала игры, если она указана в качестве Default Pawn в настройках Game Mode текущего запущенного игрового уровня.

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

Передвижение объекта класса Pawn

Как я уже говорил ранее, класс Pawn в Unreal Engine (UE4, UE5) предназначен для расширения класса Actor. Он позволяет связать контроллер с объектом Pawn, чтобы контроллер мог управлять этим объектом, получая входные сигналы от игрока (Input) или ИИ. Других специальных функций у Pawn нет. Поэтому, чтобы заставить объект Пешки передвигаться в ответ на входные сигналы от контроллера, необходимо вручную описать логику его перемещения.

В Blueprints есть несколько способов добавить движение к объектам класса Pawn. Один из них — функция Add Actor World Offset, которая смещает объект на определённое количество единиц от его текущего положения. Ещё один способ — использовать функцию Set Actor Location, которая устанавливает новые координаты для объекта. Настройка этой функции немного сложнее, чем предыдущей, так как требует расчёта новых координат. В данной статье мы рассмотрим пример создания движения для Пешки с помощью Set Actor Location в разделе практики.

Также для базового передвижения Pawn можно использовать специальный компонент (Component) — Floating Pawn Movement, который внутри себя содержит настройки скорости, ускорения и торможения. Данный компонент мы разберём в отдельной статье.

Если же Вы хотите использовать более широкий функционал для создания реалистичного передвижения двуногого персонажа, который к тому же еще и автоматически реплицируется, то тогда нужно использовать не класс Pawn, а унаследованный от него подкласс Character. Он содержит все необходимые компоненты для создания двуногого персонажа. Кроме того, только в этом классе доступен специализированный компонент Character Movement, обеспечивающий корректное физическое передвижение двуногого персонажа.

Настройки Details для Pawn (class Defaults)

В меню Details содержатся как унаследованные параметры от класса Actor, так и настройки, характерные для разбираемого класса Пешки. Details для Actor мы уже разбирали в соответствующей статье про класс Акторов. Сейчас же сконцентрируемся только на настройках Pawn.

Настройки Details для Pawn (class Defaults)
Настройки Details для Pawn (class Defaults)
  • Use Controller Rotation Pitch (bool, по умолчанию False) — включить повтор наклона Пешки (Pitch) в соответствии с наклоном Player Controller (действует, если Пешкой владеет контроллер игрока).
  • Use Controller Rotation Yaw (bool, по умолчанию False) — включить повтор поворота Пешки (Yaw) в соответствии с поворотом Player Controller (действует, если Пешкой владеет контроллер игрока).
  • Use Controller Rotation Roll (bool, по умолчанию False) — включить повтор крена Пешки (Roll) в соответствии с креном Player Controller (действует, если Пешкой владеет контроллер игрока).
  • Can Affect Navigation Generation (bool, по умолчанию False) — включить влияние объекта Pawn на генерацию навигационной сетки. Если False — влияние самого объекта на генерацию выключено, но компоненты этого объекта по-прежнему будут влиять на данную генерацию.
  • Auto Possess Player — опция определяет какой Player Controller автоматически овладеет текущей Пешкой при старте её работы на игровом уровне. По умолчанию опция отключена (Disabled).
  • Auto Possess AI — опция определяет время, когда AI Controller овладеет текущей Пешкой (если Auto Possess PlayerDisabled). На выбор представлено 4 варианта:
    Disabled — авто овладевание полностью выключено;
    Placed in World — авто овладевание происходит только при ручном помещении Pawn в игровой мир в Unreal Editor;
    Spawned — авто овладевание происходит только при спавне Пешки через код во время игры;
    Placed in World or Spawned — авто овладевание происходит всегда, и при ручном размещении объекта, и при его спавне.
  • AI Controller Class — выбор класса AI Controller для управления Pawn ИИ.
  • Override Input Component Class — выбор класса Input Component для переопределения базового Default Input Component Class.

Override functions. Переопределение событий в Pawn

В классе Pawn, в разделе Function / Override, собраны системные события Unreal Engine, которые можно переопределить. Это означает, что в этих событиях уже заложена определённая логика, но Вы можете изменить её в соответствии со своими потребностями.

В данном разделе представлены события, унаследованные как от класса Actor, так и от Pawn. Чтобы подробнее узнать о переопределяемых событиях, относящихся к классу Actor, рекомендую ознакомиться с ними в отдельной статье: Events Actor. Здесь же сосредоточимся на событиях, связанных с Pawn.

Override Events класса Pawn в Unreal Engine
Override Events класса Pawn в Unreal Engine
  • Event Possessed

Событие Possessed

Событие Possessed вызывается при овладевании контроллером текущим Pawn. В многопользовательских играх вызов происходит только на сервере.

Исходящий параметр:
New Controller — контроллер, который начал владеть Пешкой.

  • Event Unposessed

Событие Unposessed

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

Исходящий параметр:
Old Controller — контроллер, который перестал владеть Пешкой.

  • Event Receive Controller Changed

Событие Receive Controller Changed

Событие Receive Controller Changed вызывается на сервере и клиенте-владельце после смены контроллера у Пешки. Именно через данное событие имеется возможность на сетевых клиентах отследить у Пешки смену контроллера. Другие события, упоминаемые выше (Possessed и Unposessed) не позволяют этого сделать, так как вызываются только на сервере.

Исходящие параметры:
Old Controller — контроллер, который перестал владеть Пешкой.
New Controller — контроллер, который начал владеть Пешкой.

  • Event Receive Restarted

Событие Receive Restarted

Событие Receive Restarted вызывается в момент перезапуска текущего Pawn (обычно при изменении владения) на сервере или клиенте-владельце.

Подклассы Pawn в Unreal Engine

Default Pawn

Default PawnВ целом, подкласс Default Pawn мало чем отличается от базового родительского Pawn. Если базовый класс Пешки просто связывает создаваемый объект с управляющим контроллером, то подкласс Default Pawn содержит ещё некоторые дополнительные компоненты и функции, предоставляя разработчику более удобный и подготовленный вариант управляемого контроллером объекта.

А именно, класс Default Pawn в UE5 (UE4) содержит собственный компонент Movement Component, обеспечивающий базовое движение объекта. Для взаимодействия с физическим миром предусмотрена минимальная коллизия в виде компонента Collision Component, представляющего собой сферу. Также имеется компонент Static Mesh Component, отвечающий за отображение 3D-модели в игровом пространстве.

Но, если коллизию и Mesh разработчик может и сам добавить в класс Pawn, то вот компонент Movement Component — уже нет. Собственно, этим компонентом в основном и различается Default Pawn от базового класса Пешки.

Default Pawn Movement Component — это специализированный компонент для создания передвижения (полёта) объекта без учёта гравитации, включающий в себя базовые настройки этого передвижения, такие как MaxSpeed, Acceleration, Deceleration и ряд других. Более подробно данный компонент мы рассмотрим в отдельной статье.

Spectator Pawn

Spectator PawnКласс Spectator Pawn является подклассом Default Pawn. По-русски его еще называют «Пешка-Зритель». В целом, это тот же Default Pawn, с теми же компонентами коллизии и передвижения, но только без компонента Mesh.

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

Character

CharacterCharacter является подклассом Pawn с добавлением компонентов Character Movement Component, Capsule Component и Skeletal Mesh Component. Данный подкласс предназначен для вертикально-ориентированного представления игрока (в основном двуногого персонажа), который может ходить, бегать, прыгать, летать и плавать по миру. Также он содержит реализации базовых сетевых моделей и моделей ввода.

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

Практика в Unreal Engine: создание базового передвижения объекта класса Pawn. Функция Set Actor Location

Цель данной практики — воочию посмотреть, что объекты Pawn действительно привязываются к контроллеру, когда он начинает владеть ими, и получают управление от него (Input-сигналы от игрока). А также на примере разобрать принципы создания базового движения Pawn при помощи функции Set Actor Location, которая способна задавать новые координаты объекту.

Саму практику мы будем реализовывать в стандартном шаблоне проекта игры Unreal Engine (UE5) от первого лица.

Объект класса Pawn

  1. Через меню Blueprint class в Content Browser создадим Blueprint класса Pawn. Назначим ему имя BP_Pawn и разместим его на игровом уровне.
  2. Для визуализации данного объекта добавим ему Mesh в виде сферы. Для этого в разделе компонентов выберем компонент Sphere и перенесем его в корневой root-уровень, кликнув по компоненту Sphere левой клавишей мыши и не отпуская перенеся его на элемент DefaultSceneRoot. Эту операцию сделать нужно обязательно, чтобы коллизия компонента Sphere учитывалась, как коллизия всего Актора.
Компонент Sphere в BP_Pawn
Компонент Sphere в BP_Pawn

Pawn и Player Controller

  1. Перейдем во вкладку Event Graph и разместим в ней событие Key F, которое срабатывает по нажатию игроком клавиши F на клавиатуре. Чтобы проверить работу данного события, подсоединим к нему ноду Print String, которая должна будет вывести текст «Hello» при нажатии на F.
При нажатии на F вызовем функцию Print String
При нажатии на F вызовем функцию Print String
  1. Скомпилируем проект, перейдем на игровой уровень и запустим игру. Первое, что мы увидим, это то, что игра запустилась от стандартного персонажа данного шаблона игры от первого лица. Далее мы можем увидеть наш BP_Pawn, размещённый на уровне в виде сферы.
  2. Нажмём клавишу F и… ничего не произойдёт. Хотя BP_Pawn на сцене, и в нём имеется код, выводящий строку «Hello», но при нажатии на F, — всё равно ничего не происходит.

Почему код не срабатывает? Всё просто. Дело в том, что за приём входящих данных от игрока (Input) отвечает Player Controller. А в текущий момент он владеет стандартным персонажем, а не нашим BP_Pawn. Соответственно, нажатие F в нашу Пешку не транслируется. Поэтому, чтобы исправить данную ситуацию, нужно Player Controller связать с нашим BP_Pawn, иначе говоря, нужно сделать так, чтобы он завладел нашей пешкой.

В данной практике мы их свяжем принудительно через свойства Details нашей Пешки. Для этого выделим Пешку, перейдём во вкладку Details в раздел Pawn и в параметре Auto Possess Player укажем Player 0. Так как в нашей демо-игре всего один игрок, то Player 0 и будет означать контроллер текущего игрока.

Во вкладке Details в разделе Pawn в параметре Auto Possess Player нужно указать Player 0
Во вкладке Details в разделе Pawn в параметре Auto Possess Player нужно указать Player 0

Запустим игру и… первое, что увидим, это то, что игрок при старте игры перенёсся из стандартного персонажа в наш BP_Pawn и смотрит на мир камерой по умолчанию, исходящей из центра нашей Пешки. Далее нажмём F, и на экране отобразится текст «Hello».

При нажатии на F на экране отображается текст «Hello»
При нажатии на F на экране отображается текст «Hello»

Всё это стало возможным благодаря тому, что при старте игры контроллер игрока овладел нашим Pawn и стал передавать входящие сигналы игрока (Input сигналы с клавиатуры) в контролируемую Пешку, то есть в наш BP_Pawn.

Поворот камеры BP_Pawn

Теперь реализуем поворот камеры от поворота мыши. Для этого нужно описать поворот самого Player Controller при повороте мыши игрока. Писать этот код с нуля мы не будем, а просто возьмем его и скопируем из персонажа от первого лица данного шаблона игры.

  1. Перейдём в Content Browser по пути /All/Game/FirstPerson/Blueprints и откроем блюпринт BP_FirstPersonCharacter.
  2. Выделим блок кода Camera Input и скопируем его.
  3. Далее вставим скопированный код в наш BP_Pawn.
Блок кода Camera Input
Блок кода Camera Input

Данный код реагирует на событие поворота мыши и передаёт данные о повороте текущему контроллеру.

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

Set Actor Location

Начнём реализовывать базовое передвижение объекта BP_Pawn при нажатии на клавишу F при помощи функции Set Actor Location. Данная функция задаёт новые координаты Актора на игровой сцене.

Чтобы реализовать при помощи этой функции передвижение, нужно к текущим координатам объекта добавить смещение в несколько пунктов. И получившиеся новые координаты отправить в Set Actor Location. Данную операцию нужно производить постоянно, в цикле. Таким образом, объект будет передвигаться на величину смещения.

Теперь необходимо рассчитать смещение. Для этого мы получим текущий поворот контроллера игрока и на основе этого поворота вычислим вектор направления вперёд. Этот вектор будет характеризовать направление как контроллера игрока, так и камеры. А также он и станет нашим смещением, которое мы добавим к текущим координатам объекта BP_Pawn.

  1. Удалим функцию Print String, она нам больше не нужна.
  2. После события Key F вызовем функцию Set Actor Location.
  3. Далее нодой Get Control Rotation получим текущий поворот контролера игрока.
  4. Из этого поворота нодой Get Forward Vector вернём единичный вектор направления вперёд.
  5. Далее получим текущие координаты нашего BP_Pawn. Сделаем это нодой Get Actor Location.
  6. Теперь, на последнем шаге, вычислим новые координаты, куда функция Set Actor Location перенесет наш объект. Для этого к текущим координатам из пункта 5 добавим координаты вектора вперед из пункта 4. И весь результат направим в параметр New Location функции Set Actor Location.

Скриншот

Добавляем перемещение Pawn при помощи функции Set Actor Location
Добавляем перемещение Pawn при помощи функции Set Actor Location

Код на BlueprintUE

Если код отображается без связей, нажмите сверху кода на кнопку «Graph» Если код отображается без связей, нажмите сверху кода на кнопку "Graph"


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

Базовое передвижение Pawn

Я думаю, Вы согласитесь, что передвигать объект, постоянно нажимая F, — это не вариант. В качестве цикла для постоянного запуска Set Actor Location должно выступать что-то другое, а вот нажатие на F просто должно включать и выключать этот цикл.

Предлагаю в качестве цикла использовать событие Event Tick. Данное событие срабатывает каждый такт обновления экрана игры. С одной стороны, событие достаточно простое, но с другой стороны, его нужно использовать с особой осторожностью, чтобы не загрузить систему тяжёлым кодом при каждом обновлении экрана. Более подробно об этом событии и тонкостях работы с ним Вы можете узнать в отдельной статье: Unreal Engine Event Tick.

  1. Вызовем событие Event Tick.
  2. Напрямую Set Actor Location к Event Tick мы подключать не будем, так как тогда движение будет постоянным, не реагирующим на нажатие / отжатие F. Чтобы всё это связать воедино, используем ноду Gate, которая действует как некие ворота, пропускающие и не пропускающие сигнал.
  3. Итак, в параметр Enter ноды Gate подадим поток от Event Tick. Так как у Gate параметр Start Closed по умолчанию True, то это означает, что изначально ворота закрыты, и сигнал Enter далее не проходит. Чтобы открыть ворота, подадим сигнал Pressed от события F в параметр Open от ноды Gate. И, соответственно, для закрытия ворот — Released в Close. Таким образом, при нажатии на F мы будем открывать ворота и пропускать сигнал от Event Tick далее, а при отжатии F ворота будут закрываться, блокируя сигнал тика. Осталось только поток Exit ноды Gate переправить во входящий поток Set Actor Location.
  4. Если Вы запустите игру сейчас, то увидите, что наш Pawn передвигается с небольшой скоростью, но при этом не учитывает пересечения с другими объектами, просто перемещаясь сквозь них. Чтобы это исправить, нужно всего лишь у функции Set Actor Location параметр Sweep изменить True. Таким образом, данная функция будет не просто перемещать объект на новые координаты, но и учитывать его столкновение с другими объектами игрового мира. Причём все эти столкновения можно обрабатывать, так как на выходе функции имеется выходной параметр Sweep Hit Result, который раскрывается на большую структуру нодой Break Hit Result, содержащую подробную информацию о столкновении.

Скриншот

Вводим в код ноду Gate
Вводим в код ноду Gate

Код на BlueprintUE

Если код отображается без связей, нажмите сверху кода на кнопку «Graph» Если код отображается без связей, нажмите сверху кода на кнопку "Graph"


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

Скорость базового передвижения Pawn

В нашей практике осталось настроить последний момент — регулируемая скорость передвижения Pawn. В текущей версии у нас движение создаётся добавочными пунктами из вектора с направлением вперед, которые мы добавляем к текущим координатам на каждом кадре игры. Чтобы увеличить скорость, нужно увеличить эти добавочные пункты. Также, для того чтобы скорость движения объекта была на всех ПК одинаковая, при её расчёте необходимо учесть параметр Delta Seconds из события Event Tick. Что это такое и почему Delta Seconds нужно учитывать, Вы можете прочитать в отдельной статье: Unreal Engine Delta Seconds.

  1. В BP_Pawn введём переменную скорости Speed типа float. Зададим ей начальное значение в 200.
  2. Для того чтобы скорость была одинаковая на всех ПК, умножим Speed на Delta Seconds из события Event Tick.
  3. Получившийся результат умножения еще раз помножим на значения вектора вперед и далее этот результат сложим с текущими координатами Актора. Всё это и будет являться новыми координатами для Set Actor Location, учитывающими значения скорости и Delta Seconds.

Скриншот

Практика в Unreal Engine: создание базового передвижения объекта класса Pawn. Функция Set Actor Location
Практика в Unreal Engine: создание базового передвижения объекта класса Pawn. Функция Set Actor Location

Код на BlueprintUE

Если код отображается без связей, нажмите сверху кода на кнопку «Graph» Если код отображается без связей, нажмите сверху кода на кнопку "Graph"


Функции класса Pawn

Функции, возвращающие значение

  • Get Base Aim Rotation — возвращает базовое вращение для «взгляда» объекта Pawn (Aimприцеливания). Если Pawn управляется ИИ, то в качестве взгляда имеется в виду поворот самого Pawn, если управляется игроком, то под взглядом понимается поворот камеры игрока.
  • Get Control Rotation — возвращает вращение контроллера, который управляет текущим Pawn.
  • Get Controller — возвращает контроллер, который управляет текущей Пешкой.
  • Get Local Viewing Player Controller — возвращает локальный Player Controller, который в данный момент управляет камерой (то есть через которую в данный момент игрок смотрит на мир).
  • Get Movement Component — возвращает компонент перемещения текущего Pawn (при его наличии).
  • Get Nav Agent Location — возвращает текущее местоположение Pawn (обычно AI-контролируемого) на NavMesh.
  • Is Controlled — возвращает значение True, если Pawn находится под управлением контроллера.
  • Is Locally Controlled — возвращает значение True, если Pawn находится под управлением локального (не сетевого) контроллера.
  • Is Locally Viewed — возвращает значение True, если Pawn является средством «взгляда» локального (не сетевого) контроллера.
  • Is Pawn Controlled — возвращает значение True, если Pawn управляется контроллером (для сетевых клиентов значение будет False).
  • Is Player Controlled — возвращает значение True, если Pawn управляется Player Controller (для сетевых клиентов значение также будет True).

Функции работы с контроллером

  • Spawn Default Controller — создаёт и назначает контроллер для текущего Pawn.
  • Detach from Controller Pending Destroy — отсоединяет контроллер от Pawn и очищает ссылки, чтобы безопасно подготовить Пешку к уничтожению.

Input функции

  • Get Last Movement Input Vector — возвращает нормализованный вектор, представляющий последнее направление, в котором пыталась двигаться Пешка.
  • Get Override Input Component Class — возвращает переопределяемый Input Component Class, используемый для обработки ввода.
  • Get Pending Movement Input Vector — возвращает вектор необработанного ввода перемещения, который еще не был обработан компонентом перемещения Pawn Movement Component. Позволяет «заглянуть» в то, какой вектор перемещения ожидает обработки для Пешки.
  • Is Move Input Ignored — проверяет, игнорируется ли в данный момент ввод перемещения для Пешки. Если функция возвращает True, это означает, что ввод перемещения Пешкой обрабатываться не будет.
  • Add Controller Pitch Input — добавляет ввод для изменения угла наклона (Pitch) локального Player Controller, владеющего текущим Pawn. Данное значение умножается на значение Input Pitch Scale этого контроллера.
  • Add Controller Roll Input — добавляет ввод для изменения угла крена (Roll) локального Player Controller, владеющего текущим Pawn. Данное значение умножается на значение Input Roll Scale этого контроллера.
  • Add Controller Yaw Input — добавляет ввод для изменения угла поворота (Yaw) локального Player Controller, владеющего текущим Pawn. Данное значение умножается на значение Input Yaw Scale этого контроллера.
  • Add Movement Input — добавляет вектор движения к компоненту перемещения (Character Movement Component или Pawn Movement Component), определяя направление и величину силы для перемещения Pawn.
  • Consume Movement Input Vector — возвращает и обнуляет вектор намерения движения, накопленный в компоненте перемещения. Используется для получения вектора намерения движения, который был собран посредством вызовов Add Movement Input, и очистки этого вектора, чтобы избежать повторной обработки ввода в последующих кадрах.

Совет. Вскоре выйдет моя бесплатная книга по Blueprints для Unreal Engine в PDF формате. Как она выйдет, рекомендую её скачать, чтобы Вы детально изучили блюпринты Анрил Энджин.


наш Телеграм канал

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

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