Розробка

UE4 | Інвентар для Multiplayer #4 | Створення і підключення контейнера

Список статей

UE4 | Інвентар для Multiplayer #1 | Сховище даних на DataAsset
UE4 | Інвентар для Multiplayer #2 | Підключення Blueprint до C++
UE4 | Інвентар для Multiplayer #3 | Структура взаємодії
UE4 | Інвентар для Multiplayer #4 | Створення і підключення конейнера

 

У цій статті ми обговоримо створення компонента інвентарю та підключення його до необхідного Actor. Оскільки даний компонент являє собою просто сховище предметів і логіку їх завантаження/вивантаження, то немає ніякої різниці в застосуванні його для персонажа або який-небудь коробки.

 

Створити компонент можна як за допомогою Blueprint, так з допомогою С++. Я віддаю перевагу другому способу, так як збираюся активно використовувати функціонал С++.

 

В першу чергу створюємо структуру комірки для зберігання одного предмета. Я віддаю перевагу зберігати її в окремому .h файлі, щоб вільно підключати за необхідності там де потрібно:

StructContainerStack.h

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

 

Так, наша клітинка інвентарю містить всього 3 змінні: ідентифікатор, кількість і унікальні параметри. Нічого зайвого. Всі дані можуть без проблем бути скопійовані, збережені і завантажені. Жодних текстур, посилань на Actors і т. п. тут немає. Вся додаткова інформація може бути завантажена з бази даних на DataAsset, про яку ми говорили раніше.

 

Швидше за все, ви вже звернули увагу на ще одну структуру StructItemFactors.h, підключену до початку. Це не що інше як сховище будь-яких унікальних властивостей об’єкта (у вигляді float), таких як знос, шкоди і т. п. тобто властивостей, які притаманні тільки цій копії предмета, і ніякий інший такий же. Ця структура дуже проста:

StructItemFactors.h

/// Copyright 2018 Dreampax Games, Inc. All Rights Reserved.

/* Struct Factors for. This file is used as #include */

#pragma once

/* Includes from Engine */
#include "GameplayTagContainer.h"

/* Includes from Dreampax */
// includes no

#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.0 f)
{
 ItemFactor = ItemFactorNew;
}
else
{
Clear();
}
}
};

 

Варто відзначити одну дуже цікаву функцію в структурі вище, яка покликана суттєво спростити нам життя:

 

friend bool operator==(const FItemFactor & Lhs, const FItemFactor & Rhs)
{
 return Lhs.ItemFactorTag == Rhs.ItemFactorTag && Lhs.ItemFactor == Rhs.ItemFactor;
 }

 

Це не що інше як оператор порівняння ==, який ми зможемо використовувати для цієї структури, щоб не витягувати кожен раз елементи для цього. Дуже зручно.

 

Отже, зі структурами закінчили. Переходимо до створення компонента:

DreampaxContainerComponent.h

/// 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);

/*
 Далі йдуть функції для отримання даних з компонента,
реплікації і т. п..
*/
};

 

Якщо в коді вище активувати рядок

 

UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )

 

то можна буде підключати даний компонент прямо в Blueprint. Я ж віддаю перевагу робити це в С++. Для Character це виглядає ось так:

 

Inventory = CreateDefaultSubobject<UDreampaxContainerComponent>(TEXT("Inventory"));

 

Ну, а для якого-небудь скрині ось так:

 

ADreampaxActorContainer::ADreampaxActorContainer(const FObjectInitializer& ObjectInitializer)
 : Super(ObjectInitializer)
{
 Container = CreateDefaultSubobject<UDreampaxContainerComponent>(TEXT("Container"));
}

 

Як бачите, різниця лише в назвах змінних.

 

У наступній статті я розповім про особливості реплікації (по простому на пальцях), що дозволить зробити наш інвентар дійсно мультиплеєрним.

Related Articles

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *

Close