UE4 | Inventar für Multiplayer # 1 | Data Warehouse auf DataAsset




Datenasset In diesem Artikel werde ich versuchen, die Bedeutung und Methodik der Erstellung von DataAsset als Repository für verschiedene Arten von Daten aufzuzeigen. In unserem Fall handelt es sich um eine Bibliothek für Akteure und deren Parameter.




Eine kleine Einführung zum Überspringen

Als ich mich vor ungefähr 2 Jahren entschied, ein Spiel zu erstellen, half es mir, dass ich versehentlich auf Informationen über Unreal Engine 4 stieß und las, wie cool und einfach es ist. Tatsächlich ist es für eine Person, die nicht weiß, wie man Code schreibt (eine Programmiersprache spielt in diesem Zusammenhang keine Rolle) , sehr schwierig, etwas zu erstellen, das komplizierter ist als eine kleine Modifikation eines Standardsatzes von Werkstücken aus der Engine. Daher entwickelte sich der anfängliche Wunsch, ein Super-Mega-Spiel mit dem wachsenden Wissen über die Realität dieses Projekts zu entwickeln, allmählich zu einem Hobby. Alle Ebenen der Spieleentwicklung, von der 3D-Modellierung und -Animation bis zum Schreiben von Code, für eine Person zu erweitern, scheint ein wenig machbares Unterfangen zu sein. Dies ist jedoch ein gutes Training für das Gehirn.


Warum hast du dich entschieden etwas zu schreiben? .. Wahrscheinlich deswegen. dass die vorgestellten Handbücher entweder sehr oberflächliches Wissen vermitteln (und es gibt die meisten von ihnen) oder sogar für einen sehr professionellen und nur allgemeine Anweisungen enthalten.


Es ist fast immer besser, von vorne zu beginnen. Ich kann nicht sagen, dass ich das immer mache, aber ich werde versuchen, so konsequent wie möglich zu sagen.


Natürlich ist es am besten, mit der Struktur zu beginnen, aber leider ist es bei einer geschlossenen Box mit Werkzeugen sehr schwierig zu verstehen, was genau mit ihrer Hilfe gebaut werden kann. Öffnen wir also diese Box und sehen, was sich darin befindet.




Die erste zu beantwortende Frage. Warum DataAsset ?


  1. Sehr oft können Sie in Artikeln und Tutorials die Verwendung von DataTable sehen . Warum ist das so schlimm? Wenn Sie eine Adresse für eine bestimmte Blaupause speichern, müssen Sie diese Adresse beim Umbenennen oder Verschieben in einen anderen Ordner manuell ändern. Einverstanden - unangenehm? Mit DataAsset wird dies nicht passieren. Alle Mitteilungen werden automatisch aktualisiert. Wenn Sie in den kommenden Jahren absolut sicher sind, dass Ihr Projekt strukturiert ist, können Sie natürlich Tabellen verwenden.
  2. Der zweite unbestreitbare Vorteil ist die Fähigkeit, komplexe Datentypen wie Strukturen ( Struct ) zu speichern.

Nun ein wenig zu den relativen Nachteilen. Tatsächlich sehe ich nur einen. Dies ist die Notwendigkeit, C ++ - Code zu schreiben.


Wenn Sie bereits verstehen, dass Sie ohne die Arbeit mit dem Code nichts Episches tun werden, ist dies kein Nachteil, sondern eine Funktion.


Es sollte beachtet werden, dass es eine Problemumgehung gibt - die Verwendung von Actor als solches Repository. Eine solche Anwendung scheint jedoch die letzte Entschuldigung dafür zu sein, dass sie C ++ nicht lernen möchte, und birgt das Potenzial, in Zukunft in eine Sackgasse zu geraten.
Wenn Sie davon überzeugt sind, dass alles, was Sie für Ihr Projekt benötigen, mit Blueprint erledigt werden kann, verwenden Sie die Tabellen.




Nachdem Sie bereits geglaubt haben, dass DataAsset gut ist, überlegen Sie, wie Sie es für Ihr Projekt erstellen können.


Für diejenigen, die noch ganz "nicht im Tank" sind
Es gibt eine sehr detaillierte Beschreibung der Schritte und mit Bildern im russischsprachigen Forum, das UE4 gewidmet ist . Google einfach "UE4 DataAsset" für . Er hat die Grundlagen dieses Handbuchs vor etwa einem Jahr beherrscht.

Erstellen Sie zunächst eine C ++ - Klasse wie Child aus UDataAsset .
(Der gesamte folgende Code stammt aus meinem ungeborenen Projekt. Benennen Sie die Namen einfach um, wie Sie möchten.)


/// 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() } 

Auf der Grundlage dieser Klasse können Sie Blueprint sicher erstellen, aber es ist noch zu früh, um dies zu tun. Bisher ist es nur ein Dummy. Beachten Sie jedoch, dass bereits Einschlüsse für Texturen und Namen vorgenommen wurden.




Ab diesem Moment beginnen Sie mit der Erstellung der Struktur Ihres Repositorys. Es wird viele Male wiederholt, daher empfehle ich dringend, Ihren Speicher nicht sofort zu füllen. Drei bis fünf Elemente, in unserem Fall Inventargegenstände, reichen für Tests aus. Manchmal kann Ihr Blueprint nach der Kompilierung leer sein, was äußerst unangenehm ist, wenn Sie bereits ein Dutzend oder zwei Positionen besetzt haben.


Sie können eine Struktur direkt in der Header-Datei erstellen, weil In diesem Fall ist es unwahrscheinlich, dass es an anderer Stelle angewendet wird. Normalerweise ziehe ich es vor, es als separate Header-Datei „SrtuctName.h“ zu erstellen und bei Bedarf bei Bedarf zu verbinden.


In meinem Fall sieht es so aus
 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 }; 

Seien Sie Konten bei TMap . Nicht repliziert! In diesem Fall spielt es keine Rolle.


Bitte beachten Sie, dass ich FName nicht verwende. Nach modernen Trends wird die Verwendung von FGameplayTag als korrekter angesehen, weil Reduziert das Fehlerrisiko erheblich und bietet mehrere Vorteile, die sich später als nützlich erweisen werden.


GameplayTag im Editor
Gameplaytag

DataTable für GameplayTag aus Excel exportiert
Datentabelle

Es wird auch empfohlen, Funktionen zum Aufrufen von Variablen in der Struktur zu definieren , z. B. GetSomething () . Anscheinend muss ich noch an meiner Erziehung arbeiten, da sie sich in dieser Datenbank befindet und ich noch keinen solchen Anruf getätigt habe.


Hier erfahren Sie, wie Sie eine andere Datenbank als Beispiel verwenden können.
 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; } }; 

Und der wichtigste Punkt ist die Datenbankdeklaration:


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

Jetzt können Sie unseren Blueprint bereits erstellen und ausfüllen.


Beispiel für eine DataAsset-Füllung
Beispiel für eine DataAsset-Füllung

Zuvor werden wir jedoch einige weitere Aufruffunktionen schreiben, um Daten aus der Datenbank empfangen zu können.


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; } 

Im Mehrspielermodus gibt es noch nichts. Dies ist jedoch der erste Schritt in die richtige Richtung.


Im nächsten Artikel werde ich über die Methoden zum Verbinden von DataAsset (Ja und jedem Blueprint ) zum Lesen von Daten in C ++ sprechen und zeigen, welche am korrektesten ist.


Wenn Sie Fragen oder Anregungen haben, um einen Aspekt detaillierter offenzulegen, schreiben Sie bitte in die Kommentare.

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


All Articles