添加背包配置中的背包预览UI

This commit is contained in:
997146918 2025-08-31 15:36:48 +08:00
parent faddbbe338
commit 1aeaa662a9
4 changed files with 324 additions and 143 deletions

View File

@ -6,6 +6,8 @@
#include "PropertyCustomizationHelpers.h" #include "PropertyCustomizationHelpers.h"
#include "Framework/Notifications/NotificationManager.h" #include "Framework/Notifications/NotificationManager.h"
#include "ProjectFish/DataAsset/BagShapeAsset.h" #include "ProjectFish/DataAsset/BagShapeAsset.h"
#include "Widgets/BagConfigGridWidget.h"
#include "Widgets/BagShapeGridWidget.h"
#include "Widgets/Notifications/SNotificationList.h" #include "Widgets/Notifications/SNotificationList.h"
@ -20,7 +22,7 @@ void SBagConfigEditorWidget::Construct(const FArguments& InArgs)
[ [
SNew(SVerticalBox) SNew(SVerticalBox)
// BagShapeAsset 选择器 // 背包选择器
+ SVerticalBox::Slot() + SVerticalBox::Slot()
.AutoHeight() .AutoHeight()
.Padding(5.0f) .Padding(5.0f)
@ -88,140 +90,191 @@ void SBagConfigEditorWidget::Construct(const FArguments& InArgs)
] ]
] ]
// // 工具栏 // 技能按钮
// + SVerticalBox::Slot() + SVerticalBox::Slot()
// .AutoHeight() .AutoHeight()
// .Padding(5.0f) .Padding(5.0f)
// [ [
// SNew(SHorizontalBox) SNew(SHorizontalBox)
//
// + SHorizontalBox::Slot() + SHorizontalBox::Slot()
// .AutoWidth() .AutoWidth()
// .Padding(2.0f) .Padding(2.0f)
// [ [
// SNew(SButton) SNew(SButton)
// .Text(FText::FromString(TEXT("Add Skill"))) .Text(FText::FromString(TEXT("点击后,单击背包添加当前选中的技能")))
// .OnClicked(this, &SBagClassEditor::OnAddSkillClicked) .OnClicked(this, &SBagConfigEditorWidget::OnAddSkillClicked)
// .IsEnabled_Lambda([this]() { return BagClass.IsValid() && BagClass->BagShapeAsset != nullptr && SelectedSkillObject.IsValid(); }) .IsEnabled_Lambda([this]() { return BagConfig.IsValid() && BagConfig->BagShapeAsset != nullptr && SelectedSkillAsset.IsValid(); })
// ] ]
//
// + SHorizontalBox::Slot() + SHorizontalBox::Slot()
// .AutoWidth() .AutoWidth()
// .Padding(2.0f) .Padding(2.0f)
// [ [
// SNew(SButton) SNew(SButton)
// .Text(FText::FromString(TEXT("Remove Selected"))) .Text(FText::FromString(TEXT("点击后,单击背包移除当前选中的技能")))
// .OnClicked(this, &SBagClassEditor::OnRemoveSkillClicked) .OnClicked(this, &SBagConfigEditorWidget::OnRemoveSkillClicked)
// .IsEnabled_Lambda([this]() { return SelectedSkillObject.IsValid(); }) .IsEnabled_Lambda([this]() { return SelectedSkillAsset.IsValid(); })
// ] ]
//
// + SHorizontalBox::Slot() + SHorizontalBox::Slot()
// .AutoWidth() .AutoWidth()
// .Padding(2.0f) .Padding(2.0f)
// [ [
// SNew(SButton) SNew(SButton)
// .Text(FText::FromString(TEXT("Clear All"))) .Text(FText::FromString(TEXT("移除所有技能")))
// .OnClicked(this, &SBagClassEditor::OnClearAllSkillsClicked) .OnClicked(this, &SBagConfigEditorWidget::OnClearAllSkillsClicked)
// .IsEnabled_Lambda([this]() { return BagClass.IsValid() && BagClass->GetSkillCount() > 0; }) .IsEnabled_Lambda([this]() { return BagConfig.IsValid() && BagConfig->GetSkillCount() > 0; })
// ] ]
// ] ]
//
// // 主编辑区域 // 主编辑区域
// + SVerticalBox::Slot() + SVerticalBox::Slot()
// .FillHeight(1.0f) .FillHeight(1.0f)
// [ [
// SNew(SSplitter) SNew(SSplitter)
// .Orientation(Orient_Horizontal) .Orientation(Orient_Horizontal)
//
// // 左侧:背包格子视图 // 左侧:背包格子视图
// + SSplitter::Slot() + SSplitter::Slot()
// .Value(0.7f) .Value(0.7f)
// [ [
// SNew(SBox) SNew(SBox)
// .HAlign(HAlign_Center) .HAlign(HAlign_Center)
// .VAlign(VAlign_Center) .VAlign(VAlign_Center)
// .Padding(10.0f) .Padding(10.0f)
// [ [
// SNew(SVerticalBox) SNew(SVerticalBox)
//
// + SVerticalBox::Slot() + SVerticalBox::Slot()
// .AutoHeight() .AutoHeight()
// .HAlign(HAlign_Center) .HAlign(HAlign_Center)
// .Padding(0, 0, 0, 10) .Padding(0, 0, 0, 10)
// [ [
// SNew(STextBlock) SNew(STextBlock)
// .Text_Lambda([this]() .Text_Lambda([this]()
// { {
// if (!BagClass.IsValid()) if (!BagConfig.IsValid())
// { {
// return FText::FromString(TEXT("No Bag Class")); return FText::FromString(TEXT("No Bag Class"));
// } }
// else if (!BagClass->BagShapeAsset) else if (!BagConfig->BagShapeAsset)
// { {
// return FText::FromString(TEXT("Please select a Bag Shape Asset above")); return FText::FromString(TEXT("Please select a Bag Shape Asset above"));
// } }
// else else
// { {
// return FText::FromString(FString::Printf(TEXT("Bag: %s (%dx%d)"), return FText::FromString(FString::Printf(TEXT("Bag: %s (%dx%d)"),
// *BagClass->BagShapeAsset->GetName(), *BagConfig->BagShapeAsset->GetName(),
// BagClass->BagShapeAsset->BagWidth, BagConfig->BagShapeAsset->BagWidth,
// BagClass->BagShapeAsset->BagHeight)); BagConfig->BagShapeAsset->BagHeight));
// } }
// }) })
// .Font(FCoreStyle::GetDefaultFontStyle("Bold", 12)) .Font(FCoreStyle::GetDefaultFontStyle("Bold", 12))
// .ColorAndOpacity_Lambda([this]() .ColorAndOpacity_Lambda([this]()
// { {
// return (!BagClass.IsValid() || !BagClass->BagShapeAsset) ? return (!BagConfig.IsValid() || !BagConfig->BagShapeAsset) ?
// FSlateColor(FLinearColor::Red) : FSlateColor(FLinearColor::White); FSlateColor(FLinearColor::Red) : FSlateColor(FLinearColor::White);
// }) })
// ] ]
//
// + SVerticalBox::Slot() + SVerticalBox::Slot()
// .AutoHeight() .AutoHeight()
// .HAlign(HAlign_Center) .HAlign(HAlign_Center)
// [ [
// SAssignNew(BagGridWidget, SBagGridWidget) SAssignNew(BagGridWidget, SBagConfigGridWidget)
// .BagClass(BagClass.Get()) .BagConfig(BagConfig.Get())
// .OnGridCellClicked(this, &SBagClassEditor::OnGridCellClicked) .OnGridCellClicked(this, &SBagConfigEditorWidget::OnGridCellClicked)
// .Visibility_Lambda([this]() .Visibility_Lambda([this]()
// { {
// return (BagClass.IsValid() && BagClass->BagShapeAsset) ? return (BagConfig.IsValid() && BagConfig->BagShapeAsset) ?
// EVisibility::Visible : EVisibility::Hidden; EVisibility::Visible : EVisibility::Hidden;
// }) })
// ] ]
// ] ]
// ] ]
//
// // 右侧:技能列表 // // 右侧:技能列表
// + SSplitter::Slot() // + SSplitter::Slot()
// .Value(0.3f) // .Value(0.3f)
// [ // [
// SNew(SVerticalBox) // SNew(SVerticalBox)
// //
// + SVerticalBox::Slot() // + SVerticalBox::Slot()
// .AutoHeight() // .AutoHeight()
// .Padding(5.0f) // .Padding(5.0f)
// [ // [
// SNew(STextBlock) // SNew(STextBlock)
// .Text(FText::FromString(TEXT("Placed Skills:"))) // .Text(FText::FromString(TEXT("Placed Skills:")))
// .Font(FCoreStyle::GetDefaultFontStyle("Bold", 12)) // .Font(FCoreStyle::GetDefaultFontStyle("Bold", 12))
// ] // ]
// //
// + SVerticalBox::Slot() // + SVerticalBox::Slot()
// .FillHeight(1.0f) // .FillHeight(1.0f)
// .Padding(5.0f) // .Padding(5.0f)
// [ // [
// SAssignNew(SkillListWidget, SSkillListWidget) // SAssignNew(SkillListWidget, SSkillListWidget)
// .BagClass(BagClass.Get()) // .BagClass(BagClass.Get())
// .OnSkillSelected(this, &SBagClassEditor::OnSkillSelected) // .OnSkillSelected(this, &SBagClassEditor::OnSkillSelected)
// ] // ]
// ] // ]
//] ]
]; ];
RefreshUI(); RefreshUI();
} }
FReply SBagConfigEditorWidget::OnAddSkillClicked()
{
if (!BagConfig.IsValid() || !BagConfig->BagShapeAsset || !SelectedSkillAsset.IsValid())
{
ShowWarningMessage(TEXT("Please select a bag shape asset and a skill object first!"));
return FReply::Handled();
}
// 进入放置模式
bIsPlacingSkill = true;
ShowWarningMessage(TEXT("Click on the grid to place the skill"));
return FReply::Handled();
}
FReply SBagConfigEditorWidget::OnRemoveSkillClicked()
{
if (!BagConfig.IsValid() || SelectedSkillIndex < 0)
{
return FReply::Handled();
}
if (BagConfig->RemoveSkill(SelectedSkillIndex))
{
SelectedSkillIndex = -1;
RefreshUI();
// 标记资源为已修改
BagConfig->MarkPackageDirty();
}
return FReply::Handled();
}
FReply SBagConfigEditorWidget::OnClearAllSkillsClicked()
{
if (!BagConfig.IsValid())
{
return FReply::Handled();
}
BagConfig->ClearAllSkills();
SelectedSkillIndex = -1;
RefreshUI();
// 标记资源为已修改
BagConfig->MarkPackageDirty();
return FReply::Handled();
}
void SBagConfigEditorWidget::OnBagShapeAssetChanged(const FAssetData& AssetData) void SBagConfigEditorWidget::OnBagShapeAssetChanged(const FAssetData& AssetData)
{ {
if (!BagConfig.IsValid()) if (!BagConfig.IsValid())
@ -267,13 +320,58 @@ void SBagConfigEditorWidget::OnSkillAssetChanged(const FAssetData& AssetData)
SelectedSkillAsset = Cast<USkillAsset>(AssetData.GetAsset()); SelectedSkillAsset = Cast<USkillAsset>(AssetData.GetAsset());
} }
void SBagConfigEditorWidget::OnGridCellClicked(int32 X, int32 Y)
{
if (!BagConfig.IsValid())
{
return;
}
if (bIsPlacingSkill && SelectedSkillAsset.IsValid())
{
// 尝试在指定位置放置技能
if (BagConfig->CanPlaceSkill(SelectedSkillAsset.Get(), X, Y))
{
if (BagConfig->AddSkill(SelectedSkillAsset.Get(), X, Y))
{
BagConfig->MarkPackageDirty();
RefreshUI();
ShowWarningMessage(FString::Printf(TEXT("Successfully placed skill '%s' at (%d,%d)"),
*SelectedSkillAsset->SkillName.ToString(), X, Y));
// 退出放置模式
bIsPlacingSkill = false;
}
else
{
ShowWarningMessage(TEXT("Failed to place skill!"));
}
}
else
{
ShowWarningMessage(TEXT("Cannot place skill at this position!"));
}
}
else
{
// // 选择现有技能
// int32 SkillIndex = BagConfig->GetSkillAtPosition(X, Y);
// SelectedSkillIndex = SkillIndex;
//
// if (SkillListWidget.IsValid())
// {
// SkillListWidget->SetSelectedSkill(SkillIndex);
// }
}
}
void SBagConfigEditorWidget::RefreshUI() void SBagConfigEditorWidget::RefreshUI()
{ {
// if (BagGridWidget.IsValid()) if (BagGridWidget.IsValid())
// { {
// BagGridWidget->RefreshGrid(); BagGridWidget->RefreshGrid();
// } }
//
// if (SkillListWidget.IsValid()) // if (SkillListWidget.IsValid())
// { // {
// SkillListWidget->RefreshSkillList(); // SkillListWidget->RefreshSkillList();

View File

@ -0,0 +1,14 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Widgets/BagConfigGridWidget.h"
void SBagConfigGridWidget::Construct(const FArguments& InArgs)
{
}
void SBagConfigGridWidget::RefreshGrid()
{
}

View File

@ -34,13 +34,13 @@ private:
int32 SelectedSkillIndex; int32 SelectedSkillIndex;
// UI组件 // UI组件
TSharedPtr<class SBagShapeGridWidget> BagGridWidget; TSharedPtr<class SBagConfigGridWidget> BagGridWidget;
//TSharedPtr<class SSkillListWidget> SkillListWidget; //TSharedPtr<class SSkillListWidget> SkillListWidget;
// // 回调函数 // 回调函数
// FReply OnAddSkillClicked(); FReply OnAddSkillClicked();
// FReply OnRemoveSkillClicked(); FReply OnRemoveSkillClicked();
// FReply OnClearAllSkillsClicked(); FReply OnClearAllSkillsClicked();
// BagShapeAsset选择回调 // BagShapeAsset选择回调
void OnBagShapeAssetChanged(const FAssetData& AssetData); void OnBagShapeAssetChanged(const FAssetData& AssetData);
@ -51,8 +51,8 @@ private:
// // 技能选择回调 // // 技能选择回调
// void OnSkillSelected(int32 SkillIndex); // void OnSkillSelected(int32 SkillIndex);
// //
// // 格子点击回调 // 格子点击回调
// void OnGridCellClicked(int32 X, int32 Y); void OnGridCellClicked(int32 X, int32 Y);
// 刷新UI // 刷新UI
void RefreshUI(); void RefreshUI();
@ -62,3 +62,7 @@ private:
}; };

View File

@ -0,0 +1,65 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "ProjectFish/DataAsset/BagConfigAsset.h"
DECLARE_DELEGATE_TwoParams(FOnGridCellClicked, int32, int32);
/**
*
*/
class PROJECTFISHEDITOR_API SBagConfigGridWidget : public SCompoundWidget
{
public:
SLATE_BEGIN_ARGS(SBagConfigGridWidget) {}
SLATE_ARGUMENT(UBagConfigAsset*, BagConfig)
SLATE_EVENT(FOnGridCellClicked, OnGridCellClicked)
SLATE_END_ARGS()
void Construct(const FArguments& InArgs);
// // 设置背包类
// void UpdateBagConfig(UBagConfigAsset* InBagClass);
//
// // 设置预览技能(用于显示放置预览)
// void SetPreviewSkill(USkillAsset* InPreviewSkill, int32 PreviewX, int32 PreviewY);
//
// // 清除预览
// void ClearPreview();
//
// 刷新显示
void RefreshGrid();
protected:
// // Slate重写函数
// virtual FReply OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
// virtual FReply OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
// virtual int32 OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect,
// FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const override;
//
// virtual FVector2D ComputeDesiredSize(float) const override;
private:
// 背包类引用
TWeakObjectPtr<UBagConfigAsset> BagClass;
// 回调委托
FOnGridCellClicked OnGridCellClicked;
// 预览数据
TWeakObjectPtr<USkillAsset> PreviewSkill;
int32 PreviewX;
int32 PreviewY;
// UI参数
static const float CellSize;
static const float CellSpacing;
// // 工具函数
// FVector2D GetCellPosition(int32 X, int32 Y) const;
// FVector2D GetGridCellFromPosition(const FVector2D& Position) const;
// bool IsValidGridPosition(int32 X, int32 Y) const;
// FLinearColor GetCellColor(int32 X, int32 Y) const;
// int32 GetSkillAtPosition(int32 X, int32 Y) const;
};