跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++

UE5 C++ 背包系统开发教程

基于 UE5 C++ 的背包系统实现方案。涵盖 GameInstance 全局存储设计、物品 Actor 类与重叠事件、增强输入映射配置、背包组件逻辑封装以及 UMG UI 界面开发。实现了物品的拾取、丢弃、堆叠及拖拽交换功能,展示了 C++ 在游戏逻辑处理中的性能优势与结构化管理方式。

静心发布于 2026/3/24更新于 2026/6/2635 浏览
UE5 C++ 背包系统开发教程

前言

本教程适合对 UE C++ 以及 Gameplay 框架有一定了解的同学。通过实现基础功能,展示 C++ 在逻辑处理上的性能优势与代码清晰度。

创建所有需要用到的类

一、GameInstance 类

GameInstance 用于存储全局变量,背包数组在此保存可跨地图持久化。直接在 GameInstance 中创建物品结构体方便调用。

// MyBaseGameInstance.h
#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。包含物品信息结构体及重叠事件组件。

// ItemBase.h
#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);
};

构造函数初始化组件并绑定事件:

// ItemBase.cpp
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 键交互拾取。

//BackPackCPP_Character.h
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();

在 CPP 文件中绑定 Action:

//BackPackCPP_Character.cpp
void ABackPack_CppCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) {
    // ... Add Mapping Context ...
    EnhancedInputComponent->BindAction(OpenBPAction, ETriggerEvent::Triggered, this, &ABackPack_CppCharacter::BackPackUIController);
    EnhancedInputComponent->BindAction(InteractAction, ETriggerEvent::Triggered, this, &ABackPack_CppCharacter::Interact);
}

四、BackPackComponent 类

将背包逻辑封装为组件,便于管理添加/丢弃物品及 UI 控制。

//BackPackCPP_Character.h
public:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="BackPackSystem")
    UBackPackComponent* BackPackComponent;

五、UI 类

1. BackPackSlot

继承 UserWidget,显示物品图标和数量。

//BackPackSlot.h
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();
};

SetSlot 函数实现渲染逻辑:

//BackPackSlot.cpp
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

主界面 Widget,包含网格面板和关闭按钮。

//BackPackUI.h
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

右键点击格子弹出的操作菜单(使用/丢弃)。

//OperateUI.h
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 控件并添加到视口,设置鼠标光标模式。

//BackPackComponent.cpp
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();
    }
}

UI 构造时刷新格子内容:

//BackPackUI.cpp
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,检查背包是否有同名可堆叠物品,更新数量或新增条目。

//BackPackComponent.cpp
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。

//BackPackComponent.cpp
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。

//BackPackComponent.cpp
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++ 实现可有效提升复杂逻辑的性能表现与可维护性。

目录

  1. 前言
  2. 创建所有需要用到的类
  3. 一、GameInstance 类
  4. 二、Item 类
  5. 三、绑定输入映射
  6. 四、BackPackComponent 类
  7. 五、UI 类
  8. 1. BackPackSlot
  9. 2. BackPackUI
  10. 3. OperateUI
  11. 实现具体功能
  12. 1. 打开 & 关闭背包
  13. 2. 拾取物品
  14. 3. 丢弃物品
  15. 4. 交换物品
  16. 总结
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • AIGC 插画生成技术解析与 Python 代码实战
  • Java 高级工程师高频核心面试题
  • 格拉姆角场(GAF)详解
  • 写作助手 Prompt 实战:从提纲生成到全文优化
  • Buzz 离线语音转文字工具安装与使用指南
  • 为什么在人工智能时代选择学习 Python 编程语言
  • 德力西 DTS606 电表数据采集实践:485 转网络与 Python 实现
  • 知网 AIGC 检测算法升级解读与应对策略
  • VcXsrv Windows X Server 使用指南:在 Windows 上运行 Linux 图形应用
  • Python 处理 JSON 如何保持字段顺序?底层机制解析
  • Python 核心面试题:装饰器、数据结构与版本差异详解
  • JDK 17 安装与环境配置实战指南
  • 飞书与 OpenClaw 接入指南:无需服务器,长连接运行机器人
  • LangChain 工具调用与结构化输出实战
  • LLaMA Factory 大模型微调实战指南
  • 算法模拟实战:替换所有问号与提莫攻击解析
  • 算法实战:归并排序与数组逆序对详解
  • Web3 信任协议技术选型:OmniPact 实体资产上链方案解析
  • LLM 大模型技术实战:入门大模型开发框架 LangChain
  • 量化、算子融合、内存映射:C 语言实现 AI 推理的三板斧

相关免费在线工具

  • 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