UE4 Inventário para Multiplayer # 1 | Data Warehouse no DataAsset




Conjunto de dados Neste artigo, tentarei revelar o significado e a metodologia da criação do DataAsset , como um repositório para vários tipos de dados e, no nosso caso, é uma biblioteca para atores e seus parâmetros.




Uma pequena introdução para pular

Para tomar a decisão de criar um jogo há cerca de 2 anos, me ajudou a encontrar informações sobre o Unreal Engine 4 acidentalmente e a ler como é legal e simples. De fato, é muito difícil para uma pessoa que não sabe escrever código (uma linguagem de programação não importa nesse contexto) criar algo que é mais complicado do que uma pequena modificação de um conjunto padrão de peças do mecanismo. Portanto, o desejo inicial de fazer um super mega-jogo, com o crescimento do conhecimento sobre a realidade desse projeto, tornou-se gradualmente um hobby. Aumentar todas as camadas do desenvolvimento de jogos, desde modelagem e animação em 3D e escrever código, para uma pessoa parece um empreendimento pouco viável. No entanto, este é um bom treino para o cérebro.


Por que você decidiu escrever alguma coisa? Provavelmente por causa disso. que os manuais apresentados fornecem conhecimentos muito superficiais (e existem muitos), ou mesmo para um profissional, e contêm apenas instruções gerais.


É quase sempre melhor começar do começo. Não posso dizer que sempre faço isso, mas tentarei afirmar da maneira mais consistente possível.


Obviamente, é melhor começar com a estrutura, mas, infelizmente, com uma caixa fechada com ferramentas, é muito difícil entender o que exatamente pode ser construído com a ajuda deles. Então, vamos abrir esta caixa e ver o que está dentro.




A primeira pergunta a responder. Por que DataAsset ?


  1. Muitas vezes, em artigos e tutoriais, você pode ver o uso do DataTable . Por que isso é ruim? Se você armazenar um endereço para um Blueprint específico, ao renomear ou movê-lo para outra pasta, você será forçado a alterar esse endereço manualmente. Concordo - desconfortável? Com o DataAsset , isso não acontecerá. Todas as comunicações serão atualizadas automaticamente. Se você estiver absolutamente confiante na estrutura do seu projeto nos próximos anos, é claro que poderá usar as tabelas.
  2. A segunda vantagem inegável é a capacidade de armazenar tipos de dados complexos, como estruturas ( Struct ).

Agora um pouco sobre as desvantagens relativas. De fato, vejo apenas um. Essa é a necessidade de escrever código C ++ .


Se você já entende que, sem trabalhar com o código, não fará nada épico , isso não é uma desvantagem, mas um recurso.


Note-se que há uma solução alternativa - usar o Actor como um repositório. Mas esse aplicativo parece a última desculpa para não querer aprender C ++ e tem o potencial de acabar em um beco sem saída no futuro.
Se você está convencido de que tudo o que você precisa para o seu projeto pode ser feito no Blueprint , use as tabelas.




Agora que você já acredita que o DataAsset é bom, considere como criá-lo para o seu projeto.


Para aqueles que ainda estão completamente "fora do tanque"
Há uma descrição muito detalhada das etapas e com fotos no fórum em russo dedicado ao UE4 . Basta pesquisar no Google "UE4 DataAsset" para . Ele dominou o básico deste manual cerca de um ano atrás.

Primeiro, crie uma classe C ++ , como Child do UDataAsset .
(Todo o código abaixo foi retirado do meu projeto por nascer. Apenas renomeie os nomes como desejar.)


/// Copyright 2018 Dreampax Games, Inc. All Rights Reserved. #pragma once /* Includes from Engine */ #include "Engine/DataAsset.h" #include "Engine/Texture2D.h" #include "GameplayTagContainer.h" /* Includes from Dreampax */ //no includes #include "DreampaxItemsDataAsset.generated.h" UCLASS(BlueprintType) class DREAMPAX_API UDreampaxItemsDataAsset : public UDataAsset { GENERATED_BODY() } 

Agora, com base nesta classe, você pode criar o Blueprint com segurança, mas é muito cedo para fazê-lo ... até agora, é apenas um manequim. No entanto, observe que as inclusões para texturas e nomes já foram feitas.




A partir deste momento, você começa a criar a estrutura do seu repositório. Ele será refeito muitas vezes, por isso recomendo não encher imediatamente seu armazenamento. Três a cinco elementos, no nosso caso, itens de inventário, são suficientes para testes. Às vezes, após a compilação, seu Blueprint pode ficar vazio, o que é extremamente desagradável se você já tiver preenchido uma dúzia ou duas posições.


Você pode criar uma estrutura diretamente no arquivo de cabeçalho, porque nesse caso, é improvável que seja aplicado em outro lugar. Normalmente, prefiro fazê-lo como um arquivo de cabeçalho separado “SrtuctName.h” e conectá-lo sempre que necessário, conforme necessário.


No meu caso, parece que isso
 USTRUCT(BlueprintType) struct FItemsDatabase { GENERATED_USTRUCT_BODY() /* Storage for any float constant data */ UPROPERTY(EditDefaultsOnly, Category = "ItemsDatabase") TMap<FGameplayTag, float> ItemData; /* Gameplay tag container to store the properties */ UPROPERTY(EditDefaultsOnly, Category = "ItemsDatabase") FGameplayTagContainer ItemPropertyTags; /* Texture for showing in the inventory */ UPROPERTY(EditDefaultsOnly, Category = "ItemsDatabase") UTexture2D* IconTexture; /* The class put on the Mesh on the character */ UPROPERTY(EditDefaultsOnly, Category = "ItemsDatabase") TSubclassOf<class ADreampaxOutfitActor> ItemOutfitClass; /* The class to spawn the Mesh in the level then it is dropped */ UPROPERTY(EditDefaultsOnly, Category = "ItemsDatabase") TSubclassOf<class ADreampaxPickupActor> ItemPickupClass; //TODO internal call functions }; 

Seja contas com o TMap . Não replicado! Nesse caso, não importa.


Observe que eu não uso o FName . De acordo com as tendências modernas, o uso do FGameplayTag é considerado mais correto, porque reduz significativamente o risco de erro e possui várias vantagens que serão úteis mais tarde.


GameplayTag no editor
Gameplaytag

DataTable para GameplayTag exportado do Excel
DataTable

Também é uma boa prática definir funções para chamar variáveis ​​na estrutura, como GetSomething () . Aparentemente, ainda preciso trabalhar na minha educação, já que está neste banco de dados, ainda não fiz essa ligação.


Veja como isso pode ser feito usando outro banco de dados como exemplo.
 USTRUCT(BlueprintType) struct FBlocksDatabase { GENERATED_USTRUCT_BODY() /* The class put on the Mesh for the building block */ UPROPERTY(EditDefaultsOnly, Category = "BlocksDatabase") TSubclassOf<class ADreampaxBuildingBlock> BuildingBlockClass; UPROPERTY(EditDefaultsOnly, Category = "BlocksDatabase") FVector DefaultSize; UPROPERTY(EditDefaultsOnly, Category = "BlocksDatabase") FVector SizeLimits; UPROPERTY(EditDefaultsOnly, Category = "BlocksDatabase") TArray<class UMaterialInterface *> BlockMaterials; FORCEINLINE TSubclassOf<class ADreampaxBuildingBlock> * GetBuildingBlockClass() { return &BuildingBlockClass; } FORCEINLINE FVector GetDefaultSize() { return DefaultSize; } FORCEINLINE FVector GetSizeLimits() { return SizeLimits; } FORCEINLINE TArray<class UMaterialInterface *> GetBlockMaterials() { return BlockMaterials; } }; 

E o ponto mais importante é a declaração do banco de dados:


 UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "ItemsDatabase") TMap<FGameplayTag, FItemsDatabase> ItemsDataBase; 

Agora você já pode criar nosso Blueprint e preenchê-lo.


Exemplo de preenchimento DataAsset
Exemplo de preenchimento DataAsset

Mas antes disso, escreveremos mais algumas funções de chamada para poder receber dados do banco de dados.


DreampaxItemsDataAsset.h
 /// Copyright 2018 Dreampax Games, Inc. All Rights Reserved. #pragma once /* Includes from Engine */ #include "Engine/DataAsset.h" #include "Engine/Texture2D.h" #include "GameplayTagContainer.h" /* Includes from Dreampax */ //no includes #include "DreampaxItemsDataAsset.generated.h" USTRUCT(BlueprintType) struct FItemsDatabase { GENERATED_USTRUCT_BODY() /* Storage for any float constant data */ UPROPERTY(EditDefaultsOnly, Category = "ItemsDatabase") TMap<FGameplayTag, float> ItemData; /* Gameplay tag container to store the properties */ UPROPERTY(EditDefaultsOnly, Category = "ItemsDatabase") FGameplayTagContainer ItemPropertyTags; /* Texture for showing in the inventory */ UPROPERTY(EditDefaultsOnly, Category = "ItemsDatabase") UTexture2D* IconTexture; /* The class put on the Mesh on the character */ UPROPERTY(EditDefaultsOnly, Category = "ItemsDatabase") TSubclassOf<class ADreampaxOutfitActor> ItemOutfitClass; /* The class to spawn the Mesh in the level then it is dropped */ UPROPERTY(EditDefaultsOnly, Category = "ItemsDatabase") TSubclassOf<class ADreampaxPickupActor> ItemPickupClass; //TODO internal call functions }; UCLASS(BlueprintType) class DREAMPAX_API UDreampaxItemsDataAsset : public UDataAsset { GENERATED_BODY() protected: /* This GameplayTag is used to find a Max size of the stack for the Item. This tag can be missed in the ItemData */ UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "ItemsDatabase") FGameplayTag DefaultGameplayTagForMaxSizeOfStack; /* This is the main Database for all Items. It contains constant common variables */ UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "ItemsDatabase") TMap<FGameplayTag, FItemsDatabase> ItemsDataBase; public: FORCEINLINE TMap<FGameplayTag, float> * GetItemData(const FGameplayTag &ItemNameTag); FORCEINLINE FGameplayTagContainer * GetItemPropertyTags(const FGameplayTag &ItemNameTag); /* Used in the widget */ UFUNCTION(BlueprintCallable, Category = "ItemDatabase") FORCEINLINE UTexture2D * GetItemIconTexture(const FGameplayTag & ItemNameTag) const; FORCEINLINE TSubclassOf<class ADreampaxOutfitActor> * GetItemOutfitClass(const FGameplayTag & ItemNameTag); FORCEINLINE TSubclassOf<class ADreampaxPickupActor> * GetItemPickupClass(const FGameplayTag & ItemNameTag); int GetItemMaxStackSize(const FGameplayTag & ItemNameTag); FORCEINLINE bool ItemIsFound(const FGameplayTag & ItemNameTag) const; }; 

DreampaxItemsDataAsset.pp
 /// Copyright 2018 Dreampax Games, Inc. All Rights Reserved. #include "DreampaxItemsDataAsset.h" /* Includes from Engine */ // no includes /* Includes from Dreampax */ // no includes TMap<FGameplayTag, float>* UDreampaxItemsDataAsset::GetItemData(const FGameplayTag & ItemNameTag) { return & ItemsDataBase.Find(ItemNameTag)->ItemData; } FGameplayTagContainer * UDreampaxItemsDataAsset::GetItemPropertyTags(const FGameplayTag & ItemNameTag) { return & ItemsDataBase.Find(ItemNameTag)->ItemPropertyTags; } UTexture2D* UDreampaxItemsDataAsset::GetItemIconTexture(const FGameplayTag &ItemNameTag) const { if (ItemNameTag.IsValid()) { return ItemsDataBase.Find(ItemNameTag)->IconTexture; } return nullptr; } TSubclassOf<class ADreampaxOutfitActor>* UDreampaxItemsDataAsset::GetItemOutfitClass(const FGameplayTag &ItemNameTag) { return & ItemsDataBase.Find(ItemNameTag)->ItemOutfitClass; } TSubclassOf<class ADreampaxPickupActor>* UDreampaxItemsDataAsset::GetItemPickupClass(const FGameplayTag &ItemNameTag) { return & ItemsDataBase.Find(ItemNameTag)->ItemPickupClass; } int UDreampaxItemsDataAsset::GetItemMaxStackSize(const FGameplayTag & ItemNameTag) { // if DefaultGameplayTagForMaxSizeOfStack is missed return 1 for all items if (!DefaultGameplayTagForMaxSizeOfStack.IsValid()) { return 1; } int MaxStackSize = floor(GetItemData(ItemNameTag)->FindRef(DefaultGameplayTagForMaxSizeOfStack)); if (MaxStackSize > 0) { return MaxStackSize; } // if Tag for MaxStackSize is "0" return 1 return 1; } bool UDreampaxItemsDataAsset::ItemIsFound(const FGameplayTag & ItemNameTag) const { if (ItemsDataBase.Find(ItemNameTag)) { return true; } return false; } 

Do multiplayer ainda não há nada. Mas este é o primeiro passo que foi dado na direção certa.


No próximo artigo , falarei sobre os métodos de conexão do DataAsset (sim, e qualquer Blueprint ) para leitura de dados em C ++ e mostrarei qual é o mais correto.


Se você tiver dúvidas ou sugestões para divulgar qualquer aspecto com mais detalhes, escreva nos comentários.

Source: https://habr.com/ru/post/pt418913/


All Articles