Compare commits

...

21 Commits

Author SHA1 Message Date
430b533c7e 删除背包的配置 2025-09-24 16:10:25 +08:00
8c0e4f2de4 修改地图名称 2025-09-24 14:09:07 +08:00
58a57e59c7 修复状态bug 2025-09-24 14:06:48 +08:00
a64ef3a90e 更新节点的可移动性 2025-09-24 13:59:27 +08:00
670905633d 修改节点的初始状态 2025-09-24 13:48:09 +08:00
5ddb1b45f9 更新背包接收地图生成的技能 2025-09-24 12:13:42 +08:00
33a7fb06f1 更新索引 2025-09-24 11:08:09 +08:00
2b8c122a32 地图系统中添加背包 2025-09-24 11:07:45 +08:00
81629714f7 更新背包配置功能 2025-09-24 11:00:12 +08:00
a235d423b5 调整关卡切换后的地图数据保存问题 2025-09-23 19:30:06 +08:00
6f67c20a2e 添加对战结束后返回地图场景 2025-09-23 19:10:48 +08:00
d6df1f4f7a 根据节点类型不同,触发不同的节点事件 2025-09-23 19:07:16 +08:00
b6b7f2a411 调整节点状态逻辑 2025-09-22 20:32:04 +08:00
b50712416a 更新角色的移动 2025-09-22 20:18:26 +08:00
432c8718ba 添加自动移动 2025-09-22 20:08:31 +08:00
ba1f88a85e 添加节点蓝图 2025-09-22 19:48:39 +08:00
0fc222490a 添加节点状态 2025-09-22 19:38:12 +08:00
1dfaab4de9 调整间隔生成节点 2025-09-22 19:11:50 +08:00
314c35b8e2 调整连线的生成算法 2025-09-22 18:56:17 +08:00
776de95929 修复地图生成逻辑bug 2025-09-22 18:38:02 +08:00
cfa74a426d 添加地图生成系统 2025-09-22 17:39:50 +08:00
30 changed files with 640 additions and 44 deletions

View File

@ -96,6 +96,8 @@ 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.FishingMapSubSystem")
+ClassRedirects=(OldName="/Script/ProjectFish.FishingMapSystem",NewName="/Script/ProjectFish.FishingMapSubSystem")
[/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)

Binary file not shown.

Binary file not shown.

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

@ -7,7 +7,7 @@
#include "ProjectFish/Definations.h"
#include "ProjectFish/PawnWithSkill.h"
#include "ProjectFish/DataAsset/BagConfigAsset.h"
#include "ProjectFish/Subsystem/FishingRodConfigSubsystem.h"
#include "ProjectFish/Gameplay/Subsystem/FishingRodConfigSubsystem.h"
// Sets default values for this component's properties

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,41 @@
// 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;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category= MapConfig, meta = (ToolTip = "节点水平间距"))
int32 NodeOffsetX = 100;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category= MapConfig, meta = (ToolTip = "节点垂直间距"))
int32 NodeOffsetY = 300;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category= MapConfig, meta = (ToolTip = "战斗节点名称"))
FName BattleMapName = FName(TEXT("Dabaza"));
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category= MapConfig, meta = (ToolTip = "道具节点提供的技能"))
TArray<class USkillAsset*> RandomSkills;
// UPROPERTY(EditAnywhere, BlueprintReadWrite, Category= MapConfig, meta = (AllowedClasses = "SkillAsset", ToolTip = "道具节点提供的技能"))
// TArray<FSoftObjectPath> RandomSkills;
};

View File

@ -411,4 +411,52 @@ struct FShipDataConfig
TSubclassOf<UCameraShakeBase> CollisionCameraShakeClass;
};
/*** 地图相关定义 ***/
UENUM(BlueprintType)
enum class EMapNodeType : uint8
{
Battle UMETA(DisplayName = "战斗"),
Skill UMETA(DisplayName = "技能道具"),
Boss UMETA(DisplayName = "关底Boss")
};
UENUM(BlueprintType)
enum class EMapNodeState : uint8
{
Moveable UMETA(DisplayName = "可移动到"),
Immovable UMETA(DisplayName = "不可移动到"),
};
//地图层
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)
{
}
};

View File

@ -0,0 +1,325 @@
#include "FishingMapSubSystem.h"
#include "FishingRodConfigSubsystem.h"
#include "Engine/Engine.h"
#include "Kismet/GameplayStatics.h"
#include "ProjectFish/DataAsset/MapConfigAsset.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));
}
UFishingMapSubSystem::UFishingMapSubSystem()
{
}
void UFishingMapSubSystem::GenerateMap()
{
GenerateMapWithConfig(MapConfig);
}
void UFishingMapSubSystem::GenerateMapWithConfig(const UMapConfigAsset* Config)
{
// 清空现有数据
ClearMap();
// 生成节点
GenerateNodes();
// 计算节点位置
CalculateNodePositions();
// 生成连线
GenerateConnections();
UE_LOG(LogTemp, Log, TEXT("地图生成完成:%d层共%d个节点%d条连线"),
GetLayerCount(), AllNodes.Num(), AllConnections.Num());
}
int32 UFishingMapSubSystem::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;
}
UFishingMapNode* UFishingMapSubSystem::GetNode(FGuid NodeID)
{
for (auto node: AllNodes)
{
if (node->NodeID == NodeID)
return node;
}
return nullptr;
}
void UFishingMapSubSystem::ClearMap()
{
AllNodes.Empty();
AllConnections.Empty();
AllLayers.Empty();
}
void UFishingMapSubSystem::MoveToNode(FGuid NodeID)
{
PlayerPosNodeID = NodeID;
TArray<FGuid> MoveableNodeIDs;
GetAllConnectedNodes(NodeID, MoveableNodeIDs);
for (auto node:AllNodes)
{
if (MoveableNodeIDs.Find(node->NodeID) == -1)
{
//节点不在可移动的列表内,更新状态
GetNode(node->NodeID)->NodeState = EMapNodeState::Immovable;
OnNodeStateChange.Broadcast(node->NodeID, EMapNodeState::Immovable);
}
else
{
GetNode(node->NodeID)->NodeState = EMapNodeState::Moveable;
OnNodeStateChange.Broadcast(node->NodeID, EMapNodeState::Moveable);
}
}
//根据节点类型 触发不同的事件
UFishingMapNode* node = GetNode(NodeID);
switch (node->NodeType)
{
case EMapNodeType::Battle:
UGameplayStatics::OpenLevel(this, MapConfig->BattleMapName);
break;
case EMapNodeType::Boss:
UGameplayStatics::OpenLevel(this, MapConfig->BattleMapName);
break;
case EMapNodeType::Skill:
//添加技能到背包系统中
if (UGameInstance* GameInstance = GetGameInstance())
{
//USkillAsset* RandomSkill =LoadObject<USkillAsset>(this, * MapConfig->RandomSkills[FMath::RandRange(0, MapConfig->RandomSkills.Num() - 1)].ToString());
USkillAsset* RandomSkill = DuplicateObject<USkillAsset>(MapConfig->RandomSkills[FMath::RandRange(0, MapConfig->RandomSkills.Num() - 1)], this);
if (!GameInstance->GetSubsystem<UFishingRodConfigSubsystem>()->AddSkillToPlayerInventory(RandomSkill))
{
UE_LOG(LogTemp, Error, TEXT("道具节点添加道具失败,背包没有多余位置"));
}
}
break;;
}
}
bool UFishingMapSubSystem::MoveToAble(FGuid TargetNodeID)
{
UFishingMapNode* currentNode = GetNode(PlayerPosNodeID);
UFishingMapNode* targetNode = GetNode(TargetNodeID);
if (currentNode)
{
for (auto connection: AllConnections)
{
if (connection.FromNodeID == PlayerPosNodeID && connection.ToNodeID == TargetNodeID)
{
return true;;
}
}
}
else
{
return targetNode->NodeState == EMapNodeState::Moveable;
}
return false;
}
void UFishingMapSubSystem::GenerateNodes()
{
AllLayers.SetNum(MapConfig->TotalLayers);
UE_LOG(LogTemp, Warning, TEXT("生成layer层数 %d "), 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;
NewNode->NodeState = EMapNodeState::Immovable;
AllNodes.Add(NewNode);
AllLayers[LayerIndex].AddNode(NewNode);
}
else
{
// 随机生成节点数量
int32 NodeCount = FMath::RandRange(MapConfig->Layer_MinNodes, MapConfig->Layer_MaxNodes);
//int32 NodeCount = 4;
// 生成节点
for (int32 NodeIndex = 0; NodeIndex < NodeCount; ++NodeIndex)
{
UFishingMapNode* NewNode = NewObject<UFishingMapNode>(this);
NewNode->LayerIndex = LayerIndex;
NewNode->IndexInLayer = NodeIndex;
//只有第一层的节点 默认是可用状态
if (LayerIndex == 0)
{
NewNode->NodeState = EMapNodeState::Moveable;
}
else
{
NewNode->NodeState = EMapNodeState::Immovable;
}
// 随机节点类型
NewNode->RandomNodeType();
AllNodes.Add(NewNode);
AllLayers[LayerIndex].AddNode(NewNode);
}
}
UE_LOG(LogTemp, Warning, TEXT("layer%d node: %d "), LayerIndex, AllLayers[LayerIndex].GetNodeNum() );
}
}
void UFishingMapSubSystem::CalculateNodePositions()
{
for (int32 LayerIndex = 0; LayerIndex < AllLayers.Num(); ++LayerIndex)
{
FMapLayer& Layer= AllLayers[LayerIndex];
int32 NodeCount = Layer.GetNodeNum();
float TotalWidth = (NodeCount - 1) * (MapConfig->NodeOffsetX); //水平总距离
float Y = LayerIndex * MapConfig->NodeOffsetY; //垂直高度
float StartX = -TotalWidth * 0.5f;
for (int32 NodeIndex = 0; NodeIndex <NodeCount; ++NodeIndex)
{
float X = StartX + NodeIndex * MapConfig->NodeOffsetX;
Layer.GetNode(NodeIndex)->Position = FVector2D(X, Y);
}
}
}
void UFishingMapSubSystem::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> UFishingMapSubSystem::GenerateNonCrossingConnections(int32 FromLayer, int32 ToLayer)
{
TArray<FSimpleConnection> Connections;
UE_LOG(LogTemp, Warning, TEXT("生成层级 %d -> %d 的随机连接"),FromLayer, ToLayer);
if (FromLayer == AllLayers.Num() - 2)
{
//倒数第二行所有都连接到最终boss房间
UE_LOG(LogTemp, Warning, TEXT("生成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));
UE_LOG(LogTemp, Warning, TEXT("下层 %d -> 上层 %d "),GetNodeAtLayerIndex(FromNodes.GetNode(NodeIndex)->NodeID),
GetNodeAtLayerIndex(BossID));
}
}
else
{
//第一步 随机为每个fromnode 分配指定范围的上层节点
UE_LOG(LogTemp, Warning, TEXT("第一轮生成节点的连接"));
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)) );
int32 connectionEndIndex = FMath::RandRange(connectionBeginIndex, endIndex);
//随机指定当前node 连接上层node的范围
LastToNodeIndex = connectionEndIndex;
for(int32 i = connectionBeginIndex; i <= connectionEndIndex; ++i)
{
//添加连线
Connections.Add(FSimpleConnection(FromNodes.GetNode(NodeIndex)->NodeID, ToNodes.GetNode(i )->NodeID));
UE_LOG(LogTemp, Warning, TEXT("下层 %d -> 上层 %d "),GetNodeAtLayerIndex(FromNodes.GetNode(NodeIndex)->NodeID),
GetNodeAtLayerIndex(ToNodes.GetNode(i )->NodeID));
}
}
//第二步 确保tolayer 每个节点都有连接线,没有连接线的分配 指定范围内的下层节点
if(LastToNodeIndex != ToNodes.GetNodeNum() - 1)
{
UE_LOG(LogTemp, Warning, TEXT("第二轮生成节点的连接"));
//还有上层节点是没有被连线状态,都连接到下层的最后一个节点
for(int32 ToNodeIndex = LastToNodeIndex; ToNodeIndex < ToNodes.GetNodeNum(); ++ToNodeIndex)
{
Connections.Add(FSimpleConnection(FromNodes.GetNode(FromNodes.GetNodeNum() - 1)->NodeID, ToNodes.GetNode(ToNodeIndex)->NodeID));
UE_LOG(LogTemp, Warning, TEXT("下层 %d -> 上层 %d "),GetNodeAtLayerIndex(FromNodes.GetNode(FromNodes.GetNodeNum() - 1)->NodeID),
GetNodeAtLayerIndex(ToNodes.GetNode(ToNodeIndex)->NodeID));
}
}
}
// for(auto connection: Connections)
// {
// UE_LOG(LogTemp, Warning, TEXT("下层 %d -> 上层 %d "),GetNodeAtLayerIndex(connection.FromNodeID),
// GetNodeAtLayerIndex(connection.ToNodeID));
// }
return Connections;
}
void UFishingMapSubSystem::GetAllConnectedNodes(FGuid NodeID, TArray<FGuid>& ConnectedNodes)
{
//获取所有可移动的节点
ConnectedNodes.Add(NodeID);
if (GetNode(NodeID)->ConnectedNodes.Num() != 0)
{
ConnectedNodes.Append(GetNode(NodeID)->ConnectedNodes);
for (auto ConnectedNode : GetNode(NodeID)->ConnectedNodes)
{
GetAllConnectedNodes(ConnectedNode, ConnectedNodes);
}
}
}

View File

@ -0,0 +1,114 @@
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "ProjectFish/Definations.h"
#include "FishingMapSubSystem.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNodeStateChange, FGuid, NodeID, EMapNodeState, NewState);
// 简化的地图节点
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 = "节点状态"))
EMapNodeState NodeState = EMapNodeState::Moveable;
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 UFishingMapSubSystem : public UGameInstanceSubsystem
{
GENERATED_BODY()
public:
UFishingMapSubSystem();
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ToolTip = "地图配置"))
class UMapConfigAsset* MapConfig;
UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "所有节点"))
TArray<TObjectPtr<UFishingMapNode>> AllNodes;
UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "所有连线"))
TArray<FSimpleConnection> AllConnections;
UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "按层级组织的节点"))
TArray<FMapLayer> AllLayers;
UPROPERTY(BlueprintAssignable)
FOnNodeStateChange OnNodeStateChange;
UPROPERTY(BlueprintReadWrite)
FGuid PlayerPosNodeID;
// 生成地图
UFUNCTION(BlueprintCallable, Category = "FishingMap")
void GenerateMap();
// 使用配置生成地图
void GenerateMapWithConfig(const UMapConfigAsset* Config);
// 获取总层数
int32 GetLayerCount() const { return AllLayers.Num(); }
UFUNCTION(BlueprintPure, Category = "FishingMap")
int32 GetNodeAtLayerIndex(FGuid NodeID) const;
UFUNCTION(BlueprintPure, Category = "FishingMap")
class UFishingMapNode* GetNode(FGuid NodeID) ;
// 清空地图
UFUNCTION(BlueprintCallable, Category = "FishingMap")
void ClearMap();
UFUNCTION(BlueprintCallable, Category = "FishingMap")
void MoveToNode(FGuid NodeID);
UFUNCTION(BlueprintPure, Category = "FishingMap")
bool MoveToAble(FGuid TargetNodeID);
private:
void GetAllConnectedNodes(FGuid NodeID, TArray<FGuid>& ConnectedNodes);
protected:
// 生成节点
void GenerateNodes();
// 生成连线(无交叉)
void GenerateConnections();
// 计算节点位置
void CalculateNodePositions();
// 生成无交叉的连接方案
TArray<FSimpleConnection> GenerateNonCrossingConnections(int32 FromLayer, int32 ToLayer);
};

View File

@ -0,0 +1,51 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "FishingRodConfigSubsystem.h"
#include "ProjectFish/DataAsset/BagConfigAsset.h"
#include "ProjectFish/DataAsset/BagShapeAsset.h"
void UFishingRodConfigSubsystem::CreateFishingRodInventory(class UBagShapeAsset* FishingRodShape, class UBagShapeAsset* PlayerBagShape)
{
//鱼竿的技能配置资源
fishingRodInventoryConfig = NewObject<UBagConfigAsset>(this);
fishingRodInventoryConfig->BagShapeAsset = FishingRodShape;
//玩家背包的技能配置资源
playerInventoryConfig = NewObject<UBagConfigAsset>(this);
playerInventoryConfig->BagShapeAsset = PlayerBagShape;
}
UBagConfigAsset* UFishingRodConfigSubsystem::GetFishingRodInventory()
{
return this->fishingRodInventoryConfig;
}
UBagConfigAsset* UFishingRodConfigSubsystem::GetPlayerInventory()
{
return playerInventoryConfig;
}
bool UFishingRodConfigSubsystem::AddSkillByConfig(USkillAsset* SkillAsset, UBagConfigAsset* InventoryConfig,
FIntPoint Position)
{
return InventoryConfig->AddSkill(SkillAsset, Position.X, Position.Y);
}
bool UFishingRodConfigSubsystem::AddSkillToPlayerInventory(USkillAsset* SkillAsset)
{
int32 width = playerInventoryConfig->BagShapeAsset->BagWidth;
int32 height = playerInventoryConfig->BagShapeAsset->BagHeight;
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
if (playerInventoryConfig->AddSkill(SkillAsset, x, y))
{
return true;
}
}
}
return false;
}

View File

@ -0,0 +1,38 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "ProjectFish/DataAsset/SkillAsset.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "FishingRodConfigSubsystem.generated.h"
/**
*
*/
UCLASS()
class PROJECTFISH_API UFishingRodConfigSubsystem : public UGameInstanceSubsystem
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category = "PlayerInventorySubsystem")
void CreateFishingRodInventory(class UBagShapeAsset* FishingRodShape, class UBagShapeAsset* PlayerBagShape);
UFUNCTION(BlueprintPure, Category = "PlayerInventorySubsystem")
UBagConfigAsset* GetFishingRodInventory();
UFUNCTION(BlueprintPure, Category = "PlayerInventorySubsystem")
UBagConfigAsset* GetPlayerInventory();
UFUNCTION(BlueprintPure, Category = "PlayerInventorySubsystem")
bool AddSkillByConfig(USkillAsset* SkillAsset, UBagConfigAsset* InventoryConfig, FIntPoint Position);
UFUNCTION(BlueprintPure, Category = "PlayerInventorySubsystem")
bool AddSkillToPlayerInventory(USkillAsset* SkillAsset);
private:
UPROPERTY()
class UBagConfigAsset* fishingRodInventoryConfig = nullptr;
UPROPERTY()
class UBagConfigAsset* playerInventoryConfig = nullptr;
};

View File

@ -1,17 +0,0 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "FishingRodConfigSubsystem.h"
#include "ProjectFish/DataAsset/BagConfigAsset.h"
void UFishingRodConfigSubsystem::CreatePlayerInventory(class UBagShapeAsset* bagShape)
{
inventoryConfig = NewObject<UBagConfigAsset>(this);
inventoryConfig->BagShapeAsset = bagShape;
}
UBagConfigAsset* UFishingRodConfigSubsystem::GetFishingRodInventory()
{
return this->inventoryConfig;
}

View File

@ -1,26 +0,0 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "FishingRodConfigSubsystem.generated.h"
/**
*
*/
UCLASS()
class PROJECTFISH_API UFishingRodConfigSubsystem : public UGameInstanceSubsystem
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category = "PlayerInventorySubsystem")
void CreatePlayerInventory(class UBagShapeAsset* bagShape);
UFUNCTION(BlueprintPure, Category = "PlayerInventorySubsystem")
UBagConfigAsset* GetFishingRodInventory();
private:
UPROPERTY()
class UBagConfigAsset* inventoryConfig = nullptr;
};