UE4 | Инвентарь для Multiplayer #2 | Подключение Blueprint к C++


GameModeВ предыдущей статье я рассказывал как создать DataAsset, и почему он такой хороший и удобный. Здесь же мы рассмотрим то, как получить доступ к DataAsset, точнее к назначенным в нем данным, из Blueprint и C++.

Попутно мы ответим на вопрос получения доступа к любому Blueprint из C++.


Со взаимодействием Blueprints все достаточно прозрачно.
Ввиду того, что мы закрыли прямой доступ к нашей базе данных, мы не можем просто так обратится к ней из Blueprint. Обратите внимание на protected: в коде ниже.

protected:      /* This is the main Database for all Items. It contains constant common variables */     UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "ItemsDatabase")     TMap<FGameplayTag, FItemsDatabase> ItemsDataBase;

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

/* Used in the widget */     UFUNCTION(BlueprintCallable, Category = "ItemDatabase")     FORCEINLINE UTexture2D * GetItemIconTexture(const FGameplayTag & ItemNameTag) const;

BlueprintCallable как раз и означает, что данная функция может быть использована в Blueprint. Если вы читали предыдущую статью, то наверно заметили, что другие функции вызова такого атрибута не имеют. Так сделано только потому, что вызываемые ими данные на данный момент в Blueprint не понадобились. Если кому-то что-то знать не нужно — не спешим об этом сообщать.

Следующий шаг, это создание в любом Blueprint переменной типа созданной нами базы данных (в моем случае это BP_DreampaxItemsDataAsset).

После этого легко непринужденно извлекаем назначенную текстуру.


Теперь рассмотрим как получить доступ к информации в C++.
Мы не можем просто обратиться к классу DreampaxItemsDataAsset, поскольку он не содержит никакой информации. Нам нужно заполучить доступ к BP_DreampaxItemsDataAsset.

Существует два основных метода как достучаться до Blueprint.
Сначала рассмотрим неудобный способ подключения с использованием костыля ConstructorHelpers. В данном случае это доступ к текстуре.

ASHUD::ASHUD(const class FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) {     /* You can use the FObjectFinder in C++ to reference content directly in code. Although it's advisable to avoid this and instead assign content through Blueprint child classes. */     static ConstructorHelpers::FObjectFinder<UTexture2D> HUDCenterDotObj(TEXT("/Game/UI/HUD/T_CenterDot_M.T_CenterDot_M"));     CenterDotIcon = UCanvas::MakeIcon(HUDCenterDotObj.Object); }

Пример взят из замечательного проекта EpicSurvivalGameSeries, идеально подходящего для изучения Multiplayer на C++. Автор поставил цель показать как можно больше методов и приемов программирования игры на C++.

Почему данный способ неудобен? Та же беда как и с DataTable — при изменении названия Blueprint или месторасположения, файл не будет найден.

Наиболее предпочтительным можно считать метод в котором мы объявляем переменную в заголовочном файле, для последующего назначения ее уже в наследованном Blueprint. Для примера выше это могло бы выглядеть так:

    UPROPERTY(EditDefaultsOnly, Category = "AimPointer")     FCanvasIcon CenterDotIcon;

Намного проще, верно?

Назначение текстуры


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

UCLASS() class ADreampaxGameMode : public AGameMode {     GENERATED_BODY()   public:      ADreampaxGameMode(const FObjectInitializer & ObjectInitializer);   /////////////////////////////////////////////////////////////////////////////  //Data Bases  /////////////////////////////////////////////////////////////////////////////  public:     /* Connect data base in BP for items*/     UPROPERTY(EditDefaultsOnly, Category = "Database")     class UDreampaxItemsDataAsset * DreampaxItemsDataAsset;      FORCEINLINE UDreampaxItemsDataAsset * GetDreampaxItemsDataAsset() const;

Небольшое отступление для не профи на тему объявления переменных

Как человек почти незнакомый с C++, я сломал немало копий, пытаясь понять как правильно объявлять кастомные переменные.

Если стоит цель объявить переменную созданного нами класса, как, например

UDreampaxItemsDataAsset * DreampaxItemsDataAsset; // или class UDreampaxItemsDataAsset * DreampaxItemsDataAsset;

лично мне какое-то время было непонятно когда нужно применять class, а когда нет.

Все оказалось до боли просто.

  1. Если не ставить class, то нужно выполнить включение #include «Data/DreampaxItemsDataAsset.h», содержащее объявление этого класса.
  2. Если ставить class, то #include «Data/DreampaxItemsDataAsset.h» можно сделать уже в .cpp.
  3. И еще одна опция предыдущего пункта, если нужно объявить сразу много переменных данного класса. Непосредственно после всех #include предварительно объявить наш класс class UDreampaxItemsDataAsset;, а после объявлять переменные уже без приставки class.

Какой из этих способов правильный — не берусь сказать. Если кто-то объяснит, буду благодарен.

Делаем переменную в C++ классе ADreampaxGameMode, так как он виден только серверу, а все что связано со спауном объектов должно идти только через сервер. Данный класс является родителем для BP_DreampaxGameMode, где мы и подключим наш BP_DreampaxItemsDataAsset.

Подключение BP_DreampaxItemsDataAsset

Подключение BP_DreampaxItemsDataAsset

Теперь вся мощь C++ может быть использована для работы с данными нашей базы данных.

В следующей статье (наконец-то!) мы поговорим о создании инвентаря и узнаем почему нам никак не обойтись без уже созданного DataAsset.

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

FavoriteLoadingДобавить в избранное
Posted in Без рубрики

Добавить комментарий