跳到主要内容
UE5 C++ 背包系统开发教程 | 极客日志
C++
UE5 C++ 背包系统开发教程 综述由AI生成 基于 UE5 C++ 的背包系统实现方案。涵盖 GameInstance 全局存储设计、物品 Actor 类与重叠事件、增强输入映射配置、背包组件逻辑封装以及 UMG UI 界面开发。实现了物品的拾取、丢弃、堆叠及拖拽交换功能,展示了 C++ 在游戏逻辑处理中的性能优势与结构化管理方式。
静心 发布于 2026/3/24 更新于 2026/5/12 18 浏览前言
本教程适合对 UE C++ 以及 Gameplay 框架有一定了解的同学。通过实现基础功能,展示 C++ 在逻辑处理上的性能优势与代码清晰度。
创建所有需要用到的类
一、GameInstance 类
GameInstance 用于存储全局变量,背包数组在此保存可跨地图持久化。直接在 GameInstance 中创建物品结构体方便调用。
#pragma once
#include "CoreMinimal.h"
#include "Engine/GameInstance.h"
#include "Engine/DataTable.h"
#include "MyBaseGameInstance.generated.h"
UENUM (BlueprintType)
enum class EItem_Type : uint8 {
Weapon UMETA (DisplayName = "Weapon" ) ,
Prop UMETA (DisplayName = "Prop" ) ,
Food UMETA (DisplayName = "Food" )
} ;
USTRUCT (BlueprintType)
struct FItem_Struct : public FTableRowBase {
GENERATED_BODY ()
UPROPERTY (EditAnywhere, BlueprintReadWrite) FString Name;
UPROPERTY (EditAnywhere, BlueprintReadWrite) int32 Index;
UPROPERTY (EditAnywhere, BlueprintReadWrite) UTexture2D* Icon;
UPROPERTY (EditAnywhere, BlueprintReadWrite) int32 Count;
UPROPERTY (EditAnywhere, BlueprintReadWrite) int32 Max_Count;
UPROPERTY (EditAnywhere, BlueprintReadWrite) bool Can_Stack;
(EditAnywhere, BlueprintReadWrite) EItem_Type Item_Type;
};
()
UMyBaseGameInstance : UGameInstance {
()
:
(EditAnywhere, BlueprintReadWrite) TArray<FItem_Struct> BackPack_Array;
};
UPROPERTY
UCLASS
class
BACKPACK_CPP_API
public
GENERATED_BODY
public
UPROPERTY
编译后在项目设置中将游戏实例改为 MyBaseGameInstance 类。
二、Item 类 创建物品基类 Actor,命名为 ItemBase。包含物品信息结构体及重叠事件组件。
#pragma once
#include "CoreMinimal.h"
#include "Components/SphereComponent.h"
#include "GameFramework/Actor.h"
#include "Engine/DataTable.h"
#include "Gameplay/MyBaseGameInstance.h"
#include "ItemBase.generated.h"
UCLASS ()
class BACKPACK_CPP_API AItemBase : public AActor {
GENERATED_BODY ()
public :
AItemBase ();
UPROPERTY (EditAnywhere, BlueprintReadWrite, Category="BackPackSystem" ) FItem_Struct CurrentItemState;
UPROPERTY (EditAnywhere, BlueprintReadWrite, Category="Comp" ) USphereComponent* SphereComp;
UPROPERTY (EditAnywhere, BlueprintReadWrite, Category="Comp" ) UStaticMeshComponent* MeshComp;
protected :
UFUNCTION ()
void OnSphereBeginOverlap (UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult) ;
void OnSphereEndOverlap (UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult) ;
};
AItemBase::AItemBase () {
SphereComp = CreateDefaultSubobject <USphereComponent>(TEXT ("Sphere" ));
RootComponent = SphereComp;
MeshComp = CreateDefaultSubobject <UStaticMeshComponent>(TEXT ("StaticMesh" ));
MeshComp->SetupAttachment (SphereComp);
SphereComp->OnComponentBeginOverlap.AddDynamic (this , &AItemBase::OnSphereBeginOverlap);
}
void AItemBase::OnSphereBeginOverlap (...) {
if (OtherActor && OtherActor != this ) {
if (OtherActor->ActorHasTag ("Player" )) {
ABackPack_CppCharacter* Player = Cast <ABackPack_CppCharacter>(OtherActor);
if (Player && IsValid (Player->BackPackComponent)) {
Player->FocusedItem = this ;
}
}
}
}
三、绑定输入映射 在角色类中设置增强输入,绑定 Tab 键打开背包,E 键交互拾取。
public :
UPROPERTY (EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true" ))
UInputAction* OpenBPAction;
UPROPERTY (EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true" ))
UInputAction* InteractAction;
bool bIsOpenBackPack = false ;
UPROPERTY ()
UBackPackUI* BackPackUI;
UFUNCTION (BlueprintCallable, Category="BackPackSystem" )
void BackPackUIController () ;
UPROPERTY ()
AItemBase* FocusedItem;
void Interact () ;
void ABackPack_CppCharacter::SetupPlayerInputComponent (UInputComponent* PlayerInputComponent) {
EnhancedInputComponent->BindAction (OpenBPAction, ETriggerEvent::Triggered, this , &ABackPack_CppCharacter::BackPackUIController);
EnhancedInputComponent->BindAction (InteractAction, ETriggerEvent::Triggered, this , &ABackPack_CppCharacter::Interact);
}
四、BackPackComponent 类 将背包逻辑封装为组件,便于管理添加/丢弃物品及 UI 控制。
public :
UPROPERTY (EditAnywhere, BlueprintReadWrite, Category="BackPackSystem" )
UBackPackComponent* BackPackComponent;
五、UI 类
1. BackPackSlot
UCLASS ()
class BACKPACK_CPP_API UBackPackSlot : public UUserWidget {
GENERATED_BODY ()
public :
virtual void NativeConstruct () override ;
UPROPERTY (EditAnywhere, BlueprintReadWrite, Category = "BackPackSystem" , meta = (BindWidget))
TObjectPtr<UImage> SlotImage;
UPROPERTY (EditAnywhere, BlueprintReadWrite, Category = "BackPackSystem" , meta = (BindWidget))
TObjectPtr<UTextBlock> Number_Text;
UPROPERTY (EditAnywhere, BlueprintReadWrite, Category = "BackPackSystem" )
int32 Index = -1 ;
UPROPERTY (EditAnywhere, BlueprintReadWrite, Category = "BackPackSystem" )
int32 Number = 0 ;
UPROPERTY (EditAnywhere, BlueprintReadWrite, Category = "BackPackSystem" )
TObjectPtr<UTexture2D> Image;
void SetSlot () ;
};
void UBackPackSlot::SetSlot () {
if (Number > 1 ) {
Number_Text->SetText (FText::FromString (FString (std::to_string (Number).c_str ())));
Number_Text->SetOpacity (1.0f );
} else {
Number_Text->SetOpacity (0.0f );
}
FSlateBrush InBrush;
InBrush.SetResourceObject (Image);
SlotImage->SetBrush (InBrush);
}
2. BackPackUI
public :
UPROPERTY (EditAnywhere, BlueprintReadWrite, Category = "BackPackSystem" , meta = (BindWidget))
TObjectPtr<UUniformGridPanel> BackPackPanel;
UPROPERTY (EditAnywhere, BlueprintReadWrite, Category = "BackPackSystem" , meta = (BindWidget))
TObjectPtr<UButton> CloseButton;
int32 SlotCount = 16 ;
3. OperateUI
UCLASS ()
class BACKPACK_CPP_API UOperateUI : public UUserWidget {
GENERATED_BODY ()
public :
virtual void NativeConstruct () override ;
UPROPERTY (EditAnywhere, BlueprintReadWrite, Category = "BackPackSystem" , meta = (BindWidget))
TObjectPtr<UButton> UseButton;
UPROPERTY (EditAnywhere, BlueprintReadWrite, Category = "BackPackSystem" , meta = (BindWidget))
TObjectPtr<UButton> ThrowButton;
UPROPERTY (EditAnywhere, BlueprintReadWrite, Category = "BackPackSystem" )
int32 SlotIndex;
UFUNCTION (BlueprintCallable, Category="BackPackSystem" )
void OnUseButtonClick () ;
UFUNCTION (BlueprintCallable, Category="BackPackSystem" )
void OnThrowButtonClick () ;
};
实现具体功能
1. 打开 & 关闭背包 Tab 键触发 BackPackUIController,创建 UI 控件并添加到视口,设置鼠标光标模式。
void UBackPackComponent::BackPackUIController () {
APlayerController* PlayerController = GetWorld ()->GetFirstPlayerController ();
if (!bIsOpenBackPack) {
if (!IsValid (GetWorld ())) return ;
APlayerController* OwningPlayer = GetWorld ()->GetFirstPlayerController ();
TSubclassOf<UUserWidget> WidgetClass = LoadClass <UBackPackUI>(nullptr , TEXT ("/Game/BackPackSystem/UMG/UMG_BackPack.UMG_BackPack_C" ));
BackPackUI = CreateWidget <UBackPackUI>(OwningPlayer, WidgetClass);
if (BackPackUI) {
BackPackUI->BackPackTransfer = Cast <UMyBaseGameInstance>(GetWorld ()->GetGameInstance ())->BackPack_Array;
BackPackUI->AddToViewport ();
bIsOpenBackPack = true ;
PlayerController->SetShowMouseCursor (true );
PlayerController->SetInputMode (FInputModeUIOnly ());
}
} else {
if (BackPackUI) BackPackUI->RemoveFromParent ();
PlayerController->SetInputMode (FInputModeGameOnly ());
PlayerController->SetShowMouseCursor (false );
bIsOpenBackPack = false ;
if (OperateUI) OperateUI->RemoveFromParent ();
}
}
void UBackPackUI::NativeConstruct () {
Super::NativeConstruct ();
UMyBaseGameInstance* MyGameInstance = Cast <UMyBaseGameInstance>(GetWorld ()->GetGameInstance ());
BackPackTransfer = MyGameInstance->BackPack_Array;
RefreshBackPack ();
CloseButton->OnClicked.AddDynamic (this , &UBackPackUI::OnCloseButtonClick);
}
void UBackPackUI::RefreshBackPack () {
if (!IsValid (GetWorld ())) return ;
APlayerController* OwningPlayer = GetWorld ()->GetFirstPlayerController ();
TSubclassOf<UBackPackSlot> WidgetClass = LoadClass <UBackPackSlot>(nullptr , TEXT ("/Game/BackPackSystem/UMG/UMG_BackPackSlot.UMG_BackPackSlot_C" ));
BackPackPanel->ClearChildren ();
for (int32 i = 0 ; i < SlotCount; i++) {
BackPackSlot = CreateWidget <UBackPackSlot>(OwningPlayer, WidgetClass);
if (BackPackTransfer.IsValidIndex (i)) {
BackPackSlot->Image = BackPackTransfer[i].Icon;
BackPackSlot->Index = i;
BackPackSlot->Number = BackPackTransfer[i].Count;
BackPackSlot->SetSlot ();
}
BackPackPanel->AddChildToUniformGrid (BackPackSlot, (i / 4 ), (i % 4 ));
}
}
2. 拾取物品 按下 E 键调用 AddItem,检查背包是否有同名可堆叠物品,更新数量或新增条目。
void UBackPackComponent::AddItem (FItem_Struct NewItem) {
UWorld* world = GetWorld ();
if (!world) return ;
UMyBaseGameInstance* MyGameInstance = Cast <UMyBaseGameInstance>(GetWorld ()->GetGameInstance ());
if (!MyGameInstance) return ;
TArray<FItem_Struct>& BackpackArray = MyGameInstance->BackPack_Array;
for (auto & Item : BackpackArray) {
if (Item.Name == NewItem.Name && Item.Can_Stack && Item.Count < Item.Max_Count) {
Item.Count += NewItem.Count;
return ;
}
}
NewItem.Index = BackpackArray.Num ();
BackpackArray.Add (NewItem);
}
3. 丢弃物品 右键点击格子生成 OperateUI,点击丢弃按钮调用 ThrowItem,根据索引移除物品并在原地生成 Actor。
void UBackPackComponent::ThrowItem (int32 SlotIndex) {
if (!IsValid (GetWorld ())) return ;
UMyBaseGameInstance* MyGameInstance = Cast <UMyBaseGameInstance>(GetWorld ()->GetGameInstance ());
TArray<FItem_Struct>& BackPackArray = MyGameInstance->BackPack_Array;
if (SlotIndex < 0 || SlotIndex >= BackPackArray.Num ()) return ;
if (BackPackArray.Num () > 0 ) {
FString Name = BackPackArray[SlotIndex].Name;
AActor* SpawnActor = SpawnActorClass (Name);
if (SpawnActor) {
APlayerController* PlayerController = GetWorld ()->GetFirstPlayerController ();
if (PlayerController) {
APawn* PlayerPawn = PlayerController->GetPawn ();
if (PlayerPawn) {
FVector Location = PlayerPawn->GetActorLocation ();
FVector SpawnLocation = Location + FVector (0.f , 0.f , -80.f );
SpawnActor->SetActorLocation (SpawnLocation);
}
BackPackArray[SlotIndex].Count--;
if (BackPackArray[SlotIndex].Count <= 0 ) BackPackArray.RemoveAt (SlotIndex);
}
if (BackPackUI) {
BackPackUI->BackPackTransfer = BackPackArray;
BackPackUI->RefreshBackPack ();
}
}
}
}
AActor* UBackPackComponent::SpawnActorClass (const FString& Name) {
if (!IsValid (GetWorld ())) return nullptr ;
FString BlueprintPath = FString::Printf (TEXT ("/Game/BackPackSystem/Item/%s.%s_C" ), *Name, *Name);
UClass* ActorClass = LoadClass <AItemBase>(nullptr , *BlueprintPath);
if (ActorClass) {
AActor* SpawnedActor = GetWorld ()->SpawnActor <AActor>(ActorClass);
return SpawnedActor;
}
return nullptr ;
}
4. 交换物品 拖动 Slot 到另一个 Slot,交换数组中的两个物体位置并刷新 UI。
void UBackPackComponent::SwapItem (int32 DragIndex, int32 DropIndex) {
if (!IsValid (GetWorld ())) return ;
UMyBaseGameInstance* MyGameInstance = Cast <UMyBaseGameInstance>(GetWorld ()->GetGameInstance ());
TArray<FItem_Struct> BackPackArray = MyGameInstance->BackPack_Array;
FItem_Struct TempItem = BackPackArray[DragIndex];
BackPackArray[DragIndex] = BackPackArray[DropIndex];
BackPackArray[DropIndex] = TempItem;
MyGameInstance->BackPack_Array = BackPackArray;
if (BackPackUI) {
BackPackUI->BackPackTransfer = BackPackArray;
BackPackUI->RefreshBackPack ();
}
}
总结 本文展示了基于 UE5 C++ 的背包系统核心逻辑实现,包括数据存储、Actor 交互、输入绑定、组件封装及 UI 动态生成。通过 C++ 实现可有效提升复杂逻辑的性能表现与可维护性。
相关免费在线工具 Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
Markdown转HTML 将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
HTML转Markdown 将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
JSON 压缩 通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
JSON美化和格式化 将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online