UE4 | Inventory for Multiplayer # 1 | Data Store on DataAsset
UE4 | Inventory for Multiplayer # 2 | Connect Blueprint to C ++
UE4 | Inventory for Multiplayer # 3 | Interaction structure
UE4 | Inventory for Multiplayer # 4 | Creating and connecting a container
UE4 | Inventory for multiplayer # 5 | Information transfer between Server and Client
In this article, we will discuss creating an inventory component and connecting it to the required Actor. Since this component is simply a repository of items and the logic of their loading / unloading, there is no difference in applying it to a character or any box.
You can create a component using either Blueprint , or C through C ++ . I prefer the second method, as I am going to actively use the C ++ functionality.
First of all, we create a cell structure for storing a single item. I prefer to store it in a separate .h file in order to be free to connect as needed where needed:
/// Copyright 2018 Dreampax Games, Inc. All Rights Reserved. /* Struct for Container Stack. This file is used as #include */ #pragma once /* Includes from Engine */ #include "GameplayTagContainer.h" /* Includes from Dreampax */ #include "Data/StructItemFactors.h" #include "StructContainerStack.generated.h" /* Declaration for contaiter stack structure. BlueprintType required to use in BP */ USTRUCT(BlueprintType) struct FContainerStack { GENERATED_USTRUCT_BODY() /* Gameplay tag to store the name */ UPROPERTY(EditAnywhere) FGameplayTag ItemNameTag; UPROPERTY(EditAnywhere) int ItemAmount; /* Specific factors such as durability, damage etc. */ UPROPERTY(EditAnywhere) TArray <FItemFactor> ItemFactors; FContainerStack() { Clear(); } void Clear() { ItemNameTag.FromExportString("NAME_None"); ItemAmount = 0; ItemFactors.Empty(); } FORCEINLINE FGameplayTag GetItemNameTag() const { return ItemNameTag; } void SetItemNameTag(FGameplayTag const & ItemNameTagNew) { if (ItemNameTagNew.IsValid()) { ItemNameTag = ItemNameTagNew; } else { Clear(); } } FORCEINLINE int GetItemAmount() const { return ItemAmount; } void SetItemAmount(int const & ItemAmountNew) { if (ItemAmountNew > 0) { ItemAmount = ItemAmountNew; } else { Clear(); } } FORCEINLINE TArray<FItemFactor> * GetItemFactors() { return &ItemFactors; } void SetItemFactors(TArray<FItemFactor> const & ItemFactorsNew) { if (ItemFactorsNew.Num() > 0) { ItemFactors = ItemFactorsNew; } } };
Yes, our inventory cell contains only 3 variables: ID, quantity and unique parameters. Nothing extra. All data can be copied, saved and downloaded without problems. No textures, references to Actors , etc. not here. All additional information can be downloaded from the database to DataAsset , which we talked about earlier.
Most likely, you have already paid attention to another StructItemFactors.h structure, connected at the beginning. It is nothing but a repository of any unique properties of the object (in the form of a float ), such as wear, damage, etc. That is, the properties that are inherent only in this copy of the object, and no other is the same. This structure is very simple:
/// Copyright 2018 Dreampax Games, Inc. All Rights Reserved. /* Struct for Factors. This file is used as #include */ #pragma once /* Includes from Engine */ #include "GameplayTagContainer.h" /* Includes from Dreampax */ // no includes #include "StructItemFactors.generated.h" USTRUCT(BlueprintType) struct FItemFactor { GENERATED_USTRUCT_BODY() /* Name of Item Attribute Factor */ UPROPERTY(EditDefaultsOnly, Category = "ItemsDatabase") FGameplayTag ItemFactorTag; /* Factor for the Item Attribute */ UPROPERTY(EditDefaultsOnly, Category = "ItemsDatabase") float ItemFactor; /* for this type to be comparable */ friend bool operator==(const FItemFactor & Lhs, const FItemFactor & Rhs) { return Lhs.ItemFactorTag == Rhs.ItemFactorTag && Lhs.ItemFactor == Rhs.ItemFactor; } FItemFactor() { Clear(); } void Clear() { ItemFactorTag.EmptyTag; ItemFactor = 0; } FORCEINLINE FGameplayTag GetItemFactorTag() { return ItemFactorTag; } void SetItemFactorTag(FGameplayTag const &ItemFactorTagNew) { if (ItemFactorTagNew.IsValid()) { ItemFactorTag = ItemFactorTagNew; } else { Clear(); } } FORCEINLINE float GetItemFactor() { return ItemFactor; } void SetItemFactor(float const & ItemFactorNew) { if (ItemFactorNew > 0.0f) { ItemFactor = ItemFactorNew; } else { Clear(); } } };
It is worth noting one very interesting function in the structure above, which is designed to significantly simplify our lives:
friend bool operator==(const FItemFactor & Lhs, const FItemFactor & Rhs) { return Lhs.ItemFactorTag == Rhs.ItemFactorTag && Lhs.ItemFactor == Rhs.ItemFactor; }
This is nothing more than the comparison operator == , which we can use for this structure, so as not to extract elements for this every time. Very comfortably.
So, with the structures finished. We proceed to the creation of the component:
/// Copyright 2018 Dreampax Games, Inc. All Rights Reserved. #pragma once /* Includes from Engine */ #include "Components/ActorComponent.h" #include "GameplayTagContainer.h" /* Includes from Dreampax */ #include "Data/StructItemFactors.h" #include "Data/StructContainerStack.h" #include "DreampaxContainerComponent.generated.h" //UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) ) // currently not required UCLASS() class DREAMPAX_API UDreampaxContainerComponent : public UActorComponent { GENERATED_BODY() private: UPROPERTY(Transient, Replicated, EditAnywhere, Category = "Container") TArray<FContainerStack> ContentOfContainer; public: /* Sets default values for this component's properties */ UDreampaxContainerComponent(const FObjectInitializer & ObjectInitializer); /* , ... */ };
If in the code above activate the line
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
then you can connect this component directly to Blueprint . I prefer to do this in C ++ . For Character, it looks like this:
Inventory = CreateDefaultSubobject<UDreampaxContainerComponent>(TEXT("Inventory"));
Well, for some chest like this:
ADreampaxActorContainer::ADreampaxActorContainer(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { Container = CreateDefaultSubobject<UDreampaxContainerComponent>(TEXT("Container")); }
As you can see, the difference is only in the names of variables.
In the next article I will talk about the features of replication (simple on the fingers ), which will make our inventory truly multiplayer.
Source: https://habr.com/ru/post/420115/