3.策划配置表-DataTable

本文详细介绍了Unity游戏引擎中使用GameFramework的策划配置表DataTable,包括介绍、类介绍、配置表生成方法以及数据表解析流程。作者提供了一个工具,能够将txt配置转化为.bytes配置并自动生成.cs读取代码。内容还涉及XR平台的配置表功能和Unity菜单的导表功能,解释了FileSystem和NotFileSystem的区别。

GameFramework策划配置表DataTable

介绍

这个就是策划用 excel 编辑的配置表导出来给程序用的

类介绍

DataTableComponent
DataTableManager
管理所有配置表,提供创建表,删除表
DataTableBase
对应excel的 sheet,存储1张数据表的内容
按行存取,数据读取和解析委托给 DataProvider<DataTableBase>
DataProvider<DataTableBase>
加载和解析整张配置表
这里没有按行解析
加载委托 IResourceManager
解析委托给 IDataProviderHelper<DataTableBase>
IDataProviderHelper<DataTableBase>
数据表解析过程
DataTableHelperBase
DefaultDataTableHelper
默认实现,解析整个配置并传给 DataTableBase 解析行
DataTable<IDataRow>
默认数据表实现
按行解析并存储配置表
IDatatable<IDataRow>
提供给外部使用
IDataRow
数据表行类型,用来解析并存储一行
DataRowBase
数据表行类型基类
MyDataRow
你的数据表行类型

生成配置表

  • 作者提供了一个用 txt 配置生成 .bytes 配置,并自动生成 .cs 读取代码的工具
    这个工具在 StarForce 示例的目录 Assets/GameMain/Scripts/Editor/DataTableGenerator
    编辑excel表时直接保存成 .txt 文件,然后就可以用示例工具生成 .bytes 和 .cs 读取类
    可以拿来改改用,比如 XR平台 增加了直接解析 .xlsx 配置表
    最好的可能是直接集成 luban 导表工具

  • XR平台的配置表功能说明
    DataTableConfig 中定义了相关的路径
    ExcelsFolder = $“{Application.dataPath}/…/Excels/”; excel表源文件路径
    DataTableFolderPath = “Assets/[Resources]/DataTables”; bytes表生成路径
    CSharpCodePath = “Assets/Scripts/Logic/DataTable”; cs 类生成路径
    CSharpCodeTemplateFileName = “Assets/[Resources]/Configs/DataTableCodeTemplate.txt”; cs 类模板文件
    NameSpace = “MiGuFramework.Runtime”; cs 类命名空间
    ExtensionDirectoryPath = “Assets/Scripts/Logic/DataTable/Extensions”; 模板数据类型读写扩展类生成路径

    unity菜单 GameFramework->DataTable->GenerateDataTables 定义了相关导表功能
    FileSystem 和 NotFileSystem 的区别是:
    使用带 FileSystem 会在文件头存储每一行的id和对应的起始位置和长度
    也就是你可以快速定位某行数据,而不用先全部加载
    Excel To Txt:
    excel 导成 txt

  • XR平台导表
    在 Excels 目录下配置表
    运行

使用

	//	定义行类型(一般是导表的时候自动生成,默认加 DR 前缀) 
	class DRCsvRow : DataRowBase 
	{
		//	定义行存储的数据
		string name;
		int age;

		//	重载如何解析行数据
		public override bool ParseDataRow(string dataRowString, object userData)
        {
			string[] fields = dataRowString.Split(',');
			name = fields[0];
			age = int.Parse(fields[1]);
		}
	}

	DataTableComponent tableComponent = GameEntry.GetComponent<DataTableComponent>();
	//	创建表
	IDataTable<DRCsvRow> table = tableComponent.CreateDataTable<DRCsvRow>("TableName");
	//	读取表
	table.ReadData("/a/b.bytes");
	//	使用表
	IDataTable<DRCsvRow> table = tableComponent.GetDataTable<DRCsvRow>();
	DRCsvRow[] rows = table.GetAllDataRows();
	DRCsvRow row = table.GetDataRow(x=>x.id==5);

数据表解析流程

	DataTableBase.ReadData(string dataTableAssetName)
	{
		DataProvider<DataTableBase>.ReadData(string dataAssetName)
		{
			//	加载资源
			m_ResourceManager.LoadAsset(dataAssetName, priority, m_LoadAssetCallbacks, userData)
			{
				DataProvider<DataTableBase>.LoadAssetSuccessCallback(string dataAssetName, object dataAsset, float duration, object userData)
				{
					//	解析配置
					DefaultDataTableHelper.ReadData(m_Owner, dataAssetName, dataAsset, userData)
					{
						DataTableBase.ParseData(byte[] dataTableBytes, object userData)
						{
							DataProvider<DataTableBase>.ParseData(byte[] dataBytes, int startIndex, int length, object userData)
							{
								DefaultDataTableHelper.ParseData(DataTableBase dataTable, string dataTableString, object userData)
								{
									//	读取每行数据,然后添加
									while ((dataRowString = dataTableString.ReadLine(ref position)) != null)
									{
										//	# 做为注释符
										if (dataRowString[0] == '#')
											continue;
										
										dataTable.AddDataRow(dataRowString, userData)
										{
											DataTable<T>.AddDataRow(string dataRowString, object userData)
											{
												//	由行类型来解析行数据
												T dataRow = new T();
												dataRow.ParseDataRow(dataRowString, userData);
												
												//	保存行
												InternalAddDataRow(dataRow);
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}
<think> 根据用户需求,我们需要在UE5编辑器中创建一个自定义Slate窗口,分为左右两部分: 左侧:小地图(支持放大/缩小/移动,超出边框自动裁剪) 右侧:格(头包括复选框、类型、数量、显示颜色),格数据从DataTable读取(DataTable行结构继承自定义结构) 我们将分步骤实现: 1. 定义数据结构(包括DataTable行结构) 2. 创建小地图控件(SMinimapWidget) 3. 创建主窗口(SMySlateWindow),包含左右分割布局 4. 实现格视图,读取DataTable数据 5. 实现复选框交互:当选中某行时,在小地图上绘制对应的Actor位置 注意:由于代码量较大,我们将分文件展示关键代码。 步骤1:定义数据结构(MyDataTableRow.h) 我们定义一个基结构FMyBaseStruct(包含复选框、类型、数量、Actor引用、显示名称和颜色),然后定义DataTable使用的行结构FMyDataTableRow继承它。 步骤2:小地图控件(SMinimapWidget) 我们将创建一个自定义Slate控件,实现以下功能: - 绘制地图背景(这里用简单矩形代替,实际项目可替换为地图纹理) - 处理鼠标事件:滚轮缩放、拖拽移动 - 绘制标记点(根据选中的Actor位置) 步骤3:主窗口(SMySlateWindow) - 使用SSplitter分割左右两部分 - 左侧:放置小地图控件(在Border内,实现裁剪) - 右侧:创建格视图(SListView),头包含四列(复选框、类型、数量、显示颜色) -DataTable加载数据,并绑定到格 步骤4:交互 - 当用户勾选某行的复选框时,将该行对应的Actor位置绘制在小地图上(通过小地图控件的接口添加标记) - 小地图需要能够同时显示多个标记点,并且随着缩放和移动而正确变换位置 由于代码较长,我们将分文件展示。注意:以下代码需要放在相应文件中。 文件1: MyDataTableRow.h (在项目的Public或Private目录下,根据模块设置) </think> 下面是在UE5编辑器中创建Slate窗口的完整实现代码,包含左侧可缩放/移动的小地图和右侧DataTable驱动的格,支持选中行显示Actor位置: ### 1. 数据结构定义 (MyDataTableRow.h) ```cpp #pragma once #include "Engine/DataTable.h" #include "Engine/Texture2D.h" #include "MyDataTableRow.generated.h" USTRUCT(BlueprintType) struct FMyBaseStruct : public FTableRowBase { GENERATED_BODY() // 勾选状态 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Table") bool bChecked = false; // 类型 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Table") FString Type; // 数量 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Table") int32 Quantity = 0; // 关联的Actor UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Table") TSoftObjectPtr<AActor> LinkedActor; // 显示名称 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Table") FString DisplayName; // 显示颜色 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Table") FLinearColor DisplayColor = FLinearColor::White; }; // 蓝图可继承的数据行 USTRUCT(BlueprintType) struct FMyDataTableRow : public FMyBaseStruct { GENERATED_BODY() // 策划可在此添加自定义字段 }; ``` ### 2. 小地图控件实现 (SMinimapWidget.h) ```cpp #pragma once #include "CoreMinimal.h" #include "Widgets/SCompoundWidget.h" class SMinimapWidget : public SCompoundWidget { public: SLATE_BEGIN_ARGS(SMinimapWidget) {} SLATE_ARGUMENT(TArray<AActor*>, TrackedActors) // 要跟踪的Actor SLATE_END_ARGS() void Construct(const FArguments& InArgs); // 更新跟踪的Actor void UpdateTrackedActors(const TArray<AActor*>& NewActors); // 鼠标事件处理 virtual FReply OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; virtual FReply OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; virtual FReply OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; virtual FReply OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; private: // 绘制函数 int32 OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const; // 坐标转换函数 FVector2D WorldToMinimap(const FVector& WorldLocation) const; // 小地图参数 FVector2D ViewOffset = FVector2D::ZeroVector; float ZoomLevel = 1.0f; const float MinZoom = 0.5f; const float MaxZoom = 3.0f; bool bIsDragging = false; FVector2D DragStartPosition; FVector2D MapSize = FVector2D(1000, 1000); // 地图实际尺寸 // 跟踪的Actor TArray<TWeakObjectPtr<AActor>> TrackedActors; }; ``` ### 3. 小地图实现 (SMinimapWidget.cpp) ```cpp #include "SMinimapWidget.h" #include "Rendering/DrawElements.h" #include "Engine/World.h" void SMinimapWidget::Construct(const FArguments& InArgs) { // 设置默认纹理资源 ChildSlot [ SNew(SBox) .WidthOverride(400) .HeightOverride(400) ]; // 初始化跟踪的Actor TrackedActors.Reserve(InArgs._TrackedActors.Num()); for (AActor* Actor : InArgs._TrackedActors) { TrackedActors.Add(Actor); } } void SMinimapWidget::UpdateTrackedActors(const TArray<AActor*>& NewActors) { TrackedActors.Empty(); for (AActor* Actor : NewActors) { TrackedActors.Add(Actor); } } FReply SMinimapWidget::OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { const float ZoomDelta = 0.2f; const float OldZoom = ZoomLevel; // 计算缩放前后鼠标位置对应的地图位置 const FVector2D WidgetSpacePos = MyGeometry.AbsoluteToLocal(MouseEvent.GetScreenSpacePosition()); const FVector2D MapPosBeforeZoom = (WidgetSpacePos - ViewOffset) / OldZoom; // 更新缩放级别 ZoomLevel = FMath::Clamp(ZoomLevel + MouseEvent.GetWheelDelta() * ZoomDelta, MinZoom, MaxZoom); // 调整视图偏移保持鼠标位置稳定 const FVector2D MapPosAfterZoom = MapPosBeforeZoom * ZoomLevel; ViewOffset += WidgetSpacePos - MapPosAfterZoom - ViewOffset; return FReply::Handled(); } FReply SMinimapWidget::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { if (MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton) { bIsDragging = true; DragStartPosition = MyGeometry.AbsoluteToLocal(MouseEvent.GetScreenSpacePosition()); return FReply::Handled().CaptureMouse(SharedThis(this)); } return FReply::Unhandled(); } FReply SMinimapWidget::OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { if (bIsDragging && HasMouseCapture()) { const FVector2D CurrentPos = MyGeometry.AbsoluteToLocal(MouseEvent.GetScreenSpacePosition()); ViewOffset += CurrentPos - DragStartPosition; DragStartPosition = CurrentPos; // 边界约束 const FVector2D ViewSize = MyGeometry.GetLocalSize() / ZoomLevel; ViewOffset.X = FMath::Clamp(ViewOffset.X, 0.0f, MapSize.X - ViewSize.X); ViewOffset.Y = FMath::Clamp(ViewOffset.Y, 0.0f, MapSize.Y - ViewSize.Y); return FReply::Handled(); } return FReply::Unhandled(); } int32 SMinimapWidget::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const { // 绘制地图背景 const FSlateBrush* MapBrush = FCoreStyle::Get().GetBrush("WhiteBrush"); FSlateDrawElement::MakeBox( OutDrawElements, LayerId++, AllottedGeometry.ToPaintGeometry(MapSize, FSlateLayoutTransform(-ViewOffset * ZoomLevel, ZoomLevel)), MapBrush, ESlateDrawEffect::None, FLinearColor(0.2f, 0.2f, 0.2f) // 深灰色背景 ); // 绘制标记点(根据跟踪的Actor) for (const TWeakObjectPtr<AActor>& ActorPtr : TrackedActors) { if (AActor* Actor = ActorPtr.Get()) { FVector2D MinimapPos = WorldToMinimap(Actor->GetActorLocation()); FLinearColor MarkerColor = FLinearColor::Red; // 默认颜色 // 绘制标记点 FSlateDrawElement::MakeBox( OutDrawElements, LayerId++, AllottedGeometry.ToPaintGeometry( FVector2D(10, 10), FSlateLayoutTransform(MinimapPos - FVector2D(5, 5)) ), MapBrush, ESlateDrawEffect::None, MarkerColor ); // 绘制标记点名称 FSlateDrawElement::MakeText( OutDrawElements, LayerId++, AllottedGeometry.ToPaintGeometry(FVector2D(100, 20), FSlateLayoutTransform(MinimapPos + FVector2D(10, 0))), FText::FromString(Actor->GetActorLabel()), FCoreStyle::Get().GetFontStyle("NormalFont"), ESlateDrawEffect::None, FLinearColor::White ); } } return LayerId; } FVector2D SMinimapWidget::WorldToMinimap(const FVector& WorldLocation) const { // 简化的坐标转换(实际项目需根据场景调整) return FVector2D(WorldLocation.X, WorldLocation.Y) * 0.1f; } ``` ### 4. 主窗口实现 (SMySlateWindow.h) ```cpp #pragma once #include "CoreMinimal.h" #include "Widgets/SCompoundWidget.h" #include "Engine/DataTable.h" #include "MyDataTableRow.h" #include "SMinimapWidget.h" class SMinimapWidget; class SMySlateWindow : public SCompoundWidget { public: SLATE_BEGIN_ARGS(SMySlateWindow) {} SLATE_ARGUMENT(UDataTable*, DataTable) // 传入DataTable SLATE_END_ARGS() void Construct(const FArguments& InArgs); private: // 加载DataTable数据 void LoadDataTable(UDataTable* DataTable); // 格行生成器 TSharedRef<ITableRow> GenerateTableRow(TSharedPtr<FMyBaseStruct> Item, const TSharedRef<STableViewBase>& OwnerTable); // 复选框状态改变回调 void OnCheckStateChanged(ECheckBoxState NewState, TSharedPtr<FMyBaseStruct> Item); // 更新跟踪的Actor void UpdateTrackedActors(); // UI组件 TSharedPtr<SListView<TSharedPtr<FMyBaseStruct>>> TableView; TSharedPtr<SMinimapWidget> MinimapWidget; // 数据源 TArray<TSharedPtr<FMyBaseStruct>> TableItems; UDataTable* SourceDataTable; // 当前选中的Actor TArray<AActor*> TrackedActors; }; ``` ### 5. 主窗口实现 (SMySlateWindow.cpp) ```cpp #include "SMySlateWindow.h" #include "Widgets/Layout/SSplitter.h" #include "Widgets/Layout/SBorder.h" #include "Widgets/Views/SListView.h" #include "Widgets/Input/SCheckBox.h" #include "Widgets/Input/SSpinBox.h" #include "Widgets/Colors/SColorBlock.h" #include "SMinimapWidget.h" #define LOCTEXT_NAMESPACE "MySlateWindow" void SMySlateWindow::Construct(const FArguments& InArgs) { SourceDataTable = InArgs._DataTable; LoadDataTable(SourceDataTable); // 创建小地图 MinimapWidget = SNew(SMinimapWidget) .TrackedActors(TrackedActors); // 创建格视图 TSharedPtr<SHeaderRow> HeaderRow = SNew(SHeaderRow) + SHeaderRow::Column("Check") .DefaultLabel(LOCTEXT("CheckColumn", "✓")) .FixedWidth(30) + SHeaderRow::Column("Type") .DefaultLabel(LOCTEXT("TypeColumn", "类型")) .FillWidth(0.3f) + SHeaderRow::Column("Quantity") .DefaultLabel(LOCTEXT("QuantityColumn", "数量")) .FillWidth(0.2f) + SHeaderRow::Column("Color") .DefaultLabel(LOCTEXT("ColorColumn", "显示颜色")) .FillWidth(0.3f); TableView = SNew(SListView<TSharedPtr<FMyBaseStruct>>) .ListItemsSource(&TableItems) .OnGenerateRow(this, &SMySlateWindow::GenerateTableRow) .HeaderRow(HeaderRow); // 主布局 ChildSlot [ SNew(SSplitter) .Orientation(Orient_Horizontal) // 左侧小地图 + SSplitter::Slot() .Value(0.4f) [ SNew(SBorder) .Padding(5) .BorderImage(FCoreStyle::Get().GetBrush("ToolPanel.GroupBorder")) [ MinimapWidget.ToSharedRef() ] ] // 右侧格 + SSplitter::Slot() .Value(0.6f) [ SNew(SBorder) .Padding(5) .BorderImage(FCoreStyle::Get().GetBrush("ToolPanel.GroupBorder")) [ TableView.ToSharedRef() ] ] ]; } void SMySlateWindow::LoadDataTable(UDataTable* DataTable) { if (!DataTable) return; TableItems.Empty(); // 获取所有行数据 TArray<FMyBaseStruct*> Rows; DataTable->GetAllRows<FMyBaseStruct>(TEXT("SMySlateWindow"), Rows); for (FMyBaseStruct* Row : Rows) { TableItems.Add(MakeShared<FMyBaseStruct>(*Row)); } } TSharedRef<ITableRow> SMySlateWindow::GenerateTableRow( TSharedPtr<FMyBaseStruct> Item, const TSharedRef<STableViewBase>& OwnerTable) { return SNew(STableRow<TSharedPtr<FMyBaseStruct>>, OwnerTable) [ SNew(SHorizontalBox) // 复选框列 + SHorizontalBox::Slot() .AutoWidth() .Padding(5) [ SNew(SCheckBox) .IsChecked(Item->bChecked ? ECheckBoxState::Checked : ECheckBoxState::Unchecked) .OnCheckStateChanged_Lambda([this, Item](ECheckBoxState NewState) { OnCheckStateChanged(NewState, Item); }) ] // 类型列 + SHorizontalBox::Slot() .FillWidth(0.3f) .Padding(5) [ SNew(STextBlock) .Text(FText::FromString(Item->Type)) ] // 数量列 + SHorizontalBox::Slot() .FillWidth(0.2f) .Padding(5) [ SNew(SSpinBox<int32>) .Value(Item->Quantity) .OnValueChanged_Lambda([Item](int32 NewValue) { Item->Quantity = NewValue; }) ] // 颜色列 + SHorizontalBox::Slot() .FillWidth(0.3f) .Padding(5) [ SNew(SColorBlock) .Color(Item->DisplayColor) .Size(FVector2D(20, 20)) ] ]; } void SMySlateWindow::OnCheckStateChanged(ECheckBoxState NewState, TSharedPtr<FMyBaseStruct> Item) { Item->bChecked = (NewState == ECheckBoxState::Checked); // 更新选中的Actor if (Item->bChecked && Item->LinkedActor.IsValid()) { if (AActor* Actor = Item->LinkedActor.Get()) { TrackedActors.AddUnique(Actor); } } else { if (AActor* Actor = Item->LinkedActor.Get()) { TrackedActors.Remove(Actor); } } // 更新小地图 UpdateTrackedActors(); } void SMySlateWindow::UpdateTrackedActors() { if (MinimapWidget.IsValid()) { MinimapWidget->UpdateTrackedActors(TrackedActors); } } ``` ### 6. 注册窗口到编辑器 (FMySlateWindowModule.h) ```cpp #pragma once #include "Modules/ModuleManager.h" class FMySlateWindowModule : public IModuleInterface { public: virtual void StartupModule() override; virtual void ShutdownModule() override; void OpenSlateWindow(); private: TSharedPtr<class FUICommandList> PluginCommands; }; ``` ### 7. 模块实现 (FMySlateWindowModule.cpp) ```cpp #include "FMySlateWindowModule.h" #include "Framework/MultiBox/MultiBoxBuilder.h" #include "SMySlateWindow.h" #include "Engine/DataTable.h" #include "MyDataTableRow.h" #define LOCTEXT_NAMESPACE "FMySlateWindowModule" void FMySlateWindowModule::StartupModule() { // 创建命令 PluginCommands = MakeShareable(new FUICommandList); PluginCommands->MapAction( FName("OpenSlateWindow"), FExecuteAction::CreateRaw(this, &FMySlateWindowModule::OpenSlateWindow), FCanExecuteAction()); // 添加到工具菜单 FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor"); LevelEditorModule.GetGlobalLevelEditorActions()->Append(PluginCommands.ToSharedRef()); TSharedPtr<FExtender> MenuExtender = MakeShareable(new FExtender()); MenuExtender->AddMenuExtension( "WindowLayout", EExtensionHook::After, PluginCommands, FMenuExtensionDelegate::CreateLambda([](FMenuBuilder& MenuBuilder) { MenuBuilder.AddMenuEntry( LOCTEXT("OpenSlateWindow", "打开Slate窗口"), LOCTEXT("OpenSlateWindowTooltip", "打开自定义Slate窗口"), FSlateIcon(), FName("OpenSlateWindow") ); }) ); LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(MenuExtender); } void FMySlateWindowModule::ShutdownModule() { // 清理资源 } void FMySlateWindowModule::OpenSlateWindow() { // 加载DataTable(示例路径) UDataTable* DataTable = LoadObject<UDataTable>(nullptr, TEXT("/Game/Data/MyDataTable.MyDataTable")); if (!DataTable) { UE_LOG(LogTemp, Warning, TEXT("无法加载DataTable资源!")); return; } // 创建窗口 TSharedRef<SWindow> Window = SNew(SWindow) .Title(LOCTEXT("WindowTitle", "自定义Slate窗口")) .ClientSize(FVector2D(800, 600)); Window->SetContent(SNew(SMySlateWindow).DataTable(DataTable)); // 添加到编辑器窗口 IMainFrameModule& MainFrameModule = FModuleManager::LoadModuleChecked<IMainFrameModule>("MainFrame"); if (TSharedPtr<SWindow> ParentWindow = MainFrameModule.GetParentWindow()) { FSlateApplication::Get().AddWindowAsNativeChild(Window, ParentWindow.ToSharedRef()); } else { FSlateApplication::Get().AddWindow(Window); } } IMPLEMENT_MODULE(FMySlateWindowModule, MySlateWindow) ``` ### 功能说明 1. **小地图功能**: - 支持鼠标滚轮缩放($ZoomLevel$ 范围 $[0.5, 3.0]$) - 支持拖拽移动(边界约束) - 超出边框自动裁剪 - 显示选中的Actor位置和名称 2. **格功能**: - 头:复选框、类型、数量、显示颜色 - 数据源:继承自 `FMyBaseStruct` 的DataTable - 交互:勾选复选框在小地图显示对应Actor 3. **坐标转换**: - 世界坐标到小地图坐标的转换函数: $$ \text{MinimapPos} = \begin{pmatrix} \text{WorldX} \times 0.1 \\ \text{WorldY} \times 0.1 \end{pmatrix} $$ 4. **性能优化**: - 使用弱指针 (`TWeakObjectPtr`) 跟踪Actor - 只绘制可见区域内的内容 - 批量更新跟踪的Actor列 ### 使用步骤 1. 创建DataTable资源: - 右键点击内容浏览器 → 创建高级资源 → 数据- 选择 `FMyDataTableRow` 作为行结构 - 填充测试数据(包括LinkedActor字段) 2. 编译并运行模块 3. 在编辑器菜单栏找到"打开Slate窗口"选项 4. 勾选格行复选框,观察小地图上的标记点 ### 相关问题 1. 如何在小地图上显示更多类型的标记(如图标、文字标签)?[^1] 2. 如何实现DataTable的实时更新同步到Slate窗口?[^2] 3. 如何优化大量Actor在小地图上的绘制性能?[^3] 4. 如何保存和加载小地图的视图状态(缩放、位置)?[^4] 5. 如何为不同类型的Actor使用不同的小地图图标?[^5] [^1]: 可以通过扩展 `SMinimapWidget::OnPaint` 方法添加更多绘制逻辑 [^2]: 实现 `FMySlateWindowModule` 中的数据变更监听接口 [^3]: 使用空间分区数据结构(如四叉树)管理标记点 [^4]: 将视图状态序列化到配置文件或项目设置中 [^5]: 在数据结构中添加图标字段,在绘制时使用不同纹理
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值