Skip to content

lihomanovdv/PlushesTestTask

Repository files navigation

Тестовое задание про Plushes

Важно!

В проекте я использовал плагин Logic Driver Lite, который можно бесплатно скачать с FAB'а. Скачайте его перед открытием проекта.

Так же рекомендую поставить плагины ElectronicNodes и AutoSizeComments для лучшей читаемости блупринтов. Ссылка на скачивание (все три плагина): https://disk.yandex.ru/d/jgI72lWrFDePKA Разместить в ..UE5_Installation_Folder/Engine/Plugins/Marketplace/ Плагины под UE 5.5

Структура проекта (Content Browser)

/Content
	/Characters
		/AnyCharacter
			/Animations
			/Meshes
			/Materials
			/Textures
	/Core - хранятся базовые классы, структуры, энумераторы, библиотеки и др. базовые ассеты, используемые как родительские/основные где бы то ни было.
		/Blueprints
			/Subfolders if needed
		/Enums
		/Input
			/Actions
			..Mapping Contexts
	/Environments
		/Materials
		/Meshes
		/Textures
	/Gameplay - Ассеты, непосредственно касающиеся геймплея.
		/Enemies
			/Behaviours - реализацию поведения найдёте здесь
			/DataAssets - созданные датаассеты для плюшей
			/Plushes - конкретные gameplay-child'ы с указанным DataAsset'ом
		/Items
			/TamerTool
				..Any related folders
		/Player
			..Player Blueprint and etc. related to player
	/Maps
	/Materials
			

Структура проекта (C++)

/Plushes
	/Base - базовые классы игровых сущностей, класс GameMode и тд.
		/AI
		/Player
	..Unreal Gameplay framework classes (GM and etc.)
	/Gameplay - Папка с геймплейными объектами
		/PlayerPlushTool
	/Managers - папка с менеджерами/системами
		/Significance
			/SignificanceStrategies

Overview вкратце:

Для Плюшей создан базовый класс ABasePlushe (от APawn) и ABaseAIController (от AIController). Для определения других сущностей используется Perception-система, реализованная в AIController, донастроенная под нужды в ABaseAIController. ABasePlushe использует DataAsset "UPlusheBehaviorData" для инициализации параметров:

  • SkeletalMesh
  • AnimBp
  • MovementSpeed
  • SightRadius - радиус, на котором плюш видит игрока
  • LoseSightRadius, - радиус на котором плюш теряет игрока
  • SightHalfAngle - половина угла зрения
  • BehaviourStateMachine - базовое поведение плюша

Сперва начал делать плюшей Character'ами, но потом посмотрел рефы и решил, что они имеют достаточно простую форму, и перемещаются нестандартно, так что в боевом проекте бы делал из Pawn'а с кастомным Movement-компонентом. В угоду производительности, в Character'е слишком много лишнего для них.

Поведение реализовано с использованием плагина LogicDriver (Lite), который позволяет удобно использовать шаблон FSM, в некоторых (большинстве) случаях для несложных ИИ он в разы упрощает разработку и отладку по сравнению с BehaviourTree. В BehaviourTree я тоже могу, но с FSM прям сильно легче.

Для плюшей создано 3 варианта поведения: Агрессивный, Пугливый, Прирученный. При использовании инструмента (TamerTool) плюш по которому попали становится прирученным. TamerTool стреляет на 1000 единиц вперёд ровно из взгляда игрока (без сокетов, не из меша и тд, по-простому реализовал). При попадании по Плюшу заменяется его поведение на TameredBehaviour, указываемое в классе BP_TamerToolComponent.TamedStateMachine (SK_PlusheTamed).

Из интересного

Добавил для примера приёма оптимизации настроенную систему Significance менеджера, изменённую под (на мой взгляд) более удобное и простое использование и отладку. Давно хотелось такое провернуть)) В двух словах - она завёрнута в ActorComponent, который можно присоединять к любому эктору, компонент сам регистрирует эктора-овнера на старте игры, есть возможность выбора алгоритма определения важности (Significance Strategy). Обновление SignificanceManager'а происходит по таймеру из GameMode класса. Базово Significance-функционал обычно прописывают в каждом экторе, но вариант с компонентом гораздо удобнее.

Ответы на вопросы

Как бы вы расширили вашу систему для добавления “Плюшам” уникальных способностей (например, атака дальнего боя или особая реакция на гаджет) без изменения базового класса?

Исхожу из того, что имеется некоторый базовый класс на том уровне, что реализовал я. Где практически нет ничего существенного и реализованы только системные аспекты (типа Perception, Significance, возможно какой-нибудь ещё интерфейс). Т.е. Базово плюши умеют только перемещаться и реагировать на коллизии. Основные моменты поведения закладываются в StateMachine / либо BehaviourTree. Ну и допустим здоровье и сопутствующие атрибуты плюшей хранится тоже в них.

  1. Можно использовать Gameplay Ability System, особенно хорошо оно должно ложиться вкупе с Gameplay Framework. GF позволяет (и обязывает) строить игру модульно, а GAS как раз является отличным фреймворком для наращивания игровых механик, плюс имеет внутри достаточно продвинутую полунастроенную систему репликации с PredictionKey (но я не пробовал на практике систему Prediction'а, плотно знаком в теории и мимолётом трогал пару проектов с настроенной системой). (а вот саму GAS много раз пихал куда надо и не надо)) Минусы заключаются в громоздкости системы, высоком пороге входа и обязаловкой документировать каждый чих, привносимый в абилити-систему (что в целом и хорошо и плохо). Больше подходит для RPG с десятками-сотнями бафов дебафов и прочих взаимосвязанных геймплейных штук. Для небольших по механикам проектов надо тщательнее взвешивать "за" и "против".
  2. Использование Компонентной архитектуры. Добавление компонентов с ограниченным набором базовых функций, которые прописываются в базовом классе плюшей (например PrimaryAction/PrimaryAbility, HealAction и др.)
  3. Активное использование делегатов, использовать реактивные компоненты, которые прослушивают события и реагируют на них. Например, добавить HealthComponent который привязывается к получению дамага и воспроизводит нужный монтаж, калькулирует количество отнимаемого здоровья, ну и другие подобные.
  4. Своя абилити система (или около-подобная), где способности описываются DataAsset'ами, которые создают ГД. А Плюши должны уметь использовать такие компоненты.
  5. Ну и, разумеется, расширение через наследование и использование интерфейсов. Наращиваем функционал в child-классах, добавляем интерфейсы, если нужно.

Ну и в целом это обычно всё вместе работает в той или иной степени. Но именно совсем чтобы исключительно надстройкой было без малейшего вмешательства в базовый класс - вариант с GAS самый ультимативный) Остальное по отдельности - реально, но с ограничениями или оговорками)

Сетевая игра: Опишите кратко ваши шаги по репликации созданной системы для мультиплеера. Какие компоненты и свойства вы бы реплицировали?

Разделил бы данные для репликации по приоритетам: В первую очередь по важности (Reliable):

  • Transform, скорость
  • Состояние (Behaviour, статус прирученности), события, триггерящие изменения в StateMachine)
  • Обновления Perception системы Менее важные - в основном косметические данные и события (Unreliable):
  • VFX, SFX, анимации (монтажи)
  • Кто приручил плюша, если это другой игрок Без репликации
  • Анимации персонажей.

Определил бы какие данные необходимо отправлять только OwnerClient'у, а какие - всем, какие всем кроме OwnerClient'а.

К примеру, статус Tamed: Реплицируется на все клиенты Игрок использует тул на Его-Локально-Не-Прирученном Плюше (отправляет запрос на сервер для активации такой абилки) Сервер проверяет у себя возможность этого, если ок, то меняет статус на Tamed, синхронизирует по всем клиентам (NetMulticast). Если нет, то значит другой игрок успел Приручить плюша до нас и мы не приручаем Плюша выстрелив в него из TamerTool и мы это увидим. При этом если мы его приручили, то сам статус прирученности реплицируем на всех, а вот кто именно приручил - реплицируем только в игрока, который приручил. Остальным в целом достаточно знать только то, что этого плюша они уже не смогут приручить, если по геймдизайну нет важности отображать кто именно владелец. P.S. В данной реализации как такового статуса Tamed - нет, есть просто другое поведение, которое соответствует идее статуса Tamed.

Оптимизация: Если на уровне необходимо разместить 100 “Плюшей” одновременно, какие потенциальные проблемы с производительностью вы видите и как бы вы их решали?

Проблемы: Высокая сетевая нагрузка, высокая вычислительная нагрузка, нагрузка на GPU.

Как решать:

  1. Significance - система. В данной реализации я как раз добавил пример без хитрых вычислений и сетевого функционала. Я бы добавил расчёт Significance'а таким образом, чтобы в зависимости от важности те или иные плюши просчитывались и реплицировались с разной частотой. Функция получения Significance-значений использовала бы положения ViewPoint'ов всех действующих игроков относительно плюша. И, соответственно, в зависимости от важности меняется TickRate, NetUpdateFrequency и часто вызываемые функции. 1.1) Разделение на кластеры. Для дальних относительно игроков/игрока зон не обязательно рассчитывать Significance для каждого плюша. Можно разбивать на кластеры и все Плюши в кластере будут получать Significance обновление, рассчитанное сразу для всех один раз. 1.1.1) Разбивать на кластеры в геометрической прогрессии 1.2) Эта система позволяет уменьшить и сетевую, и вычислительную нагрузку.
  2. Использовать менеджер для поиска пути и EQS. Очень хотелось добавить EQS систему для поиска мест, куда плюши могут спрятаться. Вот для большого количества плюшей это будет очень узким горлышком и я бы создал систему, которая с некоторой периодичностью определяет, обновляет зоны для укрытия, а плюши в порядке живой очереди обращаются к ней при надобности.
  3. Использоваине LOD'ов для поведения: создать несколько вариантов поведения, которые бы можно было переключать в зависимости от важности.
  4. Использование LOD'ов и Culling для мешей и для анимаций. Менее важные плюши могут вовсе не проигрывать анимации, могут проигрывать их с урезанной частотой кадров или вовсе быть скрытыми.
  5. SkeletalMesh Instancing: добавить две функции ConvertTo_Instance/Actor и систему управления инстансами.
  6. Использование Pool'а для плюшей на случай если они будут постоянно рожаться/убиваться.
  7. Использование асинхронных функций там где это возможно (как пример - кастомный расчёт траекторий или поиск подходящих target-точек).

About

An UE5 job test task where you can tame aggressive and fearful entities

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published