UE4 | Инвентарь для Multiplayer #1 | Хранилище данных на DataAsset
UE4 | Инвентарь для Multiplayer #2 | Подключение Blueprint к C++
UE4 | Инвентарь для Multiplayer #3 | Структура взаимодействия
UE4 | Инвентарь для Multiplayer #4 | Создание и подключение конейнера
UE4 | Инвентарь для Multiplayer #5 | Передача информации между Сервером и Клиентом
В предыдущей статье я рассказывал как создать 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, а когда нет.
Все оказалось до боли просто.
- Если не ставить class, то нужно выполнить включение #include «Data/DreampaxItemsDataAsset.h», содержащее объявление этого класса.
- Если ставить class, то #include «Data/DreampaxItemsDataAsset.h» можно сделать уже в .cpp.
- И еще одна опция предыдущего пункта, если нужно объявить сразу много переменных данного класса. Непосредственно после всех #include предварительно объявить наш класс class UDreampaxItemsDataAsset;, а после объявлять переменные уже без приставки class.
Какой из этих способов правильный — не берусь сказать. Если кто-то объяснит, буду благодарен.
Делаем переменную в C++ классе ADreampaxGameMode, так как он виден только серверу, а все что связано со спауном объектов должно идти только через сервер. Данный класс является родителем для BP_DreampaxGameMode, где мы и подключим наш BP_DreampaxItemsDataAsset.

Теперь вся мощь C++ может быть использована для работы с данными нашей базы данных.
В следующей статье (наконец-то!) мы поговорим о создании инвентаря и узнаем почему нам никак не обойтись без уже созданного DataAsset.
Если есть вопросы или пожелания раскрыть какой-либо аспект подробнее, пожалуйста пишите в комментариях.
ссылка на оригинал статьи https://habr.com/post/418951/
Добавить комментарий