Введение

Делегирование в Unreal Engine — это объект, который содержит 1 или несколько ссылок на методы, где тип метода может быть неизвестен до времени выполнения. Методы можно активировать путем широковещательной рассылки делегата, что обеспечивает проектирование и разработку на основе событий. По своей сути делегирование Unreal Engine использует указатели функций C++. Однако он значительно повышает безопасность, контекстуальную ясность, гибкость и совместное использование данных, делая указатели на функции более простыми и удобными.

Резюме указателей функций

Указатель функции можно определить как обычный указатель, который ссылается на функцию. Тип этого указателя определяется типом возвращаемого значения функции и списком аргументов. В отличие от обычных функций, указатели на функции могут использоваться в качестве параметров или типов возвращаемых значений для других функций. Компилятор может определить тип указателя на функцию, используя ключевое слово «auto» / «decltype» вместе с функцией, на которую он намеревается указывать. Однако возвращаемый тип обычно является обычным типом и должен быть преобразован в тип указателя.

Шаги по реализации делегата

  1. Во-первых, объявите делегат, указав тип возвращаемого значения (любой динамический делегат должен возвращать значение «void») и параметры. Делегат обычно объявляется в заголовочном файле.
  2. Создайте объект делегата внутри класса (обычно в том же файле, где он объявлен), где он будет использоваться.
  3. Подключить/подписать/привязать функцию к объекту делегата. Этот процесс может быть выполнен либо в C++, либо в Blueprint. В то время как одноадресный делегат может иметь только одну функцию в качестве подписчика, многоадресный делегат позволяет подписаться на одно и то же событие, в частности, на объект делегата, несколько функций.
  4. После того, как функции подписались на делегата, делегат может быть вызван с помощью функции «broadcast» в объекте делегата для активации подписанных функций, просто не забудьте предоставить все необходимые аргументы.
OnUseItem.Broadcast();

Практический пример: Стартап-проекты FPS

Проект шаблона FPS содержит 2 определяемых пользователем объекта-делегата, которые позволяют игроку подходить к актеру с винтовкой, размещенному на уровне, автоматически поднимать винтовку и стрелять снарядами, когда используется привязка основной клавиши действия. Это достигается с помощью 3 классов C++ и схемы.

Делегирование в Pickup_Component

«Pickup_Component» — это компонент сцены винтовочного оружия. Он определяет «физическую» форму и взаимодействие актера. «pickup_component» использует два делегата: один предопределенный «On_Actor_Overlap_Begin» и один пользовательский. Объявляется определяемый пользователем делегат, и в заголовочном файле создается соответствующий объект; объект делегата является общедоступным и разрешает доступ из другого объекта.

// Declaration
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPickUp, AMyProject2Character*, PickUpCharacter);
// Initialization
FOnPickUp OnPickUp;
  • Здесь мы объявляем динамический многоадресный делегат с 1 параметром, указателем на объект персонажа игрока.

«Pickup_Component» — это класс, производный от «U_Sphere_Component», содержащий специальный делегат, срабатывающий, когда актер перекрывает компонент сферы.

OnComponentBeginOverlap.AddDynamic(this, &UTP_PickUpComponent::OnSphereBeginOverlap); // Register the onSphereBeginOverlap function to OnComponentBeginOverlap (inherited)

В классе «Pickup_Component» мы определяем функцию-член, подписывающуюся на этот специальный делегат. Эта функция-член отвечает за определение того, является ли перекрывающийся персонаж игроком, и, если это так, передает определяемый пользователем делегат и отменяет регистрацию текущей функции-члена из делегата On-Actor-Overlap.

// Take all 6 parameters from the inherited function
void UTP_PickUpComponent::OnSphereBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
  // Checking if it is a First Person's Character overlapping
  AMyProject2Character* Character = Cast<AMyProject2Character>(OtherActor);
  if(Character != nullptr)
  {
     // Notify that the actor is being picked up
     OnPickUp.Broadcast(Character);
     // Unregister from the Overlap Event so it is no longer triggered
     OnComponentBeginOverlap.RemoveAll(this);
  }
}

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

Класс «Weapon_Component»

Унаследованный от класса «Actor_Component», класс «Weapon_Component» представляет собой часть логики/игрового процесса оружия. Это включает в себя функции для прикрепления оружия к персонажу, сохранения и активации вспомогательных анимационных и звуковых данных, определения и создания класса снаряда в соответствующем месте и, в конечном итоге, запуска снаряда.

Функция «Прикрепить оружие» — это функция, подписанная на делегат «Pickup_Component», которая активируется, когда объект «Pickup_Component» перекрывается с другим актором. Функция сначала проверяет параметр, затем прикрепляет корневой компонент к указанному персонажу игрока и, наконец, подписывает свою функцию Firing на объект делегата персонажа игрока.

// Bind the fire function to the character "onUseItem" delegate
     Character->OnUseItem.AddDynamic(this, &UTP_WeaponComponent::Fire);

Компонент «Оружие_Компонент» также отвечает за уборку после себя. В конце игры объект «Weapon_Component» удалит подписку из объекта-делегата персонажа.

Character->OnUseItem.RemoveDynamic(this, &UTP_WeaponComponent::Fire);

Актер проекта «BP_Rifle»

«BP_Rifle» создает слабую связь между «Weapon_Component» и «Pickup_Component» через композицию. Функция чертежа подписывается на функцию «Прикрепить оружие» из «Weapon_Component» к объекту делегата в «Pickup_Component». Эквивалентный код C++ будет следующим

OnPickUp.AddDynamic(this, Weapon_Component::AttachWeapon(Player_Character));

Делегирование в классе персонажа игрока

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

DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnUseItem); // Delegate declaration
UPROPERTY(BlueprintAssignable, Category = "Interaction")
  FOnUseItem OnUseItem;
  • У персонажа игрока есть динамический многоадресный делегат, который не принимает никаких аргументов.

Этот объект-делегат заключен в функцию и привязан к действию ввода игрока. Когда срабатывает действие ввода, вызывается функция, которая будет транслировать объект делегата

void AMyProject2Character::OnPrimaryAction()
{
    // Trigger the OnItemUsed Event
    OnUseItem.Broadcast();
}

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

Заключение

Разбивая шаги, концепция делегирования Unreal Engine становится менее сложной и более доступной. Практический пример стартового проекта FPS является полезной иллюстрацией применения этой мощной функции в разработке игр. Освоив эти концепции, вы сможете разблокировать большую гибкость и модульность в своих проектах Unreal Engine. В следующей статье я более подробно расскажу о различных типах делегирования, доступных в движке, доступных возвращаемых значениях и некоторых более продвинутых функциях делегирования в Unreal.

Ссылка

  1. https://benui.ca/unreal/delegates-intro/