添加地图生成系统

This commit is contained in:
997146918 2025-09-22 17:39:50 +08:00
parent acc57e2874
commit cfa74a426d
11 changed files with 621 additions and 0 deletions

View File

@ -96,6 +96,7 @@ ManualIPAddress=
+ClassRedirects=(OldName="/Script/ProjectFish.Skill_Pull",NewName="/Script/ProjectFish.SkillEffect_Damage")
+ClassRedirects=(OldName="/Script/ProjectFish.SkillEffect_Pull",NewName="/Script/ProjectFish.SkillEffect_Damage")
+ClassRedirects=(OldName="/Script/ProjectFish.PlayerInventorySubsystem",NewName="/Script/ProjectFish.FishingRodConfigSubsystem")
+ClassRedirects=(OldName="/Script/ProjectFish.SimpleMapSystem",NewName="/Script/ProjectFish.FishingMapSystem")
[/Script/Engine.CollisionProfile]
-Profiles=(Name="NoCollision",CollisionEnabled=NoCollision,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="No collision",bCanModify=False)

View File

@ -1,11 +1,27 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AArray_002Eh_002Fl_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_003FRuntime_003FCore_003FPublic_003FContainers_003FArray_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAssetData_002Eh_002Fl_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_003FRuntime_003FCoreUObject_003FPublic_003FAssetRegistry_003FAssetData_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AClass_002Ecpp_002Fl_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_003FRuntime_003FCoreUObject_003FPrivate_003FUObject_003FClass_002Ecpp/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AGarbageCollection_002Ecpp_002Fl_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_003FRuntime_003FCoreUObject_003FPrivate_003FUObject_003FGarbageCollection_002Ecpp/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AK2Node_005FCallFunction_002Ecpp_002Fl_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_003FEditor_003FBlueprintGraph_003FPrivate_003FK2Node_005FCallFunction_002Ecpp/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AKismetDebugUtilities_002Ecpp_002Fl_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_003FEditor_003FUnrealEd_003FPrivate_003FKismet2_003FKismetDebugUtilities_002Ecpp/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AObjectHandle_002Eh_002Fl_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_003FRuntime_003FCoreUObject_003FPublic_003FUObject_003FObjectHandle_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AObjectPtr_002Eh_002Fl_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_003FRuntime_003FCoreUObject_003FPublic_003FUObject_003FObjectPtr_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ARasovog_002Easm_002Fl_003AC_0021_003FUsers_003F99714_003FAppData_003FLocal_003FTemp_003FSandboxFiles_003FMiqowul_003FRasovog_002Easm/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATaskPrivate_002Eh_002Fl_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_003FRuntime_003FCore_003FPublic_003FTasks_003FTaskPrivate_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATuniqep_002Easm_002Fl_003AC_0021_003FUsers_003F99714_003FAppData_003FLocal_003FTemp_003FSandboxFiles_003FKasafil_003FTuniqep_002Easm/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AUnrealEngine_002Ecpp_002Fl_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_003FRuntime_003FEngine_003FPrivate_003FUnrealEngine_002Ecpp/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AUnrealType_002Eh_002Fl_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_003FRuntime_003FCoreUObject_003FPublic_003FUObject_003FUnrealType_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AUObjectArray_002Ecpp_002Fl_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_003FRuntime_003FCoreUObject_003FPrivate_003FUObject_003FUObjectArray_002Ecpp/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AUObjectArray_002Eh_002Fl_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_003FRuntime_003FCoreUObject_003FPublic_003FUObject_003FUObjectArray_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AUObjectBase_002Eh_002Fl_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_003FRuntime_003FCoreUObject_003FPublic_003FUObject_003FUObjectBase_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=72263A5F_002DD5F9_002D3915_002D8DA0_002DCD45C38A70E5_002Fdl_003ASource_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_002Fd_003AEditor_002Fd_003ABlueprintGraph_002Fd_003APrivate_002Ff_003AK2Node_005FCallFunction_002Ecpp/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=72263A5F_002DD5F9_002D3915_002D8DA0_002DCD45C38A70E5_002Fdl_003ASource_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_002Fd_003AEditor_002Fd_003AUnrealEd_002Fd_003APrivate_002Fd_003AKismet2_002Ff_003AKismetDebugUtilities_002Ecpp/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=72263A5F_002DD5F9_002D3915_002D8DA0_002DCD45C38A70E5_002Fdl_003ASource_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_002Fd_003ARuntime_002Fd_003ACoreUObject_002Fd_003APublic_002Fd_003AAssetRegistry_002Ff_003AAssetData_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=72263A5F_002DD5F9_002D3915_002D8DA0_002DCD45C38A70E5_002Fdl_003ASource_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_002Fd_003AEditor_002Fd_003APropertyEditor_002Fd_003APublic_002Ff_003APropertyCustomizationHelpers_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=72263A5F_002DD5F9_002D3915_002D8DA0_002DCD45C38A70E5_002Fdl_003ASource_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_002Fd_003ARuntime_002Fd_003ACoreUObject_002Fd_003APublic_002Fd_003AUObject_002Ff_003AUObjectBaseUtility_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=72263A5F_002DD5F9_002D3915_002D8DA0_002DCD45C38A70E5_002Fdl_003ASource_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_002Fd_003ARuntime_002Fd_003ACore_002Fd_003APrivate_002Fd_003AMisc_002Ff_003AGuid_002Ecpp/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=72263A5F_002DD5F9_002D3915_002D8DA0_002DCD45C38A70E5_002Fdl_003ASource_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_002Fd_003ARuntime_002Fd_003ACore_002Fd_003APublic_002Fd_003AContainers_002Ff_003AArray_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=72263A5F_002DD5F9_002D3915_002D8DA0_002DCD45C38A70E5_002Fdl_003ASource_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_002Fd_003ARuntime_002Fd_003ACore_002Fd_003APublic_002Fd_003ADelegates_002Ff_003ADelegateSignatureImpl_002Einl/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=72263A5F_002DD5F9_002D3915_002D8DA0_002DCD45C38A70E5_002Fdl_003ASource_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_002Fd_003ARuntime_002Fd_003ACore_002Fd_003APublic_002Fd_003ADelegates_002Ff_003ADelegate_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=72263A5F_002DD5F9_002D3915_002D8DA0_002DCD45C38A70E5_002Fdl_003ASource_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_002Fd_003ARuntime_002Fd_003ACore_002Fd_003APublic_002Fd_003AMath_002Ff_003AUnrealMathUtility_002Eh/@EntryIndexedValue">ForceIncluded</s:String>

View File

@ -0,0 +1,212 @@
#include "FishingMapSystem.h"
#include "Engine/Engine.h"
UFishingMapNode::UFishingMapNode()
{
NodeID = FGuid::NewGuid();
NodeType = EMapNodeType::Battle;
LayerIndex = 0;
IndexInLayer = 0;
Position = FVector2D::ZeroVector;
}
void UFishingMapNode::RandomNodeType()
{
NodeType = static_cast<EMapNodeType>(FMath::RandRange(0, 1));
}
UFishingMapSystem::UFishingMapSystem()
{
}
void UFishingMapSystem::GenerateMap()
{
GenerateMapWithConfig(MapConfig);
}
void UFishingMapSystem::GenerateMapWithConfig(const UMapConfigAsset* Config)
{
// 清空现有数据
ClearMap();
// 生成节点
GenerateNodes();
// 计算节点位置
CalculateNodePositions();
// 生成连线
GenerateConnections();
UE_LOG(LogTemp, Log, TEXT("地图生成完成:%d层共%d个节点%d条连线"),
GetLayerCount(), AllNodes.Num(), AllConnections.Num());
}
int32 UFishingMapSystem::GetNodeAtLayerIndex(FGuid NodeID) const
{
for (FMapLayer Layer: AllLayers)
{
for (int i = 0; i < Layer.GetNodeNum(); i++)
{
if ( Layer.GetNode(i)->NodeID == NodeID)
return i;
}
}
return -1;
}
void UFishingMapSystem::ClearMap()
{
AllNodes.Empty();
AllConnections.Empty();
AllLayers.Empty();
}
void UFishingMapSystem::GenerateNodes()
{
AllLayers.SetNum(MapConfig->TotalLayers);
for (int32 LayerIndex = 0; LayerIndex < MapConfig->TotalLayers ; ++LayerIndex)
{
if (LayerIndex == MapConfig->TotalLayers - 1)
{
//最后一层 只有一个Boss
UFishingMapNode* NewNode = NewObject<UFishingMapNode>(this);
NewNode->LayerIndex = LayerIndex;
NewNode->IndexInLayer = 0;
NewNode->NodeType = EMapNodeType::Boss;
AllNodes.Add(NewNode);
AllLayers[LayerIndex].AddNode(NewNode);
}
else
{
// 随机生成节点数量
int32 NodeCount = FMath::RandRange(MapConfig->Layer_MinNodes, MapConfig->Layer_MaxNodes);
// 生成节点
for (int32 NodeIndex = 0; NodeIndex < NodeCount; ++NodeIndex)
{
UFishingMapNode* NewNode = NewObject<UFishingMapNode>(this);
NewNode->LayerIndex = LayerIndex;
NewNode->IndexInLayer = NodeIndex;
// 随机节点类型
NewNode->RandomNodeType();
AllNodes.Add(NewNode);
AllLayers[LayerIndex].AddNode(NewNode);
}
}
}
}
void UFishingMapSystem::CalculateNodePositions()
{
// float LayerSpacing = 1000.0f; // 层之间的距离
// float NodeSpacing = 300.0f; // 节点之间的距离
//
// for (int32 LayerIndex = 0; LayerIndex < AllLayers.Num(); ++LayerIndex)
// {
// FMapLayer& Layer= AllLayers[LayerIndex];
// int32 NodeCount = Layer.GetNodeNum();
//
// float X = LayerIndex * LayerSpacing;
// float TotalWidth = (NodeCount - 1) * NodeSpacing;
// float StartY = -TotalWidth * 0.5f;
//
// for (int32 NodeIndex = 0; NodeIndex <NodeCount; ++NodeIndex)
// {
// if (LayerNodes[NodeIndex])
// {
// float Y = StartY + NodeIndex * NodeSpacing;
// LayerNodes[NodeIndex]->Position = FVector2D(X, Y);
// }
// }
// }
}
void UFishingMapSystem::GenerateConnections()
{
AllConnections.Empty();
// 为每对相邻层生成连线
for (int32 LayerIndex = 0; LayerIndex < AllLayers.Num() - 1; ++LayerIndex)
{
TArray<FSimpleConnection> LayerConnections = GenerateNonCrossingConnections(LayerIndex, LayerIndex + 1);
AllConnections.Append(LayerConnections);
}
// 更新节点的连接信息
for (const FSimpleConnection& Connection : AllConnections)
{
// 找到起始节点并添加连接
for (UFishingMapNode* Node : AllNodes)
{
if (Node && Node->NodeID == Connection.FromNodeID)
{
Node->ConnectedNodes.AddUnique(Connection.ToNodeID);
break;
}
}
}
}
TArray<FSimpleConnection> UFishingMapSystem::GenerateNonCrossingConnections(int32 FromLayer, int32 ToLayer)
{
TArray<FSimpleConnection> Connections;
if (FromLayer == AllLayers.Num() - 2)
{
//倒数第二行所有都连接到最终boss房间
FMapLayer FromNodes = AllLayers[FromLayer];
FGuid BossID = AllLayers[ToLayer].GetNode(0)->NodeID;
for (int32 NodeIndex = 0; NodeIndex < FromNodes.GetNodeNum(); ++NodeIndex)
{
Connections.Add(FSimpleConnection(FromNodes.GetNode(NodeIndex)->NodeID, BossID));
}
}
else
{
//第一步 随机为每个fromnode 分配指定范围的上层节点
FMapLayer FromNodes = AllLayers[FromLayer];
FMapLayer ToNodes = AllLayers[ToLayer];
int32 LastToNodeIndex = 0;
for (int32 NodeIndex = 0; NodeIndex < FromNodes.GetNodeNum(); ++NodeIndex)
{
int32 connectionBeginIndex = LastToNodeIndex;
int32 endIndex = ToNodes.GetNodeNum() - 1;
//随机连线的最左侧位置根据当前所在layer的index 调整概率
int32 connectionEndIndex = FMath::Max(connectionBeginIndex,
(FMath::RandRange(connectionBeginIndex, endIndex + (FromNodes.GetNodeNum() - NodeIndex)) - (FromNodes.GetNodeNum() - NodeIndex)) );
//随机指定当前node 连接上层node的范围
LastToNodeIndex = FMath::RandRange(connectionBeginIndex, connectionEndIndex);
for(int32 i = connectionEndIndex; i < connectionEndIndex; ++i)
{
//添加连线
Connections.Add(FSimpleConnection(FromNodes.GetNode(NodeIndex)->NodeID, ToNodes.GetNode(connectionEndIndex)->NodeID));
}
}
//第二步 确保tolayer 每个节点都有连接线,没有连接线的分配 指定范围内的下层节点
if(LastToNodeIndex != ToNodes.GetNodeNum() - 1)
{
//还有上层节点是没有被连线状态,都连接到下层的最后一个节点
for(int32 ToNodeIndex = LastToNodeIndex; ToNodeIndex < ToNodes.GetNodeNum(); ++ToNodeIndex)
{
Connections.Add(FSimpleConnection(FromNodes.GetNode(FromNodes.GetNodeNum() - 1)->NodeID, ToNodes.GetNode(ToNodeIndex)->NodeID));
}
}
}
UE_LOG(LogTemp, Log, TEXT("生成层级 %d -> %d 的随机连接"),FromLayer, ToLayer);
for(auto connection: Connections)
{
UE_LOG(LogTemp, Log, TEXT("下层 %d -> 上层 %d "),GetNodeAtLayerIndex(connection.FromNodeID),
GetNodeAtLayerIndex(connection.FromNodeID));
}
return Connections;
}

View File

@ -0,0 +1,97 @@
#pragma once
#include "CoreMinimal.h"
#include "MapConfigAsset.h"
#include "Engine/DataAsset.h"
#include "ProjectFish/Definations.h"
#include "FishingMapSystem.generated.h"
// 简化的地图节点
UCLASS(BlueprintType)
class PROJECTFISH_API UFishingMapNode : public UObject
{
GENERATED_BODY()
public:
UFishingMapNode();
void RandomNodeType();
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "节点ID"))
FGuid NodeID;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "节点类型"))
EMapNodeType NodeType = EMapNodeType::Battle;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "层级索引"))
int32 LayerIndex = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "在层内的索引"))
int32 IndexInLayer = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "连接的下层节点ID"))
TArray<FGuid> ConnectedNodes;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "节点位置"))
FVector2D Position = FVector2D::ZeroVector;
};
/**
*
*/
UCLASS(BlueprintType)
class PROJECTFISH_API UFishingMapSystem : public UObject
{
GENERATED_BODY()
public:
UFishingMapSystem();
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "地图配置"))
UMapConfigAsset* MapConfig;
UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "所有节点"))
TArray<TObjectPtr<UFishingMapNode>> AllNodes;
UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "所有连线"))
TArray<FSimpleConnection> AllConnections;
UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "按层级组织的节点"))
TArray<FMapLayer> AllLayers;
// 生成地图
UFUNCTION(BlueprintCallable, Category = "FishingMap")
void GenerateMap();
// 使用配置生成地图
UFUNCTION(BlueprintCallable, Category = "FishingMap")
void GenerateMapWithConfig(const UMapConfigAsset* Config);
// 获取总层数
int32 GetLayerCount() const { return AllLayers.Num(); }
UFUNCTION(BlueprintPure, Category = "FishingMap")
int32 GetNodeAtLayerIndex(FGuid NodeID) const;
// 清空地图
UFUNCTION(BlueprintCallable, Category = "FishingMap")
void ClearMap();
protected:
// 生成节点
void GenerateNodes();
// 生成连线(无交叉)
void GenerateConnections();
// 计算节点位置
void CalculateNodePositions();
// 生成无交叉的连接方案
TArray<FSimpleConnection> GenerateNonCrossingConnections(int32 FromLayer, int32 ToLayer);
};

View File

@ -0,0 +1,4 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "MapConfigAsset.h"

View File

@ -0,0 +1,25 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "MapConfigAsset.generated.h"
/**
*
*/
UCLASS()
class PROJECTFISH_API UMapConfigAsset : public UDataAsset
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category= Layer, meta = (ToolTip = "每层最少节点数"))
int32 Layer_MinNodes = 1;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category= Layer, meta = (ToolTip = "每层最多节点数"))
int32 Layer_MaxNodes = 4;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category= MapConfig, meta = (ToolTip = "总层数", ClampMin = 2))
int32 TotalLayers = 4;
};

View File

@ -0,0 +1,187 @@
#include "SimpleMapExample.h"
#include "Engine/Engine.h"
ASimpleMapExample::ASimpleMapExample()
{
PrimaryActorTick.bCanEverTick = false;
// 创建地图系统
MapSystem = CreateDefaultSubobject<UFishingMapSystem>(TEXT("MapSystem"));
}
void ASimpleMapExample::BeginPlay()
{
Super::BeginPlay();
}
void ASimpleMapExample::GenerateCustomMap()
{
if (!MapSystem)
{
UE_LOG(LogTemp, Error, TEXT("MapSystem 未初始化"));
return;
}
// 生成地图
MapSystem->GenerateMap();
UE_LOG(LogTemp, Log, TEXT("生成自定义地图完成:%d层"), MapSystem->GetLayerCount());
//PrintMapInfo();
}
// void ASimpleMapExample::PrintMapInfo()
// {
// if (!MapSystem)
// {
// return;
// }
//
// UE_LOG(LogTemp, Log, TEXT("========== 地图信息 =========="));
// UE_LOG(LogTemp, Log, TEXT("总层数: %d"), MapSystem->GetLayerCount());
// UE_LOG(LogTemp, Log, TEXT("总节点数: %d"), MapSystem->AllNodes.Num());
// UE_LOG(LogTemp, Log, TEXT("总连线数: %d"), MapSystem->AllConnections.Num());
//
// // 打印每层信息
// for (int32 LayerIndex = 0; LayerIndex < MapSystem->GetLayerCount(); ++LayerIndex)
// {
// TArray<USimpleMapNode*> LayerNodes = MapSystem->GetNodesInLayer(LayerIndex);
// UE_LOG(LogTemp, Log, TEXT("第%d层: %d个节点"), LayerIndex + 1, LayerNodes.Num());
//
// for (int32 NodeIndex = 0; NodeIndex < LayerNodes.Num(); ++NodeIndex)
// {
// USimpleMapNode* Node = LayerNodes[NodeIndex];
// if (Node)
// {
// FString NodeTypeName;
// switch (Node->NodeType)
// {
// case EMapNodeType::Unknown:
// NodeTypeName = TEXT("起始");
// break;
// case EMapNodeType::Enemy:
// NodeTypeName = TEXT("敌人");
// break;
// case EMapNodeType::Elite:
// NodeTypeName = TEXT("精英");
// break;
// case EMapNodeType::Treasure:
// NodeTypeName = TEXT("财宝");
// break;
// case EMapNodeType::Rest:
// NodeTypeName = TEXT("休息");
// break;
// case EMapNodeType::Merchant:
// NodeTypeName = TEXT("商人");
// break;
// default:
// NodeTypeName = TEXT("未知");
// break;
// }
//
// UE_LOG(LogTemp, Log, TEXT(" 节点%d: %s (位置: %.0f, %.0f) 连接数: %d"),
// NodeIndex + 1, *NodeTypeName,
// Node->Position.X, Node->Position.Y,
// Node->ConnectedNodes.Num());
// }
// }
// }
//
// UE_LOG(LogTemp, Log, TEXT("==============================="));
// }
// void ASimpleMapExample::VisualizeMap()
// {
// if (!MapSystem)
// {
// UE_LOG(LogTemp, Warning, TEXT("MapSystem 未初始化,无法可视化"));
// return;
// }
//
// if (MapSystem->AllNodes.Num() == 0)
// {
// UE_LOG(LogTemp, Warning, TEXT("地图为空,请先生成地图"));
// return;
// }
//
// #if WITH_EDITOR
// // 在编辑器中绘制调试信息
// UWorld* World = GetWorld();
// if (World)
// {
// // 清除之前的调试绘制
// FlushPersistentDebugLines(World);
//
// // 绘制节点
// for (USimpleMapNode* Node : MapSystem->AllNodes)
// {
// if (Node)
// {
// FVector NodeLocation(Node->Position.X, Node->Position.Y, 0.0f);
//
// // 根据节点类型选择颜色
// FColor NodeColor = FColor::White;
// switch (Node->NodeType)
// {
// case EMapNodeType::Unknown:
// NodeColor = FColor::Green;
// break;
// case EMapNodeType::Enemy:
// NodeColor = FColor::Red;
// break;
// case EMapNodeType::Elite:
// NodeColor = FColor::Purple;
// break;
// case EMapNodeType::Treasure:
// NodeColor = FColor::Yellow;
// break;
// case EMapNodeType::Rest:
// NodeColor = FColor::Blue;
// break;
// case EMapNodeType::Merchant:
// NodeColor = FColor::Orange;
// break;
// }
//
// // 绘制节点球体
// DrawDebugSphere(World, NodeLocation, 50.0f, 12, NodeColor, true, 10.0f, 0, 2.0f);
// }
// }
//
// // 绘制连线
// for (const FSimpleConnection& Connection : MapSystem->AllConnections)
// {
// USimpleMapNode* FromNode = nullptr;
// USimpleMapNode* ToNode = nullptr;
//
// // 找到连接的节点
// for (USimpleMapNode* Node : MapSystem->AllNodes)
// {
// if (Node)
// {
// if (Node->NodeID == Connection.FromNodeID)
// {
// FromNode = Node;
// }
// else if (Node->NodeID == Connection.ToNodeID)
// {
// ToNode = Node;
// }
// }
// }
//
// if (FromNode && ToNode)
// {
// FVector FromLocation(FromNode->Position.X, FromNode->Position.Y, 0.0f);
// FVector ToLocation(ToNode->Position.X, ToNode->Position.Y, 0.0f);
//
// // 绘制连线
// DrawDebugLine(World, FromLocation, ToLocation, FColor::Cyan, true, 10.0f, 0, 3.0f);
// }
// }
//
// UE_LOG(LogTemp, Log, TEXT("地图可视化完成"));
// }
// #endif
// }

View File

@ -0,0 +1,38 @@
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "FishingMapSystem.h"
#include "SimpleMapExample.generated.h"
/**
* 使
*/
UCLASS(BlueprintType, Blueprintable)
class PROJECTFISH_API ASimpleMapExample : public AActor
{
GENERATED_BODY()
public:
ASimpleMapExample();
protected:
virtual void BeginPlay() override;
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Map")
TObjectPtr<UFishingMapSystem> MapSystem;
// 生成自定义地图
UFUNCTION(BlueprintCallable, Category = "Map")
void GenerateCustomMap();
// // 打印地图信息
// UFUNCTION(BlueprintCallable, Category = "Map")
// void PrintMapInfo();
// // 可视化地图(在编辑器中)
// UFUNCTION(BlueprintCallable, Category = "Map", CallInEditor = true)
// void VisualizeMap();
};

View File

@ -411,4 +411,45 @@ struct FShipDataConfig
TSubclassOf<UCameraShakeBase> CollisionCameraShakeClass;
};
/*** 地图相关定义 ***/
UENUM(BlueprintType)
enum class EMapNodeType : uint8
{
Battle UMETA(DisplayName = "战斗"),
Skill UMETA(DisplayName = "技能道具"),
Boss UMETA(DisplayName = "关底Boss")
};
//地图层
USTRUCT(BlueprintType)
struct FMapLayer
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray<TObjectPtr<class UFishingMapNode>> Nodes;
void AddNode(TObjectPtr<class UFishingMapNode> Node) { Nodes.Add(Node); };
TObjectPtr<UFishingMapNode> GetNode(int32 index) {return Nodes[index];}
int32 GetNodeNum() {return Nodes.Num();}
};
// 连线
USTRUCT(BlueprintType)
struct FSimpleConnection
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FGuid FromNodeID;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FGuid ToNodeID;
FSimpleConnection()
{
}
FSimpleConnection(const FGuid& From, const FGuid& To)
: FromNodeID(From), ToNodeID(To)
{
}
};