diff --git a/ProjectFish/Binaries/Win64/UnrealEditor-ProjectFish.dll b/ProjectFish/Binaries/Win64/UnrealEditor-ProjectFish.dll index 2150fef..41640f9 100644 Binary files a/ProjectFish/Binaries/Win64/UnrealEditor-ProjectFish.dll and b/ProjectFish/Binaries/Win64/UnrealEditor-ProjectFish.dll differ diff --git a/ProjectFish/Binaries/Win64/UnrealEditor-ProjectFishEditor.dll b/ProjectFish/Binaries/Win64/UnrealEditor-ProjectFishEditor.dll index e93ce78..1f55a0a 100644 Binary files a/ProjectFish/Binaries/Win64/UnrealEditor-ProjectFishEditor.dll and b/ProjectFish/Binaries/Win64/UnrealEditor-ProjectFishEditor.dll differ diff --git a/ProjectFish/Config/DefaultEngine.ini b/ProjectFish/Config/DefaultEngine.ini index 179cdfd..6e3834b 100644 --- a/ProjectFish/Config/DefaultEngine.ini +++ b/ProjectFish/Config/DefaultEngine.ini @@ -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) diff --git a/ProjectFish/ProjectFish.sln.DotSettings.user b/ProjectFish/ProjectFish.sln.DotSettings.user index 953b168..b0bdbaf 100644 --- a/ProjectFish/ProjectFish.sln.DotSettings.user +++ b/ProjectFish/ProjectFish.sln.DotSettings.user @@ -1,11 +1,27 @@  + ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded ForceIncluded + ForceIncluded ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded diff --git a/ProjectFish/Source/ProjectFish/DataAsset/FishingMapSystem.cpp b/ProjectFish/Source/ProjectFish/DataAsset/FishingMapSystem.cpp new file mode 100644 index 0000000..50286c9 --- /dev/null +++ b/ProjectFish/Source/ProjectFish/DataAsset/FishingMapSystem.cpp @@ -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(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(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(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 Position = FVector2D(X, Y); + // } + // } + // } +} + +void UFishingMapSystem::GenerateConnections() +{ + AllConnections.Empty(); + + // 为每对相邻层生成连线 + for (int32 LayerIndex = 0; LayerIndex < AllLayers.Num() - 1; ++LayerIndex) + { + TArray 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 UFishingMapSystem::GenerateNonCrossingConnections(int32 FromLayer, int32 ToLayer) +{ + TArray 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; +} diff --git a/ProjectFish/Source/ProjectFish/DataAsset/FishingMapSystem.h b/ProjectFish/Source/ProjectFish/DataAsset/FishingMapSystem.h new file mode 100644 index 0000000..9527d93 --- /dev/null +++ b/ProjectFish/Source/ProjectFish/DataAsset/FishingMapSystem.h @@ -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 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> AllNodes; + + UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "所有连线")) + TArray AllConnections; + + UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "按层级组织的节点")) + TArray 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 GenerateNonCrossingConnections(int32 FromLayer, int32 ToLayer); +}; \ No newline at end of file diff --git a/ProjectFish/Source/ProjectFish/DataAsset/MapConfigAsset.cpp b/ProjectFish/Source/ProjectFish/DataAsset/MapConfigAsset.cpp new file mode 100644 index 0000000..2500016 --- /dev/null +++ b/ProjectFish/Source/ProjectFish/DataAsset/MapConfigAsset.cpp @@ -0,0 +1,4 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "MapConfigAsset.h" diff --git a/ProjectFish/Source/ProjectFish/DataAsset/MapConfigAsset.h b/ProjectFish/Source/ProjectFish/DataAsset/MapConfigAsset.h new file mode 100644 index 0000000..0ee903d --- /dev/null +++ b/ProjectFish/Source/ProjectFish/DataAsset/MapConfigAsset.h @@ -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; +}; diff --git a/ProjectFish/Source/ProjectFish/DataAsset/SimpleMapExample.cpp b/ProjectFish/Source/ProjectFish/DataAsset/SimpleMapExample.cpp new file mode 100644 index 0000000..b102f7d --- /dev/null +++ b/ProjectFish/Source/ProjectFish/DataAsset/SimpleMapExample.cpp @@ -0,0 +1,187 @@ +#include "SimpleMapExample.h" +#include "Engine/Engine.h" + +ASimpleMapExample::ASimpleMapExample() +{ + PrimaryActorTick.bCanEverTick = false; + + // 创建地图系统 + MapSystem = CreateDefaultSubobject(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 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 +// } \ No newline at end of file diff --git a/ProjectFish/Source/ProjectFish/DataAsset/SimpleMapExample.h b/ProjectFish/Source/ProjectFish/DataAsset/SimpleMapExample.h new file mode 100644 index 0000000..c01eb5e --- /dev/null +++ b/ProjectFish/Source/ProjectFish/DataAsset/SimpleMapExample.h @@ -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 MapSystem; + + + // 生成自定义地图 + UFUNCTION(BlueprintCallable, Category = "Map") + void GenerateCustomMap(); + + // // 打印地图信息 + // UFUNCTION(BlueprintCallable, Category = "Map") + // void PrintMapInfo(); + + // // 可视化地图(在编辑器中) + // UFUNCTION(BlueprintCallable, Category = "Map", CallInEditor = true) + // void VisualizeMap(); +}; \ No newline at end of file diff --git a/ProjectFish/Source/ProjectFish/Definations.h b/ProjectFish/Source/ProjectFish/Definations.h index e412b0a..240591a 100644 --- a/ProjectFish/Source/ProjectFish/Definations.h +++ b/ProjectFish/Source/ProjectFish/Definations.h @@ -411,4 +411,45 @@ struct FShipDataConfig TSubclassOf 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> Nodes; + void AddNode(TObjectPtr Node) { Nodes.Add(Node); }; + TObjectPtr 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) + { + } +}; \ No newline at end of file