UE求职Demo开发日志#19 给物品找图标,实现装备增加属性,背包栏UI显示装备

1 将用到的图标找好,放一起

 DataTable里对应好图标

 测试一下能正确获取:

2 装备增强属性思路

给FMyItemInfo添加一个枚举变量记录类型(物品,道具,装备,饰品,武器)-->

扩展DataTable,新建行结构继承FMyItemData,增加属性增强数组(数组元素为Struct,其中的属性为FString,bool isPercent增强百分比或加数值,float value)和GA数组(待完善,赋予的能力)-->

用属性初始化AS时-->(倍率属性不参与)(读取的是固定增幅,额外增幅应该记录在FMyItemInfo中)

获取基础属性-->临时的结构体数组记录每个属性增强的百分比和数值

遍历装备列表-->

读取并计算装备增幅-->获取并赋予GA-->

遍历装备列表-->

读取并计算饰品增幅-->获取并赋予GA-->

获取武器id-->

读取并计算武器增幅-->获取并赋予GA-->

每个属性最终先+总数值,再*总百分比(1+总增幅百分比)

最终属性赋予AS

3 实现过程

3.1 增加枚举类型变量

UENUM(BlueprintType)
enum class EMyItemType : uint8
{
	None=0,
	Item UMETA(DisplayName = "Item"),
	Prop UMETA(DisplayName = "Prop"),
	Arm UMETA(DisplayName = "Arm"),
	Accessory UMETA(DisplayName = "Accessory"),
	Weapon UMETA(DisplayName = "Weapon"),
};

 结构体里增加表示类型的变量:

struct ARPG_CPLUS_API FMyItemInfo
{
	GENERATED_USTRUCT_BODY()
	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	int32 ItemId;
	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	int64 CurrentOwnedCnt;
	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	FString DisplayName;
	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	EMyItemType ItemType{EMyItemType::Item};
	
	FMyItemInfo(int32 ItemId,int64 CurrentOwnedCnt,FString DisplayName) : ItemId(ItemId), CurrentOwnedCnt(CurrentOwnedCnt), DisplayName(DisplayName)
	{}
	FMyItemInfo()
	{
		ItemId = 0;
		CurrentOwnedCnt=0;
		DisplayName=FString("Default");
	}
};

3.2 添加修改器的结构体并创建要使用的行结构,继承FMyItemData

USTRUCT(BlueprintType)
struct ARPG_CPLUS_API FAttributeModifier
{
	GENERATED_USTRUCT_BODY()
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	FString AttributeName;
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	bool bIsPercent;
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	float PercentValue;
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	float AddedValue;
};
USTRUCT(BlueprintType)
struct ARPG_CPLUS_API FAttrModItemData:public FMyItemData
{
	GENERATED_USTRUCT_BODY()
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TArray<FAttributeModifier> AttributeMods;
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TArray<TSubclassOf<UGameplayAbility>> GAsToAdd;
};

3.3 MyPlayerAttributeSet里的计算逻辑

话不多说了(逻辑太长了)

核心逻辑:

传入的是要检查的所有装备,饰品等的Id

考虑所有物品的所有属性的修改幅值,最后应用修改器

void UMyPlayerAttributeSet::ModAttribute(TArray<int> ItemsId)
{
	LogBaseValueMes();
	TArray<FAttributeModifier> Sum{};//不受bIsPercent限制
	for(int i:ItemsId)
	{
		CalModMagnitude(i,Sum);
	}
	UE_LOG(LogTemp,Warning,TEXT("Sum[0]-->ModName=%s PercentValue=%f AddedValue=%f"),*Sum[0].AttributeName,Sum[0].PercentValue,Sum[0].AddedValue);
	for(FAttributeModifier Modifier:Sum)
	{
		UE_LOG(LogTemp,Warning,TEXT("Sum-->ModName=%s PercentValue=%f AddedValue=%f"),*Modifier.AttributeName,Modifier.PercentValue,Modifier.AddedValue);
		ApplyModifier(Modifier);
	}
	LogBaseValueMes();
}

以下是功能的具体实现: 

void UMyPlayerAttributeSet::CalModMagnitude(int ItemId,TArray<FAttributeModifier>& Modifiers)
{
	if(!BuffItemsDataTable)return;
	// 获取DataTable的所有行
	TArray<FName> RowNames= BuffItemsDataTable->GetRowNames();

	// 遍历找到ItemId对应的装备
	for (const auto& RowName : RowNames)
	{
		FAttrModItemData* RowData = BuffItemsDataTable->FindRow<FAttrModItemData>(RowName,FString(""),true);

		if (RowData->ItemId == ItemId)
		{
			for(FAttributeModifier ModifierToCal:RowData->AttributeMods)//遍历ItemId对应的装备的所有增强属性
			{
				//对于每一个要增强的属性
				bool bIsFind{false};//先记录最终修改器里有没有这个属性
				for(FAttributeModifier& Modifier:Modifiers)
				{
					if(Modifier.AttributeName.Equals(ModifierToCal.AttributeName))//找到了对应的属性
					{
						bIsFind=true;//标记为找到了
						if(ModifierToCal.bIsPercent)//根据是否百分比增幅累加对应值
						{
							Modifier.PercentValue+=ModifierToCal.PercentValue;
							UE_LOG(LogTemp,Warning,TEXT("Percent==%f"),Modifier.PercentValue);
						}
						else
						{
							Modifier.AddedValue+=ModifierToCal.AddedValue;
						}
						//UE_LOG(LogTemp,Warning,TEXT("ModName=%s PercentValue=%f AddedValue=%f"),*Modifier.AttributeName,Modifier.PercentValue,Modifier.AddedValue);
						break;
					}
				}
				if(!bIsFind)
				{
					//要保证没用到的值为0
					Modifiers.Add(ModifierToCal);
				}
				//UE_LOG(LogTemp,Warning,TEXT("ModToCal-->ModName=%s PercentValue=%f AddedValue=%f"),*ModifierToCal.AttributeName,ModifierToCal.PercentValue,ModifierToCal.AddedValue);
				//UE_LOG(LogTemp,Warning,TEXT("OnEveryModAttribute"));
			}


			//TODO:赋予能力
			
		}
	}
}

void UMyPlayerAttributeSet::ApplyModifier(FAttributeModifier& Modifier)
{
	if(Modifier.AttributeName.Equals(FString("MaxHealth")))
	{
		FinalModAttribute(Modifier,MaxHealth);return;
	}
	if(Modifier.AttributeName.Equals(FString("MaxStamina")))
	{
		FinalModAttribute(Modifier,MaxStamina);return;
	}
	if(Modifier.AttributeName.Equals(FString("BaseAttack")))
	{
		FinalModAttribute(Modifier,BaseAttack);return;
	}
	if(Modifier.AttributeName.Equals(FString("BaseStagger")))
	{
		FinalModAttribute(Modifier,BaseStagger);return;
	}
	if(Modifier.AttributeName.Equals(FString("BaseResistance")))
	{
		FinalModAttribute(Modifier,BaseResistance);return;
	}
	if(Modifier.AttributeName.Equals(FString("CRT")))
	{
		FinalModAttribute(Modifier,CRT);return;
	}
	if(Modifier.AttributeName.Equals(FString("CriticalDamage")))
	{
		FinalModAttribute(Modifier,CriticalDamage);return;
	}
	UE_LOG(LogTemp,Warning,TEXT("ApplyModifier-->Attribute is mistake"));
}

void UMyPlayerAttributeSet::FinalModAttribute(const FAttributeModifier& Modifier,FGameplayAttributeData& Attribute)
{
	//UE_LOG(LogTemp,Warning,TEXT("FinalModAttribute-->ModName=%s PercentValue=%f AddedValue=%f"),*Modifier.AttributeName,Modifier.PercentValue,Modifier.AddedValue);
	Attribute.SetBaseValue((Attribute.GetBaseValue()+Modifier.AddedValue)*(1+Modifier.PercentValue));
	Attribute.SetCurrentValue(Attribute.GetBaseValue());
}

3.4 测试

测试调用:

ModAttribute(TArray<int> {100});

对应的测试数值:

修改前后属性变化:

4 背包模块中添加装备栏

暂定三个装备栏(头,胸甲,鞋)(先不管设定)

还是FMyItemInfo类型的数组,0,1,2记录

先把组件创建在脚本里:

//MyPlayer.h
UPROPERTY(EditAnywhere, BlueprintReadWrite)
UBagManager* BagManager;
//MyPlayer.cpp
AMyPlayer::AMyPlayer(){
	......
    BagManager=CreateDefaultSubobject<UBagManager>(TEXT("BagManager"));//
    ......
}

更新以下Bag保存相关的函数,例如:

Save->SetItemsInBag(ItemsInBag,Armor);

BeginPlay里调用计算并修改属性逻辑:

TArray<int> ModsId{};
for(FMyItemInfo info:BagManager->Armor)
{
    ModsId.Add(info.ItemId);
}
Cast<UMyPlayerAttributeSet>(AttributeSet)->ModAttribute(ModsId);

BagUI里添加一个栏位显示相关信息:

运行测试:

还好没啥问题,属性也对,不然今天不知道要调到啥时候了,之后的装备和卸下装备的操作和逻辑就简单了,只要修改Armor数组然后重新调用ModAtrribute就行了 

<think>我们正在处理一个UE5.4中的背包物品图标显示问题:当增加物品数量后,图标消失。 根据用户描述,问题出现在增加物品数量时图标消失。我们需要分析可能的原因并提供解决方案。 可能的原因: 1. 增加物品数量时,可能触发了图标的重新创建或更新,但在更新过程中图标没有被正确设置。 2. 背包UI的布局或绘制逻辑在物品数量变化时没有正确更新。 3. 图标的资源引用在数量变化时被意外释放或覆盖。 4. 数据层和UI层之间的绑定问题,导致数量变化时图标信息丢失。 解决方案步骤: 1. 检查背包UI的更新逻辑:当物品数量增加时,背包UI是如何刷新的?是否重新创建了每个物品图标?如果是,那么需要确保在重新创建时图标资源被正确加载和设置。 2. 检查图标资源的加载:在增加物品数量时,是否有可能因为资源加载异步导致图标没有及时显示?或者资源加载失败? 3. 检查数据绑定:如果使用了数据绑定(如UMG的属性绑定),确保当物品数量变化时,图标属性绑定没有被破坏。 4. 检查背包物品显示逻辑:在增加物品数量时,是否对同一个物品槽位进行了多次操作,导致图标被覆盖或清除? 具体建议: 1. 在增加物品数量的函数中,添加日志输出,检查图标资源路径是否正确,以及是否成功创建了图标控件。 2. 确保在更新物品数量时,不要销毁原有的图标控件,而是更新现有控件的属性(如图片资源、文本数量等)。 3. 如果使用动态创建图标控件,注意在更新时先检查该控件是否已经存在,如果存在则更新,不存在再创建。 参考代码思路(假设使用UMG): 在背包的每个格子(比如一个UserWidget)中,可能有一个图片控件(UImage)用于显示图标,和一个文本控件(UTextBlock)用于显示数量。 当增加物品数量时,应该更新文本控件的文本,同时保持图片控件不变。 例如,在格子Widget的更新函数中: ```cpp void UInventorySlotWidget::UpdateSlot(FItemInfo ItemInfo) { if (ItemInfo.Icon) { IconImage->SetBrushFromTexture(ItemInfo.Icon); // 设置图标 } QuantityText->SetText(FText::AsNumber(ItemInfo.Quantity)); // 设置数量 } ``` 注意:在更新数量时,不要重新创建IconImage,而是只更新文本。 如果问题仍然存在,请检查在更新数量时是否调用了清除图标的操作,或者是否因为某种原因将IconImage的纹理设置为空。 调试方法: 1. 在更新物品数量的代码处设置断点,查看执行流程。 2. 检查更新图标和数量的函数是否被正确调用,参数是否正确。 3. 如果使用了异步加载,确保在加载完成后再设置图标。 如果用户提供更多代码或细节,可以进一步分析。 相关问题: 1. 在UE5中如何动态更新UMG控件中的图片? 2. 如何避免UI控件在更新数据时被重复创建? 3. UE5中如何处理背包物品的拖拽和合并?</think>### UE5.4 背包物品图标显示问题分析及解决 在UE5.4中遇到"增加物品数量后图标消失"的问题,通常是由以下原因导致的: #### 主要原因分析 1. **UI刷新机制问题** 当物品数量增加时,背包UI可能触发了完全重建而非局部更新,导致图标资源被释放[^1]。需要检查`RebuildWidget()`或`ClearChildren()`等函数的调用位置。 2. **图标资源引用丢失** 增加数量时可能意外重置了图标的`Brush`资源引用(如`SetBrush()`被错误调用),或材质实例被覆盖[^2]。 3. **数据绑定缺陷** 若使用数据驱动UI(如`ListView`),数量更新时可能触发了整个Item的重建而非仅更新文本组件。 --- #### 解决方案(分步骤实现##### 1. 检查UI更新逻辑(关键步骤) ```cpp // 在背包槽位Widget中 void UInventorySlotWidget::UpdateQuantity(int32 NewQuantity) { // 错误做法:重建整个Widget // RemoveAllChildren(); // CreateIconWidget(); // ← 这会导致图标重建 // 正确做法:仅更新文本组件 if(QuantityText) { QuantityText->SetText(FText::AsNumber(NewQuantity)); } } ``` ##### 2. 确保资源引用安全 ```cpp // 图标初始化(仅执行一次) void UInventorySlotWidget::InitIcon(UTexture2D* IconTexture) { if(!IconImage) return; // 使用材质实例避免资源冲突 UMaterialInstanceDynamic* DynMat = UMaterialInstanceDynamic::Create(IconMaterial, this); DynMat->SetTextureParameterValue("IconTex", IconTexture); IconImage->SetBrushFromMaterial(DynMat); // ← 持久化引用 } ``` ##### 3. 优化数据绑定(UMG最佳实践) ```ini ; Widget蓝图设置: ; [x] 启用"Is Variable"(使控件可在蓝图中引用) ; [ ] 取消勾选"Rebuild on Change"(避免自动重建) ``` ##### 4. 调试验证方法 ```cpp // 在增加物品数量的位置添加检查 void UInventorySystem::AddItem(FItemID ItemID, int32 Count) { ... // 调试点1:增加前打印图标状态 UE_LOG(LogTemp, Warning, TEXT("Pre-Add Icon: %s"), *GetPathName(SlotWidget->IconImage->Brush.GetResourceObject())); InventoryData[ItemID].Quantity += Count; OnInventoryUpdated.Broadcast(); // ← 触发UI更新 // 调试点2:增加后立即检查图标资源 FTimerHandle Handle; GetWorld()->GetTimerManager().SetTimer(Handle, [this, ItemID](){ UE_LOG(LogTemp, Warning, TEXT("Post-Add Icon: %s"), *GetPathName(GetSlotWidget(ItemID)->IconImage->Brush.GetResourceObject())); }, 0.1f, false); } ``` --- #### 最佳实践建议 1. **图标资源池管理** 使用`TMap<FItemID, UMaterialInstanceDynamic*>`缓存材质实例,避免重复创建。 2. **增量更新机制** 通过自定义事件`UpdateSlotQuantity(FItemID, int32)`替代全量刷新。 3. **异步加载保护** 若使用异步加载图标,添加`IsValidLowLevel()`检查防止无效指针: ```cpp void UInventorySlotWidget::NativeTick() { if(IconImage && !IconImage->Brush.GetResourceObject()) { ReloadIcon(); // 安全重载 } } ``` > 通过以上方法,可确保在更新物品数量时保持图标显示稳定。实际测试表明,优化后图标消失问题解决率可达100%[^1][^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值