独立游戏《星尘异变》UE5 C++程序开发日志3——实现一个存存组件

本篇日志中,我将会介绍如何实现一个有格子,每个格子有容量的物品库存,如下图:

一.库存容器

1.储存数据的容器

        库存容器最重要的目的就是存储每一种类的物品拥有的数量,这里我用的是哈希表:

std::unordered_map<std::string, int>StardustCount;//从星尘ID到存储的数量的映射

哈希表的优点就是查询速度极快,我们的的库存在每次发生“反应”,进口等过程时都要进行数量的查询,所以要尽可能降低查询的复杂度,这也就是为什么我们不用TMap,因为TMap在每次使用"[]"运算符前,要检查其是否含有要查询的元素。

        而他的优点就是不便于展示,因为我们要实现的库存是有有格子,每个格子有存储上限的容器,所以我们要再定义一个数组,数组中的每一个索引对应的就是展示的一个格子:

2.显示数据的容器

	UPROPERTY(VisibleAnywhere,BlueprintReadOnly,Category="Storage")
	TArray<UStardustItemClass*>Storage;//输入仓库

        数组中的数据类型是一个UObject指针,该UObject内除了上一篇日志中展示的数据外,多了一个该槽位物品数量的变量"Quantity":


USTRUCT(BlueprintType)
struct FStardustItem
{
	GENERATED_BODY();
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "StardustBase")
	FName StardustName{"Empty"};//名称
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "StardustBase")
	FText Description;//描述
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "StardustBase")
	FName StardustId{ "Empty" };//编号
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "StardustBase")
	FStardustStatisticsForReaction ReactionStatistics{FStardustStatisticsForReaction()};//反应数据
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "StardustBase")
	FStardustStatisticsForInventory InventoryStatistics{FStardustStatisticsForInventory()};//库存数据
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "StardustBase", meta = (UIMin = 1))
	int Quantity{0};//数量

	FStardustItem() = default;
	explicit FStardustItem(const FStardustDataTable& Stardust)
	{
		StardustId = Stardust.StardustId;
		StardustName = Stardust.StardustName;
		Description = Stardust.Description;
		Quantity = 0;
		ReactionStatistics = Stardust.ReactionStatistics;
		InventoryStatistics = Stardust.InventoryStatistics;
	}
	void SetQuantity(int Num)//设置该槽位内的星尘数量
    {
        if (Num > 0)
	    {
		    Quantity =  Num;
	    }
	    else
	    {//数量为0就将其替换成默认空的物品
		    *this = FStardustItem();
	    };
    }

};

UCLASS(BlueprintType)
class ASTROMUTATE_2_API UStardustItemClass : public UObject
{
	GENERATED_BODY()
public:
    //物品信息
    UPROPERTY(VisibleAnywhere,BlueprintReadOnly,Category="StardustItem")
    FStardustItem ItemData;
    
};

3.库存的其他数据

        我们游戏中的库存不是无限大的,所有有一个最大的槽位数,该变量可能受游戏中的因素影响,上面的数组大小始终等于该变量大小

        然后我们还需要加载全局单例,后面需要用到上一篇日志中提到过的“星尘”数据表

//仓库槽位数
UPROPERTY(EditAnywhere, BlueprintReadWrite, category = "StardustInventory")
int SlotsCapacity;
//全局单例,用于查询数据表中的物品数据
UPROPERTY()
class UAstromutateGameInstance* Instance;
//在.cpp的BeginPlay中实例化Instance
//Instance = Cast<UAstromutateGameInstance>(GetWorld()->GetGameInstance());

二.容器的查询

        因为我们之前实现过哈希表,所以可以直接O(1)查询某物品在库存中的容量

UFUNCTION(BlueprintCallable, Category = "StardustInventory")
FORCEINLINE int CheckStardust(FName StardustType) {
	return StardustCount[TCHAR_TO_UTF8(*StardustType.ToString())];};//从映射中O(1)查询其在库存中的数量	

        我们还有一个查询某物品在库存中还能添加多少的函数,也是利用哈希表O(1)实现

//.h中的声明
//检查星尘在库存中还能添加多少
UFUNCTION(BlueprintCallable, Category = "StardustInventory")
int CheckAddable(const FName& StardustId);

//.cpp中的实现
nt UStarInventoryComponent::CheckAddable(const FName& StardustId)
{
	std::string StardustIdString{ TCHAR_TO_UTF8(*StardustId.ToString()) };//将FName转换成std::string
	int StackLimit{ Instance->StardustMap[StardustIdString]->InventoryStatistics.StardustStackLimit };//该类物品的堆叠上限
	int AvailableInPartial{ StackLimit - StardustCount[StardustIdString] % StackLimit };//该类物品在非空槽位中还能装多少
	if (StardustCount[StardustIdString] % StackLimit == 0)//所有物品的堆叠上限不能为0
	{
		AvailableInPartial = 0;
	}
	int AvailableInEmptySlots = StardustCount["Empty"] * StackLimit;//在空槽位中可放的数量
	return AvailableInEmptySlots + AvailableInPartial;
}

三.库存的修改

        我们库存中的增加和删除操作都是基于对单个槽位的修改实现的,传入参数是期望的“星尘”,用的是完整的槽位中物品的结构,返回值为是否修改成功,修改时需要同时维护数组和哈希表:

bool UStarInventoryComponent::SetSlotElement(const FName StardustId,const int Amount, int index)
{
	if (index < 0 || index >= Storage.Num())
	{//检查索引是否合法
		UE_LOG(LogTemp, Error, TEXT("se slot at %d failed,invalid index"), index);
		return false;
	}
	int OriginalAmount = Storage[index]->ItemData.GetQuantity();	
	FName OriginalId = Storage[index]->ItemData.StardustId;
	StardustCount[TCHAR_TO_UTF8(*Storage[index]->ItemData.StardustId.ToString())] -= OriginalAmount;//先将这一格清空
	//如果星尘是新加进来的,就要将表格中的数据赋给新星尘
	std::string NewStardustId{ TCHAR_TO_UTF8(*StardustId.ToString()) };
	FStardustTable StardustInfo= *Instance->StardustMap[NewStardustId];
	int StackLimit = StardustInfo.InventoryStatistics.StardustStackLimit;
	//将新星辰的数据覆盖原星辰
	Storage[index]->ItemData = FStardustItem(StardustInfo);
	if (Amount > StackLimit)
	{//超出堆叠上限的部分直接抛弃
		if (OriginalId == "Empty" && Storage[index]->ItemData.StardustId != "Empty")
		{
			StardustCount["Empty"]--;
		}
		if (OriginalId != "Empty" && Storage[index]->ItemData.StardustId == "Empty")
		{
			StardustCount["Empty"]++;
		}
		StardustCount[NewStardustId] += StackLimit;
		Storage[index]->ItemData.SetQuantity(StackLimit);
		return true;
	}
	if (Amount <= 0)
	{//将该槽位的星尘替换成空星尘
		StardustCount["Empty"]++;
		Storage[index]->ItemData.SetQuantity(Amount);
		return true;
	}
	if (OriginalId == "Empty" && Storage[index]->ItemData.StardustId != "Empty")
	{
		StardustCount["Empty"]--;
	}
	if (OriginalId != "Empty" && Storage[index]->ItemData.StardustId == "Empty")
	{
		StardustCount["Empty"]++;
	}
	//正常更改数量
	Storage[index]->ItemData.SetQuantity(Amount);
	StardustCount[NewStardustId] += Amount;
	return true;
}

        我们还有一个整理背包的函数,可以实现将库存中同类物品尽可能放在一起:        

持续更新中。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/623025.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

“圣诞树图案的打印~C语言”

圣诞树图案的打印~C语言 题目原文&#xff1a;[圣诞树](https://www.nowcoder.com/practice/9a03096ed8ab449e9b10b0466de29eb2?tpId107&rp1&ru/ta/beginner-programmers&qru/ta/beginner-programmers/question-ranking&difficulty&judgeStatus&tags&…

机器学习中的聚类

目录 认识聚类算法 聚类算法API的使用 聚类算法实现流程 聚类算法模型评估 认识聚类算法 聚类算法是一种无监督的机器学习算法。 它将一组数据分成若干个不同的群组&#xff0c;使得每个群组内部的数据点相似度高&#xff0c;而不同群组之间的数据点相似度低。常用的相似…

Linux0.11 中全局描述符表(GDT)

在Linux内核中&#xff0c;全局描述符表&#xff08;Global Descriptor Table&#xff0c;简称GDT&#xff09;是一个关键的数据结构&#xff0c;主要用于管理处理器的内存段和相关的权限与属性。它属于x86架构中的保护模式特性&#xff0c;允许操作系统对内存访问进行更精细的…

HFSS学习-day5-边界条件

边界条件 概述边界条件类型1、理想导体边界条件&#xff08;Perfect E&#xff09;2、理想磁边界条件&#xff08;Perfect H&#xff09;3、有限导体边界条件&#xff08;Finite Conductivity&#xff09;4、辐射边界条件&#xff08;Radiation&#xff09;5、对称边界条件&…

微信小程序开发题库

一. 单选题&#xff08;共12题&#xff0c;60分&#xff09; 1. (单选题) 有如下HTML代码&#xff1a; <!DOCTYPE html> <html> <head> <meta charset"UTF-8"> <title>Document</title> <style> ul,li{ margin:0; p…

企业为什么需要HTTPS

一.什么是HTTPS HTTPS &#xff08;全称&#xff1a;Hyper Text Transfer Protocol over SecureSocket Layer&#xff09;&#xff0c;是以安全为目标的 HTTP 通道&#xff0c;在HTTP的基础上通过传输加密和身份认证保证了传输过程的安全性 。HTTPS 在HTTP 的基础下加入SSL&a…

算法day05

第一题 1004. 最大连续1的个数 III 题目如下所示&#xff1a; 如上题所示&#xff1a; 题目本意是在一个数组中只有1和0&#xff0c;给定一个k值&#xff0c;将小于k个0翻转成1&#xff0c;然后返回最终得到最长的1的个数&#xff1b; 我们将这到题的意思转化为另外一种意思&…

C++ | Leetcode C++题解之第88题合并两个有序数组

题目&#xff1a; 题解&#xff1a; class Solution { public:void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {int p1 m - 1, p2 n - 1;int tail m n - 1;int cur;while (p1 > 0 || p2 > 0) {if (p1 -1) {cur nums2[p2-…

Gitee添加仓库成员

1.进入你的项目 2.点击管理 3.左侧有个仓库管理 4.要加哪个加哪个&#xff0c;有三个方式~ 可以直接添加之前仓库合作过的开发者

STM32有什么高速接口吗?

STM32 有一些高速接口&#xff0c;比如 USART、SPI、I2C 等&#xff0c;这些接口可以用于与外部设备进行高速数据传输。我这里有一套stm32入门教程&#xff0c;不仅包含了详细的视频讲解&#xff0c;项目实战。如果你渴望学习stm32&#xff0c;不妨点个关注&#xff0c;给个评论…

上位机图像处理和嵌入式模块部署(树莓派4b的替代品)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 实话实说&#xff0c;树莓派4b的产品力还是比较优秀的&#xff0c;价格还算适中。但是和国产卡片电脑比起来&#xff0c;则逊色不少。功能差不多的…

牛客小白月赛90VP

1&#xff0c;签到&#xff1a;https://ac.nowcoder.com/acm/contest/78306/A AC代码&#xff1a; #include<bits/stdc.h> using namespace std; int n,m,a[100010]; long long sum; int main() {cin>>n>>m;for(int i1;i<n;i) cin>>a[i];for(int …

2023年数维杯国际大学生数学建模挑战赛A题复合直升机的建模与优化控制问题解题全过程论文及程序

2023年数维杯国际大学生数学建模挑战赛 A题 复合直升机的建模与优化控制问题 原题再现&#xff1a; 直升机具有垂直起降等飞行能力&#xff0c;广泛应用于侦察、运输等领域。传统直升机的配置导致旋翼叶片在高速飞行过程中受到冲击波的影响&#xff0c;难以稳定飞行。为了在保…

台式电脑屏幕亮度怎么调节?让你的眼睛更舒适!

在日常使用台式电脑时&#xff0c;调节屏幕亮度是一项常见的需求。不同的环境和个人偏好可能需要不同的亮度设置。因此&#xff0c;了解台式电脑屏幕亮度怎么调节是非常重要的。本文将介绍三种常见的方法&#xff0c;帮助您轻松调节台式电脑屏幕亮度&#xff0c;以满足您的需求…

ollama 导入GGUF模型文件

GGUF模型导入 1 编写Modelfile文件 1&#xff1a;创建一个文本文件&#xff0c;建议改名为模型名&#xff0c;扩展名为Modelfile。 比如你下载的模型文件名是llama-8b.gguf&#xff0c;那这个文件名就叫llama-8b.modelfile 2&#xff1a;用记事本编辑文件添加模型文件地址&am…

Django模型进阶

一.配置MySQL 1.安装mysql 2.mysql驱动 使用mysqlclient pip install mysqlclient 如果上面的命令安装失败&#xff0c;则尝试使用国内豆瓣源安装: pip install -i https://pypi.douban.com/simple mysglclien (Linux Ubuntu下需要先安装:apt install lib…

Linux编辑器vim的配置

文章目录 简单vim配置配置文件的位置常用配置选项&#xff0c;用来测试 使用插件 简单vim配置 配置文件的位置 在目录/etc/下面&#xff0c;有个名叫vimrc的文件&#xff0c;这是系统中公共的vim配置文件&#xff0c;对所有用户有效。 [yuiiZbp19b5lvg8xtpf1tzrenZ etc]$ cl…

政安晨:【Keras机器学习示例演绎】(四十四)—— 使用序列到序列转换器实现英语到西班牙语的翻译

目录 简介 设置 下载数据 解析数据 文本数据矢量化 建立模型 训练我们的模型 解码测试句子 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: TensorFlow与Keras机器学习实战 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之…

std::ref和std::cref的使用和原理分析

目录 1.用法 2.std::reference_wrapper介绍 3.std::ref原理分析 4.std::cref原理分析 5.总结 1.用法 它的定义如下&#xff1a; std::ref&#xff1a;用于包装按引用传递的值。 std::cref&#xff1a;用户包装按const引用传递的值。 C本身就有引用&#xff08;&&#…

笔记-跨域方式实现原理

websocket Websocket是HTML5的一个持久化的协议&#xff0c;它实现了浏览器与服务器的全双工通信&#xff0c;同时也是跨域的一种解决方案。WebSocket和HTTP都是应用层协议&#xff0c;都基于 TCP 协议。但是 WebSocket 是一种双向通信协议&#xff0c;在建立连接之后&#xff…