Unreal Engine - многопоточность в Blueprint

Чтобы разблокировать силу многопоточности (и скорее всего силу земли) достаточно простого советского...

Зачем

  • Разгрузить основной поток, сбрагрив часть нагрузки на другие рабочие потоки
  • Сгладить возможные фризы/просадки FPS
  • Флексить умением работы с многопоточностью в резюме/перед коллегами

Собственно код

Достаточно интегрировать код ниже в любой существующий .h файл проекта или же создать новый .h и скопировать все туда. После этого нужно заменить ThreadTest в последнем include на название файла, в который вы поместили код.

#pragma once #include "CoreMinimal.h" #include "Kismet/BlueprintAsyncActionBase.h" // нужно заменить ThreadTest на название файла, в котором находится этот код #include "ThreadTest.generated.h" DECLARE_DYNAMIC_MULTICAST_DELEGATE(FAsyncDelegate); UCLASS() class UAsyncNode : public UBlueprintAsyncActionBase { GENERATED_BODY() public: // делегат, который вызовется в рабочем потоке UPROPERTY(BlueprintAssignable) FAsyncDelegate Async; // делегат, который вызовется при окончании работы рабочего потока UPROPERTY(BlueprintAssignable) FAsyncDelegate Complete; // название функции будет использоваться в качестве названия блюпринт ноды. Можно переименовать на свое усмотрение UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly="true")) static UAsyncNode* DoAsync() { UAsyncNode* Node = NewObject<UAsyncNode>(); return Node; }; virtual void Activate() override { AsyncTask(ENamedThreads::AnyThread, [&]() { Async.Broadcast(); AsyncTask(ENamedThreads::GameThread, [&]() { Complete.Broadcast(); SetReadyToDestroy(); }); }); }; };

В общем-то и все, теперь в любом блюпринте проекта можно будет добавить DoAsync ноду с 3 пинами:

Unreal Engine - многопоточность в Blueprint
  1. Безымянный Exec - сработает в основном потоке сразу при активации ноды
  2. Async - сработает в worker потоке
  3. Complete - сработает в основном потоке после завершения исполнения Async

Ограничения

К сожалению, значительная часть кода Unreal может исполняться только в основном потоке (GameThread), например

  • Cоздание UObject'ов (а следовательно Actor'ов, Character'ов и т.д.)
  • Манипуляции с положением объектов в пространстве
  • Поиск всех акторов по классу

За это отвечают щедро рассыпанные по коду IsInGameThread() проверки при провале которых можно словить краш редактора.

Что можно/стоит делать?

  • Тяжелыевычисления
  • Блокирующие API вызовы
  • Чтение/парсинг файлов
  • Обработку списков объектов/акторов, если этот список был уже сформирован в главном потоке
Время наслаждаться плодами оптимизации
Время наслаждаться плодами оптимизации

Делайте хорошие игры. Плохие тоже делайте, а то как иначе учиться. :)

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

46
8
29 комментариев