diff --git a/ProjectFish/Binaries/Win64/UnrealEditor-ProjectFish.dll b/ProjectFish/Binaries/Win64/UnrealEditor-ProjectFish.dll index 092afe9..1a5bd07 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 36c0f9a..ec782de 100644 Binary files a/ProjectFish/Binaries/Win64/UnrealEditor-ProjectFishEditor.dll and b/ProjectFish/Binaries/Win64/UnrealEditor-ProjectFishEditor.dll differ diff --git a/ProjectFish/Config/DefaultDialogue.ini b/ProjectFish/Config/DefaultDialogue.ini new file mode 100644 index 0000000..257892b --- /dev/null +++ b/ProjectFish/Config/DefaultDialogue.ini @@ -0,0 +1,5 @@ + + +[/Script/Dialogue.DialogueSettings] +DialogueWidth=125.000000 + diff --git a/ProjectFish/Config/DefaultEngine.ini b/ProjectFish/Config/DefaultEngine.ini index 88b2e38..563e4de 100644 --- a/ProjectFish/Config/DefaultEngine.ini +++ b/ProjectFish/Config/DefaultEngine.ini @@ -1,7 +1,8 @@ [/Script/EngineSettings.GameMapsSettings] GameDefaultMap=/Game/TopDown/Maps/TopDownMap.TopDownMap -EditorStartupMap=/Game/ART/Map/fishing.fishing +EditorStartupMap=/Game/Maps/Home.Home GlobalDefaultGameMode="/Script/ProjectFish.ProjectFishGameMode" +GameInstanceClass=/Game/Gameplay/BP_FishGameInstance.BP_FishGameInstance_C [/Script/Engine.RendererSettings] r.DefaultFeature.AutoExposure.ExtendDefaultLuminanceRange=True diff --git a/ProjectFish/Content/ART/Map/fishing.umap b/ProjectFish/Content/ART/Map/fishing.umap index 3a8a89f..373deab 100644 Binary files a/ProjectFish/Content/ART/Map/fishing.umap and b/ProjectFish/Content/ART/Map/fishing.umap differ diff --git a/ProjectFish/Content/DataAssets/Dialogue/Dialogue_AnotherFish.uasset b/ProjectFish/Content/DataAssets/Dialogue/Dialogue_AnotherFish.uasset new file mode 100644 index 0000000..1a7f8a1 Binary files /dev/null and b/ProjectFish/Content/DataAssets/Dialogue/Dialogue_AnotherFish.uasset differ diff --git a/ProjectFish/Content/DataAssets/Dialogue/Dialogue_Battle.uasset b/ProjectFish/Content/DataAssets/Dialogue/Dialogue_Battle.uasset new file mode 100644 index 0000000..3a384d3 Binary files /dev/null and b/ProjectFish/Content/DataAssets/Dialogue/Dialogue_Battle.uasset differ diff --git a/ProjectFish/Content/DataAssets/Dialogue/Dialogue_Click_MarketQuest.uasset b/ProjectFish/Content/DataAssets/Dialogue/Dialogue_Click_MarketQuest.uasset new file mode 100644 index 0000000..ab56b99 Binary files /dev/null and b/ProjectFish/Content/DataAssets/Dialogue/Dialogue_Click_MarketQuest.uasset differ diff --git a/ProjectFish/Content/DataAssets/Dialogue/Dialogue_FirstQuest.uasset b/ProjectFish/Content/DataAssets/Dialogue/Dialogue_FirstQuest.uasset new file mode 100644 index 0000000..a1bbb2a Binary files /dev/null and b/ProjectFish/Content/DataAssets/Dialogue/Dialogue_FirstQuest.uasset differ diff --git a/ProjectFish/Content/DataAssets/Dialogue/Dialogue_ReadyMarket.uasset b/ProjectFish/Content/DataAssets/Dialogue/Dialogue_ReadyMarket.uasset new file mode 100644 index 0000000..39cc794 Binary files /dev/null and b/ProjectFish/Content/DataAssets/Dialogue/Dialogue_ReadyMarket.uasset differ diff --git a/ProjectFish/Content/DataAssets/Dialogue/Dialogue_Tutorial_Final.uasset b/ProjectFish/Content/DataAssets/Dialogue/Dialogue_Tutorial_Final.uasset new file mode 100644 index 0000000..347b837 Binary files /dev/null and b/ProjectFish/Content/DataAssets/Dialogue/Dialogue_Tutorial_Final.uasset differ diff --git a/ProjectFish/Content/DataAssets/Dialogue/Dialogue_Tutorial_Returnback.uasset b/ProjectFish/Content/DataAssets/Dialogue/Dialogue_Tutorial_Returnback.uasset new file mode 100644 index 0000000..3bb7990 Binary files /dev/null and b/ProjectFish/Content/DataAssets/Dialogue/Dialogue_Tutorial_Returnback.uasset differ diff --git a/ProjectFish/Content/DataAssets/Fish/Fish_Boss.uasset b/ProjectFish/Content/DataAssets/Fish/Fish_Boss.uasset index 7356f20..31cebeb 100644 Binary files a/ProjectFish/Content/DataAssets/Fish/Fish_Boss.uasset and b/ProjectFish/Content/DataAssets/Fish/Fish_Boss.uasset differ diff --git a/ProjectFish/Content/DataAssets/Fish/Fish_Tutorial2.uasset b/ProjectFish/Content/DataAssets/Fish/Fish_Tutorial2.uasset new file mode 100644 index 0000000..c8469e0 Binary files /dev/null and b/ProjectFish/Content/DataAssets/Fish/Fish_Tutorial2.uasset differ diff --git a/ProjectFish/Content/DataAssets/FishReward/Fish1Reward.uasset b/ProjectFish/Content/DataAssets/FishReward/Fish1Reward.uasset index 6597492..d7bbba3 100644 Binary files a/ProjectFish/Content/DataAssets/FishReward/Fish1Reward.uasset and b/ProjectFish/Content/DataAssets/FishReward/Fish1Reward.uasset differ diff --git a/ProjectFish/Content/DataAssets/FishReward/Reward_MagicBook.uasset b/ProjectFish/Content/DataAssets/FishReward/Reward_MagicBook.uasset new file mode 100644 index 0000000..54d92a7 Binary files /dev/null and b/ProjectFish/Content/DataAssets/FishReward/Reward_MagicBook.uasset differ diff --git a/ProjectFish/Content/DataAssets/FishReward/Shapes/Shape_2x2.uasset b/ProjectFish/Content/DataAssets/FishReward/Shapes/Shape_2x2.uasset new file mode 100644 index 0000000..9bcabf9 Binary files /dev/null and b/ProjectFish/Content/DataAssets/FishReward/Shapes/Shape_2x2.uasset differ diff --git a/ProjectFish/Content/DataAssets/FishReward/Shapes/Shape_4x3.uasset b/ProjectFish/Content/DataAssets/FishReward/Shapes/Shape_4x3.uasset new file mode 100644 index 0000000..e3640ce Binary files /dev/null and b/ProjectFish/Content/DataAssets/FishReward/Shapes/Shape_4x3.uasset differ diff --git a/ProjectFish/Content/DataAssets/Quest/Quest1_AutoAccept_CompleteByReward.uasset b/ProjectFish/Content/DataAssets/Quest/Quest1_AutoAccept_CompleteByReward.uasset deleted file mode 100644 index 14a7f74..0000000 Binary files a/ProjectFish/Content/DataAssets/Quest/Quest1_AutoAccept_CompleteByReward.uasset and /dev/null differ diff --git a/ProjectFish/Content/DataAssets/Quest/Quest2_TriggerByQuest1.uasset b/ProjectFish/Content/DataAssets/Quest/Quest2_TriggerByQuest1.uasset deleted file mode 100644 index f152d8c..0000000 Binary files a/ProjectFish/Content/DataAssets/Quest/Quest2_TriggerByQuest1.uasset and /dev/null differ diff --git a/ProjectFish/Content/DataAssets/Quest/Quest3_CompleteByTargetPos.uasset b/ProjectFish/Content/DataAssets/Quest/Quest3_CompleteByTargetPos.uasset deleted file mode 100644 index 2b9cb5a..0000000 Binary files a/ProjectFish/Content/DataAssets/Quest/Quest3_CompleteByTargetPos.uasset and /dev/null differ diff --git a/ProjectFish/Content/DataAssets/Quest/Quest4_CompleteByEnemy.uasset b/ProjectFish/Content/DataAssets/Quest/Quest4_CompleteByEnemy.uasset deleted file mode 100644 index dd9c752..0000000 Binary files a/ProjectFish/Content/DataAssets/Quest/Quest4_CompleteByEnemy.uasset and /dev/null differ diff --git a/ProjectFish/Content/DataAssets/Quest/Quest_Main_Witch.uasset b/ProjectFish/Content/DataAssets/Quest/Quest_Main_Witch.uasset new file mode 100644 index 0000000..c13eefa Binary files /dev/null and b/ProjectFish/Content/DataAssets/Quest/Quest_Main_Witch.uasset differ diff --git a/ProjectFish/Content/DataAssets/Quest/Quest_Tutorial_First.uasset b/ProjectFish/Content/DataAssets/Quest/Quest_Tutorial_First.uasset new file mode 100644 index 0000000..909fb79 Binary files /dev/null and b/ProjectFish/Content/DataAssets/Quest/Quest_Tutorial_First.uasset differ diff --git a/ProjectFish/Content/DataAssets/Quest/TestQuest2_TriggerByQuest1.uasset b/ProjectFish/Content/DataAssets/Quest/TestQuest2_TriggerByQuest1.uasset new file mode 100644 index 0000000..20a833b Binary files /dev/null and b/ProjectFish/Content/DataAssets/Quest/TestQuest2_TriggerByQuest1.uasset differ diff --git a/ProjectFish/Content/DataAssets/Quest/TestQuest3_CompleteByTargetPos.uasset b/ProjectFish/Content/DataAssets/Quest/TestQuest3_CompleteByTargetPos.uasset new file mode 100644 index 0000000..03875b2 Binary files /dev/null and b/ProjectFish/Content/DataAssets/Quest/TestQuest3_CompleteByTargetPos.uasset differ diff --git a/ProjectFish/Content/DataAssets/Quest/TestQuest4_CompleteByEnemy.uasset b/ProjectFish/Content/DataAssets/Quest/TestQuest4_CompleteByEnemy.uasset new file mode 100644 index 0000000..1e7fd08 Binary files /dev/null and b/ProjectFish/Content/DataAssets/Quest/TestQuest4_CompleteByEnemy.uasset differ diff --git a/ProjectFish/Content/DataTable/DT_MarketQuestDialogue.uasset b/ProjectFish/Content/DataTable/DT_MarketQuestDialogue.uasset index 650a14f..e0c199c 100644 Binary files a/ProjectFish/Content/DataTable/DT_MarketQuestDialogue.uasset and b/ProjectFish/Content/DataTable/DT_MarketQuestDialogue.uasset differ diff --git a/ProjectFish/Content/DataTable/DT_TutorialSteps.uasset b/ProjectFish/Content/DataTable/DT_TutorialSteps.uasset new file mode 100644 index 0000000..c1670f1 Binary files /dev/null and b/ProjectFish/Content/DataTable/DT_TutorialSteps.uasset differ diff --git a/ProjectFish/Content/Gameplay/BP_Dabaza_GameMode.uasset b/ProjectFish/Content/Gameplay/BP_Dabaza_GameMode.uasset index 1e142c5..4cfeb48 100644 Binary files a/ProjectFish/Content/Gameplay/BP_Dabaza_GameMode.uasset and b/ProjectFish/Content/Gameplay/BP_Dabaza_GameMode.uasset differ diff --git a/ProjectFish/Content/Gameplay/BP_FishGameInstance.uasset b/ProjectFish/Content/Gameplay/BP_FishGameInstance.uasset new file mode 100644 index 0000000..0d17d6d Binary files /dev/null and b/ProjectFish/Content/Gameplay/BP_FishGameInstance.uasset differ diff --git a/ProjectFish/Content/Gameplay/BagSystem/BP_BagConfigComponent.uasset b/ProjectFish/Content/Gameplay/BagSystem/BP_BagConfigComponent.uasset index 693375c..9051e4a 100644 Binary files a/ProjectFish/Content/Gameplay/BagSystem/BP_BagConfigComponent.uasset and b/ProjectFish/Content/Gameplay/BagSystem/BP_BagConfigComponent.uasset differ diff --git a/ProjectFish/Content/Gameplay/BagSystem/BagConfig/TutorialBagConfig.uasset b/ProjectFish/Content/Gameplay/BagSystem/BagConfig/TutorialBagConfig.uasset new file mode 100644 index 0000000..a07aec8 Binary files /dev/null and b/ProjectFish/Content/Gameplay/BagSystem/BagConfig/TutorialBagConfig.uasset differ diff --git a/ProjectFish/Content/Gameplay/Home/BP_HomePlayerController.uasset b/ProjectFish/Content/Gameplay/Home/BP_HomePlayerController.uasset index 5815031..02687f7 100644 Binary files a/ProjectFish/Content/Gameplay/Home/BP_HomePlayerController.uasset and b/ProjectFish/Content/Gameplay/Home/BP_HomePlayerController.uasset differ diff --git a/ProjectFish/Content/Gameplay/Ship/BP_Ship.uasset b/ProjectFish/Content/Gameplay/Ship/BP_Ship.uasset index 4badb97..d46a22b 100644 Binary files a/ProjectFish/Content/Gameplay/Ship/BP_Ship.uasset and b/ProjectFish/Content/Gameplay/Ship/BP_Ship.uasset differ diff --git a/ProjectFish/Content/Gameplay/Ship/BP_ShipController.uasset b/ProjectFish/Content/Gameplay/Ship/BP_ShipController.uasset index 038406a..f6c0a51 100644 Binary files a/ProjectFish/Content/Gameplay/Ship/BP_ShipController.uasset and b/ProjectFish/Content/Gameplay/Ship/BP_ShipController.uasset differ diff --git a/ProjectFish/Content/Gameplay/TutorialSystem/TutorialTask_ConfigFish.uasset b/ProjectFish/Content/Gameplay/TutorialSystem/TutorialTask_ConfigFish.uasset new file mode 100644 index 0000000..48074e3 Binary files /dev/null and b/ProjectFish/Content/Gameplay/TutorialSystem/TutorialTask_ConfigFish.uasset differ diff --git a/ProjectFish/Content/Gameplay/TutorialSystem/TutorialTask_ConfigFish1.uasset b/ProjectFish/Content/Gameplay/TutorialSystem/TutorialTask_ConfigFish1.uasset new file mode 100644 index 0000000..d0aa121 Binary files /dev/null and b/ProjectFish/Content/Gameplay/TutorialSystem/TutorialTask_ConfigFish1.uasset differ diff --git a/ProjectFish/Content/Gameplay/TutorialSystem/TutorialTask_Delay.uasset b/ProjectFish/Content/Gameplay/TutorialSystem/TutorialTask_Delay.uasset new file mode 100644 index 0000000..24947ee Binary files /dev/null and b/ProjectFish/Content/Gameplay/TutorialSystem/TutorialTask_Delay.uasset differ diff --git a/ProjectFish/Content/Gameplay/TutorialSystem/TutorialTask_ShipItems_1.uasset b/ProjectFish/Content/Gameplay/TutorialSystem/TutorialTask_ShipItems_1.uasset new file mode 100644 index 0000000..de4428b Binary files /dev/null and b/ProjectFish/Content/Gameplay/TutorialSystem/TutorialTask_ShipItems_1.uasset differ diff --git a/ProjectFish/Content/Gameplay/TutorialSystem/TutorialTask_ShipItems_3.uasset b/ProjectFish/Content/Gameplay/TutorialSystem/TutorialTask_ShipItems_3.uasset new file mode 100644 index 0000000..9c78edb Binary files /dev/null and b/ProjectFish/Content/Gameplay/TutorialSystem/TutorialTask_ShipItems_3.uasset differ diff --git a/ProjectFish/Content/Maps/ChildLevel_Ship.umap b/ProjectFish/Content/Maps/ChildLevel_Ship.umap index 8c6ed8d..fa291ac 100644 Binary files a/ProjectFish/Content/Maps/ChildLevel_Ship.umap and b/ProjectFish/Content/Maps/ChildLevel_Ship.umap differ diff --git a/ProjectFish/Content/Maps/Home.umap b/ProjectFish/Content/Maps/Home.umap index 2e8c7c8..6bd145e 100644 Binary files a/ProjectFish/Content/Maps/Home.umap and b/ProjectFish/Content/Maps/Home.umap differ diff --git a/ProjectFish/Content/Textures/FishingRewards/MagicBook.uasset b/ProjectFish/Content/Textures/FishingRewards/MagicBook.uasset new file mode 100644 index 0000000..e4b190e Binary files /dev/null and b/ProjectFish/Content/Textures/FishingRewards/MagicBook.uasset differ diff --git a/ProjectFish/Content/UI/Dialogue/UMG_DialogueText_Widget.uasset b/ProjectFish/Content/UI/Dialogue/UMG_DialogueText_Widget.uasset new file mode 100644 index 0000000..c453db5 Binary files /dev/null and b/ProjectFish/Content/UI/Dialogue/UMG_DialogueText_Widget.uasset differ diff --git a/ProjectFish/Content/UI/Dialogue/UMG_DialogueWindow.uasset b/ProjectFish/Content/UI/Dialogue/UMG_DialogueWindow.uasset new file mode 100644 index 0000000..8afc438 Binary files /dev/null and b/ProjectFish/Content/UI/Dialogue/UMG_DialogueWindow.uasset differ diff --git a/ProjectFish/Content/UI/Fishing/Ready/UMG_FishingRodConfig.uasset b/ProjectFish/Content/UI/Fishing/Ready/UMG_FishingRodConfig.uasset index bfd83fd..1c211fa 100644 Binary files a/ProjectFish/Content/UI/Fishing/Ready/UMG_FishingRodConfig.uasset and b/ProjectFish/Content/UI/Fishing/Ready/UMG_FishingRodConfig.uasset differ diff --git a/ProjectFish/Content/UI/Fishing/Widgets/Select/UMG_FishingReward_SelectWidget.uasset b/ProjectFish/Content/UI/Fishing/Widgets/Select/UMG_FishingReward_SelectWidget.uasset index 96e05aa..2594d28 100644 Binary files a/ProjectFish/Content/UI/Fishing/Widgets/Select/UMG_FishingReward_SelectWidget.uasset and b/ProjectFish/Content/UI/Fishing/Widgets/Select/UMG_FishingReward_SelectWidget.uasset differ diff --git a/ProjectFish/Content/UI/Fishing/Windows/UMG_ManagerFishingREward_Window.uasset b/ProjectFish/Content/UI/Fishing/Windows/UMG_ManagerFishingREward_Window.uasset index 0c97972..d91d6e9 100644 Binary files a/ProjectFish/Content/UI/Fishing/Windows/UMG_ManagerFishingREward_Window.uasset and b/ProjectFish/Content/UI/Fishing/Windows/UMG_ManagerFishingREward_Window.uasset differ diff --git a/ProjectFish/Content/UI/Fishing/Windows/UMG_ReadyWIndow.uasset b/ProjectFish/Content/UI/Fishing/Windows/UMG_ReadyWIndow.uasset index a675d21..3b28ddb 100644 Binary files a/ProjectFish/Content/UI/Fishing/Windows/UMG_ReadyWIndow.uasset and b/ProjectFish/Content/UI/Fishing/Windows/UMG_ReadyWIndow.uasset differ diff --git a/ProjectFish/Content/UI/HomeUI/Widgets/HomeFuncList/UMG_Home_FuncList.uasset b/ProjectFish/Content/UI/HomeUI/Widgets/HomeFuncList/UMG_Home_FuncList.uasset index 41907b1..244f6db 100644 Binary files a/ProjectFish/Content/UI/HomeUI/Widgets/HomeFuncList/UMG_Home_FuncList.uasset and b/ProjectFish/Content/UI/HomeUI/Widgets/HomeFuncList/UMG_Home_FuncList.uasset differ diff --git a/ProjectFish/Content/UI/HomeUI/Windows/UMG_HomeWindow_Main.uasset b/ProjectFish/Content/UI/HomeUI/Windows/UMG_HomeWindow_Main.uasset index 4682a73..58e8583 100644 Binary files a/ProjectFish/Content/UI/HomeUI/Windows/UMG_HomeWindow_Main.uasset and b/ProjectFish/Content/UI/HomeUI/Windows/UMG_HomeWindow_Main.uasset differ diff --git a/ProjectFish/Content/UI/HomeUI/Windows/UMG_HomeWindow_MapSelect.uasset b/ProjectFish/Content/UI/HomeUI/Windows/UMG_HomeWindow_MapSelect.uasset index ad41901..a5635aa 100644 Binary files a/ProjectFish/Content/UI/HomeUI/Windows/UMG_HomeWindow_MapSelect.uasset and b/ProjectFish/Content/UI/HomeUI/Windows/UMG_HomeWindow_MapSelect.uasset differ diff --git a/ProjectFish/Content/UI/Market/Widgets/UMG_Market_ContainerSwitcher.uasset b/ProjectFish/Content/UI/Market/Widgets/UMG_Market_ContainerSwitcher.uasset index 4ce6471..a81d397 100644 Binary files a/ProjectFish/Content/UI/Market/Widgets/UMG_Market_ContainerSwitcher.uasset and b/ProjectFish/Content/UI/Market/Widgets/UMG_Market_ContainerSwitcher.uasset differ diff --git a/ProjectFish/Content/UI/Market/Window/UMG_Market_Dialogue.uasset b/ProjectFish/Content/UI/Market/Window/UMG_Market_Dialogue.uasset index bee20de..861734a 100644 Binary files a/ProjectFish/Content/UI/Market/Window/UMG_Market_Dialogue.uasset and b/ProjectFish/Content/UI/Market/Window/UMG_Market_Dialogue.uasset differ diff --git a/ProjectFish/Content/UI/Market/Window/UMG_Market_Quest.uasset b/ProjectFish/Content/UI/Market/Window/UMG_Market_Quest.uasset index e728237..403deba 100644 Binary files a/ProjectFish/Content/UI/Market/Window/UMG_Market_Quest.uasset and b/ProjectFish/Content/UI/Market/Window/UMG_Market_Quest.uasset differ diff --git a/ProjectFish/Content/UI/Tutorial/UMG_Tutorial_FishingTip.uasset b/ProjectFish/Content/UI/Tutorial/UMG_Tutorial_FishingTip.uasset new file mode 100644 index 0000000..653de57 Binary files /dev/null and b/ProjectFish/Content/UI/Tutorial/UMG_Tutorial_FishingTip.uasset differ diff --git a/ProjectFish/Content/UI/Tutorial/UMG_Tutorial_MoveTip.uasset b/ProjectFish/Content/UI/Tutorial/UMG_Tutorial_MoveTip.uasset new file mode 100644 index 0000000..3125886 Binary files /dev/null and b/ProjectFish/Content/UI/Tutorial/UMG_Tutorial_MoveTip.uasset differ diff --git a/ProjectFish/Content/UI/Tutorial/UMG_Tutorial_RewardSelect_Tip.uasset b/ProjectFish/Content/UI/Tutorial/UMG_Tutorial_RewardSelect_Tip.uasset new file mode 100644 index 0000000..8b13a0e Binary files /dev/null and b/ProjectFish/Content/UI/Tutorial/UMG_Tutorial_RewardSelect_Tip.uasset differ diff --git a/ProjectFish/Docs/TutorialSystemDesign.md b/ProjectFish/Docs/TutorialSystemDesign.md new file mode 100644 index 0000000..1388255 --- /dev/null +++ b/ProjectFish/Docs/TutorialSystemDesign.md @@ -0,0 +1,1059 @@ +# 新手引导系统设计文档 + +## 一、系统整体架构 + +### 1.1 核心组件 + +``` +TutorialSystem/ +├── TutorialSubsystem (UGameInstanceSubsystem) +│ ├── 管理引导流程状态 +│ ├── 协调各个系统的引导行为 +│ └── 持久化引导进度 +│ +├── TutorialStepAsset (UDataAsset) +│ ├── 定义每个引导步骤的配置 +│ ├── 触发条件和完成条件 +│ └── UI显示配置 +│ +├── TutorialUIController (UObject) +│ ├── 管理UI遮罩层 +│ ├── 控制高亮区域 +│ └── 显示引导提示 +│ +├── TutorialModeInterface (Interface) +│ ├── UI组件实现此接口 +│ ├── 提供正常模式/引导模式切换 +│ └── 提供UI元素过滤逻辑 +│ +└── TutorialEventListener (UObject) + ├── 监听游戏事件 + ├── 触发引导步骤推进 + └── 验证玩家操作 +``` + +### 1.2 数据流设计 + +``` +TutorialSubsystem (中央控制器) + ↓ + ├→ TutorialStepAsset (步骤配置) + ├→ TutorialUIController (UI控制) + ├→ QuestManager (任务系统集成) + ├→ DialogueAsset (对话系统集成) + ├→ FishingMapSubSystem (地图系统集成) + └→ GameInfoManager (进度存档) +``` + +--- + +## 二、核心系统设计 + +### 2.1 引导子系统 (TutorialSubsystem) + +#### 头文件结构 +```cpp +// TutorialSubsystem.h +#pragma once + +#include "CoreMinimal.h" +#include "Subsystems/GameInstanceSubsystem.h" +#include "TutorialSubsystem.generated.h" + +// 引导步骤状态 +UENUM(BlueprintType) +enum class ETutorialStepState : uint8 +{ + NotStarted, // 未开始 + InProgress, // 进行中 + WaitingForUser, // 等待用户操作 + Completed // 已完成 +}; + +// 引导步骤类型 +UENUM(BlueprintType) +enum class ETutorialStepType : uint8 +{ + Dialogue, // 对话步骤 + UIInteraction, // UI交互步骤 + Gameplay, // 游戏玩法步骤 + Battle, // 战斗步骤 + Custom // 自定义步骤 +}; + +// 引导事件委托 +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnTutorialStepChanged, int32, StepIndex, ETutorialStepState, NewState); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnTutorialModeChanged, bool, bInTutorialMode); + +UCLASS() +class PROJECTFISH_API UTutorialSubsystem : public UGameInstanceSubsystem +{ + GENERATED_BODY() + +public: + virtual void Initialize(FSubsystemCollectionBase& Collection) override; + virtual void Deinitialize() override; + + // 引导控制 + UFUNCTION(BlueprintCallable, Category = "Tutorial") + void StartTutorial(); + + UFUNCTION(BlueprintCallable, Category = "Tutorial") + void StopTutorial(); + + UFUNCTION(BlueprintCallable, Category = "Tutorial") + void AdvanceToNextStep(); + + UFUNCTION(BlueprintCallable, Category = "Tutorial") + void CompleteCurrentStep(); + + UFUNCTION(BlueprintCallable, Category = "Tutorial") + bool IsInTutorialMode() const { return bInTutorialMode; } + + UFUNCTION(BlueprintCallable, Category = "Tutorial") + int32 GetCurrentStepIndex() const { return CurrentStepIndex; } + + // 条件检查 + UFUNCTION(BlueprintCallable, Category = "Tutorial") + bool CanShowUIElement(FName ElementTag) const; + + UFUNCTION(BlueprintCallable, Category = "Tutorial") + bool ShouldHighlightUIElement(FName ElementTag) const; + + // 事件委托 + UPROPERTY(BlueprintAssignable, Category = "Tutorial") + FOnTutorialStepChanged OnTutorialStepChanged; + + UPROPERTY(BlueprintAssignable, Category = "Tutorial") + FOnTutorialModeChanged OnTutorialModeChanged; + +protected: + // 引导步骤列表 + UPROPERTY(EditDefaultsOnly, Category = "Tutorial") + TArray TutorialSteps; + + // 当前状态 + UPROPERTY() + bool bInTutorialMode = false; + + UPROPERTY() + int32 CurrentStepIndex = -1; + + UPROPERTY() + ETutorialStepState CurrentStepState = ETutorialStepState::NotStarted; + + // UI控制器 + UPROPERTY() + class UTutorialUIController* UIController; + +private: + void ExecuteStep(int32 StepIndex); + void OnStepCompleted(); +}; +``` + +--- + +### 2.2 引导步骤资产 (TutorialStepAsset) + +#### 数据资产设计 +```cpp +// TutorialStepAsset.h +#pragma once + +#include "CoreMinimal.h" +#include "Engine/DataAsset.h" +#include "TutorialStepAsset.generated.h" + +// UI元素过滤配置 +USTRUCT(BlueprintType) +struct FTutorialUIFilter +{ + GENERATED_BODY() + + // 可见的UI元素标签列表 + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TArray VisibleElementTags; + + // 高亮的UI元素标签 + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TArray HighlightedElementTags; + + // 可交互的UI元素标签 + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TArray InteractableElementTags; + + // 是否使用黑名单模式(默认白名单) + UPROPERTY(EditAnywhere, BlueprintReadWrite) + bool bUseBlacklist = false; +}; + +// 引导提示配置 +USTRUCT(BlueprintType) +struct FTutorialHintConfig +{ + GENERATED_BODY() + + // 提示文本 + UPROPERTY(EditAnywhere, BlueprintReadWrite) + FText HintText; + + // 提示位置(锚定到某个UI元素) + UPROPERTY(EditAnywhere, BlueprintReadWrite) + FName AnchorElementTag; + + // 提示偏移 + UPROPERTY(EditAnywhere, BlueprintReadWrite) + FVector2D Offset = FVector2D::ZeroVector; + + // 箭头指向 + UPROPERTY(EditAnywhere, BlueprintReadWrite) + bool bShowArrow = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + FVector2D ArrowDirection = FVector2D(0, 1); +}; + +// 完成条件类型 +UENUM(BlueprintType) +enum class ETutorialCompletionType : uint8 +{ + Manual, // 手动完成(代码调用) + UIClick, // 点击指定UI + TaskCompleted, // 任务完成 + DialogueFinished, // 对话结束 + BattleWon, // 战斗胜利 + ItemCollected, // 收集物品 + LocationReached // 到达位置 +}; + +// 完成条件配置 +USTRUCT(BlueprintType) +struct FTutorialCompletionCondition +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + ETutorialCompletionType CompletionType; + + // 条件参数(根据类型不同含义不同) + UPROPERTY(EditAnywhere, BlueprintReadWrite) + FName ConditionParameter; + + // 数值参数 + UPROPERTY(EditAnywhere, BlueprintReadWrite) + int32 NumericParameter = 1; +}; + +UCLASS(BlueprintType) +class PROJECTFISH_API UTutorialStepAsset : public UDataAsset +{ + GENERATED_BODY() + +public: + // 步骤基本信息 + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Basic") + FName StepID; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Basic") + FText StepName; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Basic") + ETutorialStepType StepType; + + // UI过滤配置 + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI") + FTutorialUIFilter UIFilter; + + // 引导提示 + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI") + FTutorialHintConfig HintConfig; + + // 完成条件 + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Completion") + FTutorialCompletionCondition CompletionCondition; + + // 对话资产(如果是对话步骤) + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Dialogue") + class UDialogueAsset* DialogueAsset; + + // 特殊地图配置(如果需要) + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Map") + class UMapConfigAsset* OverrideMapConfig; + + // 特殊战斗配置 + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Battle") + class UFishInfoConfigAsset* OverrideFishConfig; + + // 自动进入下一步的延迟时间(秒) + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Flow") + float AutoAdvanceDelay = 0.0f; + + // 是否在此步骤暂停游戏 + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Flow") + bool bPauseGame = false; +}; +``` + +--- + +### 2.3 UI引导控制器 (TutorialUIController) + +```cpp +// TutorialUIController.h +#pragma once + +#include "CoreMinimal.h" +#include "UObject/NoExportTypes.h" +#include "TutorialUIController.generated.h" + +UCLASS() +class PROJECTFISH_API UTutorialUIController : public UObject +{ + GENERATED_BODY() + +public: + // 创建遮罩层 + UFUNCTION(BlueprintCallable, Category = "Tutorial UI") + void ShowMaskOverlay(); + + UFUNCTION(BlueprintCallable, Category = "Tutorial UI") + void HideMaskOverlay(); + + // 高亮指定区域 + UFUNCTION(BlueprintCallable, Category = "Tutorial UI") + void HighlightWidget(UWidget* TargetWidget, float Padding = 10.0f); + + UFUNCTION(BlueprintCallable, Category = "Tutorial UI") + void ClearHighlight(); + + // 显示引导提示 + UFUNCTION(BlueprintCallable, Category = "Tutorial UI") + void ShowHint(const FTutorialHintConfig& HintConfig); + + UFUNCTION(BlueprintCallable, Category = "Tutorial UI") + void HideHint(); + +protected: + // 遮罩Widget类 + UPROPERTY(EditDefaultsOnly, Category = "Tutorial UI") + TSubclassOf MaskOverlayClass; + + // 提示Widget类 + UPROPERTY(EditDefaultsOnly, Category = "Tutorial UI") + TSubclassOf HintWidgetClass; + + UPROPERTY() + class UUserWidget* MaskOverlayWidget; + + UPROPERTY() + class UUserWidget* HintWidget; +}; +``` + +--- + +### 2.4 UI引导接口 (ITutorialModeInterface) + +```cpp +// TutorialModeInterface.h +#pragma once + +#include "CoreMinimal.h" +#include "UObject/Interface.h" +#include "TutorialModeInterface.generated.h" + +UINTERFACE(MinimalAPI, Blueprintable) +class UTutorialModeInterface : public UInterface +{ + GENERATED_BODY() +}; + +class PROJECTFISH_API ITutorialModeInterface +{ + GENERATED_BODY() + +public: + // 切换到引导模式 + UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Tutorial") + void EnterTutorialMode(const FTutorialUIFilter& Filter); + + // 退出引导模式 + UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Tutorial") + void ExitTutorialMode(); + + // 检查元素是否应该显示 + UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Tutorial") + bool ShouldShowElement(FName ElementTag) const; + + // 检查元素是否应该高亮 + UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Tutorial") + bool ShouldHighlightElement(FName ElementTag) const; + + // 设置元素Tag(在UI组件中实现) + UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Tutorial") + void SetElementTag(FName Tag); +}; +``` + +--- + +## 三、UI双模式实现方案 + +### 3.1 基础Widget改造 + +所有需要参与引导的Widget都应该: +1. 实现 `ITutorialModeInterface` 接口 +2. 为关键子控件设置 `ElementTag` +3. 在引导模式下动态控制显示/隐藏 + +#### 改造示例:家园UI + +```cpp +// HomeUIWidget.h +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "TutorialModeInterface.h" +#include "HomeUIWidget.generated.h" + +UCLASS() +class PROJECTFISH_API UHomeUIWidget : public UUserWidget, public ITutorialModeInterface +{ + GENERATED_BODY() + +public: + virtual void NativeConstruct() override; + + // ITutorialModeInterface implementation + virtual void EnterTutorialMode_Implementation(const FTutorialUIFilter& Filter) override; + virtual void ExitTutorialMode_Implementation() override; + virtual bool ShouldShowElement_Implementation(FName ElementTag) const override; + virtual bool ShouldHighlightElement_Implementation(FName ElementTag) const override; + +protected: + // UI元素绑定 + UPROPERTY(meta = (BindWidget)) + class UButton* Btn_Sail; + + UPROPERTY(meta = (BindWidget)) + class UButton* Btn_Market; + + UPROPERTY(meta = (BindWidget)) + class UButton* Btn_Shop; + + UPROPERTY(meta = (BindWidget)) + class UButton* Btn_Backpack; + + // 元素Tag映射 + UPROPERTY(EditDefaultsOnly, Category = "Tutorial") + TMap ElementTagMap; + + // 引导模式状态 + bool bInTutorialMode = false; + FTutorialUIFilter CurrentFilter; + +private: + void InitializeElementTags(); + void UpdateUIVisibility(); + void UpdateUIHighlight(); +}; +``` + +```cpp +// HomeUIWidget.cpp +#include "HomeUIWidget.h" +#include "Components/Button.h" +#include "TutorialSubsystem.h" + +void UHomeUIWidget::NativeConstruct() +{ + Super::NativeConstruct(); + InitializeElementTags(); +} + +void UHomeUIWidget::InitializeElementTags() +{ + // 为每个UI元素设置Tag + ElementTagMap.Add(TEXT("Btn_Sail"), Btn_Sail); + ElementTagMap.Add(TEXT("Btn_Market"), Btn_Market); + ElementTagMap.Add(TEXT("Btn_Shop"), Btn_Shop); + ElementTagMap.Add(TEXT("Btn_Backpack"), Btn_Backpack); +} + +void UHomeUIWidget::EnterTutorialMode_Implementation(const FTutorialUIFilter& Filter) +{ + bInTutorialMode = true; + CurrentFilter = Filter; + UpdateUIVisibility(); + UpdateUIHighlight(); +} + +void UHomeUIWidget::ExitTutorialMode_Implementation() +{ + bInTutorialMode = false; + + // 恢复所有元素为可见 + for (auto& Elem : ElementTagMap) + { + if (Elem.Value) + { + Elem.Value->SetVisibility(ESlateVisibility::Visible); + // 移除高亮效果 + } + } +} + +bool UHomeUIWidget::ShouldShowElement_Implementation(FName ElementTag) const +{ + if (!bInTutorialMode) + return true; + + if (CurrentFilter.bUseBlacklist) + { + // 黑名单模式:不在列表中的显示 + return !CurrentFilter.VisibleElementTags.Contains(ElementTag); + } + else + { + // 白名单模式:在列表中的显示 + return CurrentFilter.VisibleElementTags.Contains(ElementTag); + } +} + +bool UHomeUIWidget::ShouldHighlightElement_Implementation(FName ElementTag) const +{ + if (!bInTutorialMode) + return false; + + return CurrentFilter.HighlightedElementTags.Contains(ElementTag); +} + +void UHomeUIWidget::UpdateUIVisibility() +{ + for (auto& Elem : ElementTagMap) + { + if (Elem.Value) + { + bool bShouldShow = ShouldShowElement_Implementation(Elem.Key); + Elem.Value->SetVisibility(bShouldShow ? ESlateVisibility::Visible : ESlateVisibility::Collapsed); + } + } +} + +void UHomeUIWidget::UpdateUIHighlight() +{ + for (auto& Elem : ElementTagMap) + { + if (Elem.Value && ShouldHighlightElement_Implementation(Elem.Key)) + { + // 添加高亮效果(例如:发光边框、脉冲动画等) + // 可以使用UMG动画或者材质参数 + } + } +} +``` + +### 3.2 通用UI引导基类 + +为了简化改造,可以创建一个基类: + +```cpp +// TutorialEnabledWidget.h +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "TutorialModeInterface.h" +#include "TutorialEnabledWidget.generated.h" + +UCLASS() +class PROJECTFISH_API UTutorialEnabledWidget : public UUserWidget, public ITutorialModeInterface +{ + GENERATED_BODY() + +public: + virtual void NativeConstruct() override; + virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override; + + // ITutorialModeInterface implementation + virtual void EnterTutorialMode_Implementation(const FTutorialUIFilter& Filter) override; + virtual void ExitTutorialMode_Implementation() override; + virtual bool ShouldShowElement_Implementation(FName ElementTag) const override; + virtual bool ShouldHighlightElement_Implementation(FName ElementTag) const override; + +protected: + // 子类需要重写此方法来注册UI元素 + UFUNCTION(BlueprintNativeEvent, Category = "Tutorial") + void RegisterTutorialElements(); + virtual void RegisterTutorialElements_Implementation() {} + + // 注册单个元素 + UFUNCTION(BlueprintCallable, Category = "Tutorial") + void RegisterElement(FName Tag, UWidget* Widget); + + // 引导模式状态 + UPROPERTY(BlueprintReadOnly, Category = "Tutorial") + bool bInTutorialMode = false; + + UPROPERTY(BlueprintReadOnly, Category = "Tutorial") + FTutorialUIFilter CurrentFilter; + + // 元素映射 + UPROPERTY() + TMap ElementMap; + +private: + void UpdateAllElements(); + void ApplyHighlight(UWidget* Widget, bool bHighlight); +}; +``` + +--- + +## 四、关卡特殊配置系统 + +### 4.1 战斗关卡配置扩展 + +为新手引导创建特殊的战斗配置: + +```cpp +// TutorialBattleConfig.h +#pragma once + +#include "CoreMinimal.h" +#include "Engine/DataAsset.h" +#include "TutorialBattleConfig.generated.h" + +// 战斗事件触发点 +UENUM(BlueprintType) +enum class ETutorialBattleTrigger : uint8 +{ + OnBattleStart, // 战斗开始时 + OnFirstCast, // 第一次抛竿时 + OnFishHooked, // 鱼上钩时 + OnTurnStart, // 回合开始时 + OnTurnEnd, // 回合结束时 + OnVictory, // 胜利时 + OnHPThreshold // HP到达阈值时 +}; + +// 战斗中对话触发配置 +USTRUCT(BlueprintType) +struct FTutorialBattleDialogueTrigger +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + ETutorialBattleTrigger TriggerType; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float TriggerValue = 0.0f; // 用于HP阈值等 + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + class UDialogueAsset* DialogueAsset; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + bool bPauseBattle = true; +}; + +// 强制奖励配置 +USTRUCT(BlueprintType) +struct FTutorialBattleReward +{ + GENERATED_BODY() + + // 强制掉落的物品ID + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TArray ForcedDropItemIDs; + + // 是否强制玩家拾取所有奖励 + UPROPERTY(EditAnywhere, BlueprintReadWrite) + bool bMustCollectAll = true; + + // 拾取提示文本 + UPROPERTY(EditAnywhere, BlueprintReadWrite) + FText CollectHintText; +}; + +UCLASS(BlueprintType) +class PROJECTFISH_API UTutorialBattleConfig : public UDataAsset +{ + GENERATED_BODY() + +public: + // 使用的敌人配置 + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Battle") + class UFishInfoConfigAsset* FishConfig; + + // 对话触发器列表 + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Battle") + TArray DialogueTriggers; + + // 奖励配置 + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Battle") + FTutorialBattleReward RewardConfig; + + // 禁用的UI元素 + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Battle") + TArray DisabledUIElements; + + // 是否禁用返航按钮 + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Battle") + bool bDisableReturnButton = false; +}; +``` + +### 4.2 地图系统引导模式 + +```cpp +// 在 FishingMapSubSystem 中添加引导模式支持 + +// FishingMapSubSystem.h 中添加: + +UCLASS() +class PROJECTFISH_API UFishingMapSubSystem : public UGameInstanceSubsystem +{ + // ... 现有代码 ... + +public: + // 引导模式:生成固定的单节点地图 + UFUNCTION(BlueprintCallable, Category = "Tutorial") + void GenerateTutorialMap(const FVector2D& NodePosition); + + // 引导模式:只显示指定节点 + UFUNCTION(BlueprintCallable, Category = "Tutorial") + void SetTutorialVisibleNodes(const TArray& VisibleNodeIDs); + + // 引导模式:高亮指定节点 + UFUNCTION(BlueprintCallable, Category = "Tutorial") + void SetTutorialHighlightNode(int32 NodeID); + +protected: + UPROPERTY() + bool bInTutorialMode = false; + + UPROPERTY() + TArray TutorialVisibleNodes; + + UPROPERTY() + int32 TutorialHighlightNodeID = -1; +}; +``` + +--- + +## 五、实现流程步骤拆解 + +根据你的需求,新手引导分为15个步骤。以下是每个步骤的配置示例: + +### Step 1: 家园初始对话 +```cpp +// Content/DataAssets/Tutorial/Step01_HomeIntro.uasset +StepID: "Tutorial_Step01" +StepName: "家园初始对话" +StepType: Dialogue +DialogueAsset: DA_Tutorial_HomeIntro +CompletionCondition: + CompletionType: DialogueFinished + ConditionParameter: "DA_Tutorial_HomeIntro" +AutoAdvanceDelay: 0.5 +``` + +### Step 2: 引导出航按钮 +```cpp +// Content/DataAssets/Tutorial/Step02_SailButton.uasset +StepID: "Tutorial_Step02" +StepName: "引导点击出航" +StepType: UIInteraction +UIFilter: + VisibleElementTags: ["Btn_Sail"] + HighlightedElementTags: ["Btn_Sail"] + InteractableElementTags: ["Btn_Sail"] +HintConfig: + HintText: "点击【出航】按钮开始冒险" + AnchorElementTag: "Btn_Sail" + bShowArrow: true +CompletionCondition: + CompletionType: UIClick + ConditionParameter: "Btn_Sail" +``` + +### Step 3: 地图界面单点 +```cpp +// Content/DataAssets/Tutorial/Step03_MapSingleNode.uasset +StepID: "Tutorial_Step03" +StepName: "地图单节点引导" +StepType: UIInteraction +OverrideMapConfig: DA_Tutorial_MapConfig_SingleNode +UIFilter: + VisibleElementTags: ["MapNode_0"] + HighlightedElementTags: ["MapNode_0"] +HintConfig: + HintText: "点击这个地点开始探险" + AnchorElementTag: "MapNode_0" +CompletionCondition: + CompletionType: UIClick + ConditionParameter: "MapNode_0" +``` + +### Step 5: 移动按键引导 +```cpp +// Content/DataAssets/Tutorial/Step05_MovementGuide.uasset +StepID: "Tutorial_Step05" +StepName: "移动操作引导" +StepType: Gameplay +HintConfig: + HintText: "使用 WASD 键移动船只" + Offset: (0, -200) +CompletionCondition: + CompletionType: LocationReached + ConditionParameter: "FishingSpot_01" +``` + +### Step 6-10: 战斗引导(特殊配置) +```cpp +// Content/DataAssets/Tutorial/Battle01_Config.uasset +FishConfig: DA_Fish_Tutorial_Bass01 +DialogueTriggers: + [0]: + TriggerType: OnBattleStart + DialogueAsset: DA_Tutorial_Battle_Intro + [1]: + TriggerType: OnVictory + DialogueAsset: DA_Tutorial_Battle_Victory01 +RewardConfig: + ForcedDropItemIDs: ["Item_Bass"] + bMustCollectAll: true + CollectHintText: "左键选中,右键旋转,收集所有鱼获" +DisabledUIElements: ["Btn_Backpack", "Btn_Return"] +``` + +--- + +## 六、代码结构建议 + +### 6.1 目录结构 + +``` +Source/ProjectFish/ +└── Tutorial/ + ├── TutorialSubsystem.h/cpp # 核心子系统 + ├── TutorialUIController.h/cpp # UI控制器 + ├── TutorialEventListener.h/cpp # 事件监听器 + ├── TutorialModeInterface.h # UI接口 + ├── DataAsset/ + │ ├── TutorialStepAsset.h/cpp # 步骤资产 + │ └── TutorialBattleConfig.h/cpp # 战斗配置 + └── Widget/ + └── TutorialEnabledWidget.h/cpp # UI基类 + +Content/ +├── DataAssets/ +│ └── Tutorial/ +│ ├── Steps/ # 引导步骤资产 +│ │ ├── Step01_HomeIntro.uasset +│ │ ├── Step02_SailButton.uasset +│ │ └── ... +│ ├── Battles/ # 战斗配置 +│ │ ├── Battle01_FirstFish.uasset +│ │ └── Battle02_SecondFish.uasset +│ └── Maps/ # 地图配置 +│ └── Tutorial_SingleNode.uasset +│ +└── UI/ + └── Tutorial/ + ├── WBP_TutorialMask.uasset # 遮罩Widget + ├── WBP_TutorialHint.uasset # 提示Widget + └── WBP_TutorialHighlight.uasset # 高亮效果 +``` + +### 6.2 现有系统集成点 + +#### QuestManager 集成 +```cpp +// 在 QuestManager.h 中添加: +UFUNCTION(BlueprintCallable, Category = "Tutorial") +void AcceptTutorialQuest(UQuestAsset* QuestAsset); +``` + +#### DialogueAsset 集成 +```cpp +// 对话系统已经有事件委托,直接监听即可 +// 在 TutorialSubsystem 中: +void UTutorialSubsystem::Initialize(FSubsystemCollectionBase& Collection) +{ + // 监听对话结束事件 + // UDialogueAsset::OnDialogueFinished.AddDynamic(this, &UTutorialSubsystem::OnDialogueFinished); +} +``` + +#### FishingMapSubSystem 集成 +```cpp +// 在地图生成前检查是否引导模式 +void UFishingMapSubSystem::GenerateMap() +{ + UTutorialSubsystem* TutorialSys = GetGameInstance()->GetSubsystem(); + if (TutorialSys && TutorialSys->IsInTutorialMode()) + { + // 使用引导专用地图配置 + GenerateTutorialMap(...); + return; + } + // 正常地图生成 + ... +} +``` + +--- + +## 七、UI改造优先级 + +### 高优先级(必须改造) +1. **HomeUI** - 家园界面(出航、鱼市、道具店、背包按钮) +2. **MapUI** - 地图界面(节点显示和高亮) +3. **BattleUI** - 战斗界面(技能、背包、返航按钮控制) +4. **RewardUI** - 奖励界面(强制拾取逻辑) + +### 中优先级 +5. **MarketUI** - 鱼市界面(任务提交) +6. **FishingSceneUI** - 钓鱼场景UI(移动提示) + +### 低优先级 +7. 其他辅助UI + +--- + +## 八、实现建议 + +### 8.1 分阶段实现 + +**Phase 1: 核心框架** +- [ ] 实现 TutorialSubsystem +- [ ] 实现 TutorialStepAsset +- [ ] 实现 ITutorialModeInterface +- [ ] 实现 TutorialUIController + +**Phase 2: UI改造** +- [ ] 创建 TutorialEnabledWidget 基类 +- [ ] 改造 HomeUI +- [ ] 改造 MapUI +- [ ] 改造 BattleUI +- [ ] 改造 RewardUI + +**Phase 3: 系统集成** +- [ ] 集成对话系统 +- [ ] 集成任务系统 +- [ ] 集成地图系统 +- [ ] 集成战斗系统 + +**Phase 4: 配置数据** +- [ ] 创建15个引导步骤资产 +- [ ] 创建引导专用对话 +- [ ] 创建引导专用战斗配置 +- [ ] 创建引导专用地图配置 + +**Phase 5: 测试优化** +- [ ] 流程测试 +- [ ] UI表现优化 +- [ ] 存档系统集成 +- [ ] Bug修复 + +### 8.2 关键技术点 + +1. **UI遮罩实现**:使用 Canvas Panel + Image(黑色半透明)+ Retainer Box(剪切高亮区域) + +2. **高亮效果**: + - 方案A:Material + 发光参数 + - 方案B:UMG Animation(缩放脉冲) + - 方案C:Border + 高亮颜色 + +3. **事件监听**: + - 使用委托系统 + - 在 TutorialEventListener 中统一管理 + - 避免硬编码,使用数据驱动 + +4. **存档集成**: + - 在 PlayerInfoSaveGame 中添加: + ```cpp + UPROPERTY() + bool bTutorialCompleted = false; + + UPROPERTY() + int32 LastCompletedTutorialStep = -1; + ``` + +--- + +## 九、最佳实践建议 + +### 9.1 设计原则 +- **数据驱动**:所有配置用 DataAsset,方便策划调整 +- **解耦设计**:引导系统不侵入现有逻辑 +- **接口隔离**:UI组件实现接口即可支持引导 +- **事件驱动**:使用委托而非轮询 + +### 9.2 性能优化 +- 引导模式下禁用不必要的Tick +- UI过滤用 Collapsed 而非 Hidden(减少渲染) +- 遮罩层使用单例复用 + +### 9.3 可扩展性 +- 预留自定义步骤类型 +- 支持蓝图扩展 TutorialStepAsset +- UI标签系统支持动态注册 + +--- + +## 十、示例流程图 + +``` +[游戏启动] + ↓ +[检查存档:是否完成引导] + ↓ 否 +[TutorialSubsystem::StartTutorial()] + ↓ +[Step 1: 家园对话] → DialogueAsset 播放 + ↓ 对话结束 +[Step 2: 出航按钮] → HomeUI.EnterTutorialMode({Btn_Sail}) + ↓ 点击出航 +[Step 3: 地图界面] → MapUI.EnterTutorialMode({MapNode_0}) + ↓ 点击节点 +[Loading...] + ↓ +[Step 4-10: 战斗流程] + ├→ Step 5: 移动引导 + ├→ Step 6: 钓鱼引导 + ├→ Step 7: 战斗1 + 收获 + ├→ Step 8: 对话提示 + ├→ Step 9: 战斗2(中间插入对话) + └→ Step 10: 收获(魔法书) + ↓ +[Step 11: 返航引导] → 显示返航按钮 + ↓ +[Loading...] + ↓ +[Step 12-15: 家园流程] + ├→ Step 13: 家园对话 + ├→ Step 14: 鱼市引导 + 任务提交 + └→ Step 15: 解锁功能 + ↓ +[引导完成] → 保存进度 → 退出引导模式 +``` + +--- + +## 总结 + +这套新手引导系统设计具有以下特点: + +✅ **非侵入式**:通过接口和子系统,不破坏现有代码结构 +✅ **数据驱动**:使用 DataAsset 配置所有步骤,策划友好 +✅ **灵活可扩展**:支持自定义步骤类型和条件 +✅ **UI双模式**:正常模式和引导模式无缝切换 +✅ **系统集成**:充分利用现有的对话、任务、地图、战斗系统 +✅ **性能友好**:按需加载,最小化运行时开销 + +建议按照分阶段实现计划逐步推进,优先完成核心框架和关键UI改造。 diff --git a/ProjectFish/Docs/TutorialSystemDesign_Simple.md b/ProjectFish/Docs/TutorialSystemDesign_Simple.md new file mode 100644 index 0000000..c08fae0 --- /dev/null +++ b/ProjectFish/Docs/TutorialSystemDesign_Simple.md @@ -0,0 +1,619 @@ +# 新手引导系统设计(简化版) + +## 一、核心思路 + +**一句话概括**:用一个 `TutorialManager` 管理引导流程,通过 Widget 的 `Tag` 控制显示/隐藏/高亮。 + +--- + +## 二、只需要3个东西 + +### 1. TutorialManager(一个管理类) + +```cpp +// TutorialManager.h +#pragma once + +#include "CoreMinimal.h" +#include "Subsystems/GameInstanceSubsystem.h" +#include "TutorialManager.generated.h" + +// 简单的步骤配置 +USTRUCT(BlueprintType) +struct FTutorialStep +{ + GENERATED_BODY() + + // 步骤ID + UPROPERTY(EditAnywhere, BlueprintReadWrite) + int32 StepID = 0; + + // 可见的Widget标签(其他都隐藏) + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TArray VisibleWidgetTags; + + // 高亮的Widget标签 + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TArray HighlightWidgetTags; + + // 提示文本(可选) + UPROPERTY(EditAnywhere, BlueprintReadWrite) + FText HintText; + + // 对话资产(可选) + UPROPERTY(EditAnywhere, BlueprintReadWrite) + class UDialogueAsset* DialogueAsset; +}; + +UCLASS() +class PROJECTFISH_API UTutorialManager : public UGameInstanceSubsystem +{ + GENERATED_BODY() + +public: + // 开始引导 + UFUNCTION(BlueprintCallable, Category = "Tutorial") + void StartTutorial(); + + // 进入下一步 + UFUNCTION(BlueprintCallable, Category = "Tutorial") + void NextStep(); + + // 完成引导 + UFUNCTION(BlueprintCallable, Category = "Tutorial") + void FinishTutorial(); + + // 是否在引导中 + UFUNCTION(BlueprintPure, Category = "Tutorial") + bool IsInTutorial() const { return bInTutorial; } + + // 获取当前步骤 + UFUNCTION(BlueprintPure, Category = "Tutorial") + int32 GetCurrentStep() const { return CurrentStepIndex; } + + // 检查Widget是否应该显示 + UFUNCTION(BlueprintPure, Category = "Tutorial") + bool ShouldShowWidget(FName WidgetTag) const; + + // 检查Widget是否应该高亮 + UFUNCTION(BlueprintPure, Category = "Tutorial") + bool ShouldHighlightWidget(FName WidgetTag) const; + + // 隐藏遮罩层 + UFUNCTION(BlueprintCallable, Category = "Tutorial") + void ShowMask(bool bShow); + +protected: + // 引导步骤配置(在编辑器中配置) + UPROPERTY(EditDefaultsOnly, Category = "Tutorial") + TArray TutorialSteps; + + UPROPERTY() + bool bInTutorial = false; + + UPROPERTY() + int32 CurrentStepIndex = -1; + +private: + void ApplyCurrentStep(); + void BroadcastStepChanged(); +}; +``` + +```cpp +// TutorialManager.cpp +#include "TutorialManager.h" + +void UTutorialManager::StartTutorial() +{ + bInTutorial = true; + CurrentStepIndex = 0; + ApplyCurrentStep(); +} + +void UTutorialManager::NextStep() +{ + if (!bInTutorial) return; + + CurrentStepIndex++; + if (CurrentStepIndex >= TutorialSteps.Num()) + { + FinishTutorial(); + return; + } + + ApplyCurrentStep(); +} + +void UTutorialManager::FinishTutorial() +{ + bInTutorial = false; + CurrentStepIndex = -1; + + // 保存到存档 + // GameInfoManager->SetTutorialCompleted(true); + + ShowMask(false); +} + +bool UTutorialManager::ShouldShowWidget(FName WidgetTag) const +{ + if (!bInTutorial) return true; // 正常模式全显示 + + if (!TutorialSteps.IsValidIndex(CurrentStepIndex)) + return true; + + const FTutorialStep& Step = TutorialSteps[CurrentStepIndex]; + + // 如果没有配置,默认显示 + if (Step.VisibleWidgetTags.Num() == 0) + return true; + + // 白名单模式:只显示列表中的 + return Step.VisibleWidgetTags.Contains(WidgetTag); +} + +bool UTutorialManager::ShouldHighlightWidget(FName WidgetTag) const +{ + if (!bInTutorial) return false; + + if (!TutorialSteps.IsValidIndex(CurrentStepIndex)) + return false; + + const FTutorialStep& Step = TutorialSteps[CurrentStepIndex]; + return Step.HighlightWidgetTags.Contains(WidgetTag); +} + +void UTutorialManager::ApplyCurrentStep() +{ + BroadcastStepChanged(); + + if (!TutorialSteps.IsValidIndex(CurrentStepIndex)) + return; + + const FTutorialStep& Step = TutorialSteps[CurrentStepIndex]; + + // 如果有对话,播放对话 + if (Step.DialogueAsset) + { + // 调用对话系统播放 + // DialogueSystem->PlayDialogue(Step.DialogueAsset); + } + + // 显示遮罩 + ShowMask(true); +} + +void UTutorialManager::BroadcastStepChanged() +{ + // 通知所有UI刷新 + // 可以用事件委托或直接遍历所有Widget +} + +void UTutorialManager::ShowMask(bool bShow) +{ + // 创建/显示/隐藏遮罩Widget + // 遮罩Widget是一个全屏的半透明黑色背景 +} +``` + +--- + +### 2. Widget上设置Tag + +在UMG编辑器中,给每个需要控制的Widget设置Tag: + +``` +HomeUI: + ├─ Btn_Sail (Tag: "Btn_Sail") + ├─ Btn_Market (Tag: "Btn_Market") + ├─ Btn_Shop (Tag: "Btn_Shop") + └─ Btn_Backpack (Tag: "Btn_Backpack") +``` + +--- + +### 3. Widget蓝图中检查是否显示 + +在每个Widget的蓝图中,添加简单逻辑: + +``` +Event Construct: + ├─ Get TutorialManager + ├─ Bind to OnStepChanged Event + └─ Call UpdateVisibility() + +UpdateVisibility(): + ├─ For each child widget: + │ ├─ Get widget tag + │ ├─ TutorialManager->ShouldShowWidget(tag) + │ └─ Set Visibility (Visible / Collapsed) + └─ Update Highlight state +``` + +--- + +## 三、15步引导配置(简单表格) + +在 `TutorialManager` 的 `TutorialSteps` 数组中配置: + +| 步骤 | VisibleWidgetTags | HighlightWidgetTags | 说明 | +|------|-------------------|---------------------|------| +| 0 | [] | [] | 初始对话(DialogueAsset配置) | +| 1 | [Btn_Sail] | [Btn_Sail] | 只显示出航按钮并高亮 | +| 2 | [MapNode_0] | [MapNode_0] | 地图只显示一个节点 | +| 3 | [] | [] | 进入Loading | +| 4 | [MovementHint] | [] | 显示移动提示 | +| 5 | [FishingHint] | [] | 显示钓鱼提示 | +| 6 | [BattleUI] | [] | 战斗界面(隐藏背包/返航) | +| ... | ... | ... | ... | + +**配置方式**:在编辑器的 `Project Settings -> Tutorial Manager` 中直接填表格。 + +--- + +## 四、UI改造(最简单的方式) + +### 方式1:蓝图中直接判断(推荐) + +在每个Widget的蓝图中: + +```blueprint +Event Construct: + ├─ GetGameInstance + ├─ GetSubsystem(TutorialManager) + ├─ Bind Event: OnStepChanged -> UpdateVisibility + └─ Call UpdateVisibility + +Function UpdateVisibility: + ├─ If TutorialManager->IsInTutorial(): + │ ├─ Set Btn_Sail Visibility: ShouldShowWidget("Btn_Sail") + │ ├─ Set Btn_Market Visibility: ShouldShowWidget("Btn_Market") + │ └─ ... + └─ Else: Show All +``` + +### 方式2:C++中统一处理 + +创建一个简单的基类: + +```cpp +// TutorialWidget.h +#pragma once + +#include "Blueprint/UserWidget.h" +#include "TutorialWidget.generated.h" + +UCLASS() +class UTutorialWidget : public UUserWidget +{ + GENERATED_BODY() + +public: + virtual void NativeConstruct() override; + +protected: + // 在子类中填写:Widget名 -> Tag的映射 + UPROPERTY(EditDefaultsOnly, Category = "Tutorial") + TMap WidgetTagMap; + + UFUNCTION() + void UpdateVisibility(); + +private: + UPROPERTY() + class UTutorialManager* TutorialManager; +}; +``` + +```cpp +// TutorialWidget.cpp +#include "TutorialWidget.h" +#include "TutorialManager.h" + +void UTutorialWidget::NativeConstruct() +{ + Super::NativeConstruct(); + + TutorialManager = GetGameInstance()->GetSubsystem(); + if (TutorialManager) + { + // 绑定步骤改变事件 + // TutorialManager->OnStepChanged.AddDynamic(this, &UTutorialWidget::UpdateVisibility); + } + + UpdateVisibility(); +} + +void UTutorialWidget::UpdateVisibility() +{ + if (!TutorialManager || !TutorialManager->IsInTutorial()) + { + // 正常模式:全部显示 + for (auto& Pair : WidgetTagMap) + { + if (Pair.Value) + Pair.Value->SetVisibility(ESlateVisibility::Visible); + } + return; + } + + // 引导模式:根据Tag判断 + for (auto& Pair : WidgetTagMap) + { + if (!Pair.Value) continue; + + bool bShouldShow = TutorialManager->ShouldShowWidget(Pair.Key); + Pair.Value->SetVisibility(bShouldShow ? ESlateVisibility::Visible : ESlateVisibility::Collapsed); + + // 高亮效果 + bool bShouldHighlight = TutorialManager->ShouldHighlightWidget(Pair.Key); + if (bShouldHighlight) + { + // 添加高亮动画或材质效果 + // PlayAnimation(HighlightAnim); + } + } +} +``` + +然后让 HomeUI 继承这个基类,在构造函数中填写映射: + +```cpp +// HomeUIWidget.cpp +void UHomeUIWidget::NativeConstruct() +{ + Super::NativeConstruct(); + + // 填写映射 + WidgetTagMap.Add(TEXT("Btn_Sail"), Btn_Sail); + WidgetTagMap.Add(TEXT("Btn_Market"), Btn_Market); + WidgetTagMap.Add(TEXT("Btn_Shop"), Btn_Shop); + WidgetTagMap.Add(TEXT("Btn_Backpack"), Btn_Backpack); +} +``` + +--- + +## 五、特殊情况处理 + +### 1. 战斗特殊配置 + +不需要新的配置类,直接在战斗开始时检查: + +```cpp +// AProjectFishGameMode.cpp +void AProjectFishGameMode::StartBattle() +{ + UTutorialManager* Tutorial = GetGameInstance()->GetSubsystem(); + + if (Tutorial && Tutorial->IsInTutorial()) + { + int32 Step = Tutorial->GetCurrentStep(); + + // 第一场战斗(步骤6) + if (Step == 6) + { + // 使用特定的鱼配置 + CurrentFish = LoadObject(..., TEXT("Tutorial_Fish_01")); + + // 强制掉落 + bForceDropItem = true; + ForcedDropItems.Add(TEXT("Item_Bass")); + } + // 第二场战斗(步骤9) + else if (Step == 9) + { + CurrentFish = LoadObject(..., TEXT("Tutorial_Fish_02")); + ForcedDropItems.Add(TEXT("Item_Bass")); + ForcedDropItems.Add(TEXT("Item_MagicBook")); + } + } +} +``` + +### 2. 地图单节点 + +```cpp +// FishingMapSubSystem.cpp +void UFishingMapSubSystem::GenerateMap() +{ + UTutorialManager* Tutorial = GetGameInstance()->GetSubsystem(); + + if (Tutorial && Tutorial->IsInTutorial() && Tutorial->GetCurrentStep() == 2) + { + // 只生成1个节点 + AllNodes.Empty(); + UFishingMapNode* Node = NewObject(); + Node->NodeType = EMapNodeType::Battle; + AllNodes.Add(Node); + return; + } + + // 正常生成 + GenerateMapWithConfig(DefaultMapConfig); +} +``` + +### 3. 强制拾取奖励 + +```cpp +// RewardWidget.cpp +void URewardWidget::NativeConstruct() +{ + Super::NativeConstruct(); + + UTutorialManager* Tutorial = GetGameInstance()->GetSubsystem(); + + if (Tutorial && Tutorial->IsInTutorial()) + { + int32 Step = Tutorial->GetCurrentStep(); + if (Step == 7 || Step == 10) // 两场战斗后 + { + // 隐藏关闭按钮,直到拾取完毕 + Btn_Close->SetVisibility(ESlateVisibility::Collapsed); + bMustCollectAll = true; + } + } +} + +void URewardWidget::OnAllItemsCollected() +{ + if (bMustCollectAll) + { + Btn_Close->SetVisibility(ESlateVisibility::Visible); + // 显示提示:"点击关闭继续" + } +} +``` + +--- + +## 六、遮罩和高亮效果(简单实现) + +### 遮罩Widget(WBP_TutorialMask) + +创建一个简单的UMG: +``` +Canvas Panel (全屏) +└─ Image (黑色,透明度0.7) +``` + +在 TutorialManager 中显示/隐藏它。 + +### 高亮效果(3种简单方案) + +**方案1:边框发光** +```cpp +// 给按钮添加一个Border,设置发光颜色 +Btn_Sail->SetBorderBrush(HighlightBrush); +``` + +**方案2:缩放动画** +``` +创建UMG动画: + 0.0s -> Scale 1.0 + 0.5s -> Scale 1.1 + 1.0s -> Scale 1.0 +循环播放 +``` + +**方案3:后处理材质** +给高亮Widget添加后处理效果(最炫酷,但稍复杂)。 + +--- + +## 七、完整流程示例 + +### 配置TutorialSteps(在编辑器中) + +```cpp +TutorialSteps[0]: + StepID: 0 + DialogueAsset: DA_Tutorial_Intro + VisibleWidgetTags: [] + HintText: "" + +TutorialSteps[1]: + StepID: 1 + VisibleWidgetTags: [Btn_Sail] + HighlightWidgetTags: [Btn_Sail] + HintText: "点击【出航】按钮" + +TutorialSteps[2]: + StepID: 2 + VisibleWidgetTags: [MapNode_0] + HighlightWidgetTags: [MapNode_0] + HintText: "选择一个地点" + +// ... 继续配置到步骤15 +``` + +### 调用流程 + +```cpp +// 游戏启动时 +void AMyGameInstance::Init() +{ + Super::Init(); + + UTutorialManager* Tutorial = GetSubsystem(); + + // 检查存档 + bool bCompletedTutorial = GameInfoManager->HasCompletedTutorial(); + if (!bCompletedTutorial) + { + Tutorial->StartTutorial(); + } +} + +// 出航按钮点击时 +void UHomeUIWidget::OnSailButtonClicked() +{ + UTutorialManager* Tutorial = GetGameInstance()->GetSubsystem(); + + if (Tutorial && Tutorial->IsInTutorial() && Tutorial->GetCurrentStep() == 1) + { + // 完成步骤1,进入步骤2 + Tutorial->NextStep(); + } + + // 打开地图 + OpenMapUI(); +} + +// 地图节点点击时 +void UMapWidget::OnNodeClicked() +{ + UTutorialManager* Tutorial = GetGameInstance()->GetSubsystem(); + + if (Tutorial && Tutorial->IsInTutorial() && Tutorial->GetCurrentStep() == 2) + { + Tutorial->NextStep(); + } + + // 进入战斗 + StartBattle(); +} +``` + +--- + +## 八、总结 + +### 只需要做3件事: + +1. **创建 TutorialManager**(1个类,~200行代码) +2. **给UI Widget设置Tag**(在UMG编辑器中操作) +3. **配置TutorialSteps表格**(在编辑器中填数据) + +### 改造工作量: + +- ✅ 新增代码:2个文件(TutorialManager + TutorialWidget基类) +- ✅ UI改造:继承基类或蓝图中加几行代码 +- ✅ 特殊逻辑:在现有代码中加if判断 +- ✅ 总代码量:<500行 + +### 优势: + +- 🟢 **极简**:只有1个核心类 +- 🟢 **直观**:配置就是一个表格 +- 🟢 **灵活**:可蓝图可C++ +- 🟢 **低侵入**:不破坏现有结构 + +--- + +## 九、文件结构 + +``` +Source/ProjectFish/Tutorial/ +├── TutorialManager.h # 管理类(核心) +├── TutorialManager.cpp +├── TutorialWidget.h # UI基类(可选) +└── TutorialWidget.cpp + +Content/UI/Tutorial/ +└── WBP_TutorialMask.uasset # 遮罩Widget +``` + +就这么简单!🎉 diff --git a/ProjectFish/Plugins/DeskMode/Binaries/Win64/UnrealEditor-DeskMode.dll b/ProjectFish/Plugins/DeskMode/Binaries/Win64/UnrealEditor-DeskMode.dll index 8ed9fe7..c9105af 100644 Binary files a/ProjectFish/Plugins/DeskMode/Binaries/Win64/UnrealEditor-DeskMode.dll and b/ProjectFish/Plugins/DeskMode/Binaries/Win64/UnrealEditor-DeskMode.dll differ diff --git a/ProjectFish/Plugins/Dialogue/Binaries/Win64/UnrealEditor-Dialogue.dll b/ProjectFish/Plugins/Dialogue/Binaries/Win64/UnrealEditor-Dialogue.dll index 88547cb..ef9686e 100644 Binary files a/ProjectFish/Plugins/Dialogue/Binaries/Win64/UnrealEditor-Dialogue.dll and b/ProjectFish/Plugins/Dialogue/Binaries/Win64/UnrealEditor-Dialogue.dll differ diff --git a/ProjectFish/Plugins/Dialogue/Binaries/Win64/UnrealEditor-DialogueEditor.dll b/ProjectFish/Plugins/Dialogue/Binaries/Win64/UnrealEditor-DialogueEditor.dll index 33fbd01..c1c4824 100644 Binary files a/ProjectFish/Plugins/Dialogue/Binaries/Win64/UnrealEditor-DialogueEditor.dll and b/ProjectFish/Plugins/Dialogue/Binaries/Win64/UnrealEditor-DialogueEditor.dll differ diff --git a/ProjectFish/Plugins/Dialogue/Source/Dialogue/Private/DialogueAsset.cpp b/ProjectFish/Plugins/Dialogue/Source/Dialogue/Private/DialogueAsset.cpp index 4517da6..4bc288b 100644 --- a/ProjectFish/Plugins/Dialogue/Source/Dialogue/Private/DialogueAsset.cpp +++ b/ProjectFish/Plugins/Dialogue/Source/Dialogue/Private/DialogueAsset.cpp @@ -67,7 +67,7 @@ FText UDialogueAsset::GetNextNodeText() } else { - UE_LOG(LogTemp, Warning, TEXT("DialogueNode has no output pin")); + OnDialogueComplete.Broadcast(); } return FText::FromString(TEXT("")); } diff --git a/ProjectFish/Plugins/Dialogue/Source/Dialogue/Public/DialogueAsset.h b/ProjectFish/Plugins/Dialogue/Source/Dialogue/Public/DialogueAsset.h index f377169..6b011fb 100644 --- a/ProjectFish/Plugins/Dialogue/Source/Dialogue/Public/DialogueAsset.h +++ b/ProjectFish/Plugins/Dialogue/Source/Dialogue/Public/DialogueAsset.h @@ -93,6 +93,7 @@ public: }; DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FNeedPlayeSelectDelegate, const TArray&, Options); +DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnDialogueComplete); /** * */ @@ -115,6 +116,8 @@ public: public: UPROPERTY(BlueprintAssignable, Category=Dialogue) FNeedPlayeSelectDelegate OnNeedPlayerSelect; + UPROPERTY(BlueprintAssignable, Category=Dialogue) + FOnDialogueComplete OnDialogueComplete; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Dialogue) TArray NodeDatas; diff --git a/ProjectFish/Source/ProjectFish/Data/ContainerInfo.cpp b/ProjectFish/Source/ProjectFish/Data/ContainerInfo.cpp index 2de4af8..3f160c8 100644 --- a/ProjectFish/Source/ProjectFish/Data/ContainerInfo.cpp +++ b/ProjectFish/Source/ProjectFish/Data/ContainerInfo.cpp @@ -84,8 +84,8 @@ TSet UContainerInfo::GetOverlapOtherItem(FContainerItem Item) if (Item.IsSlotUsing(FIntPoint(x, y))) { - if (ContainerShape->IsSlotActive(Item.ContainerStartPos.X + x, Item.ContainerStartPos.Y + y) && - SlotItems[FIntPoint(x + Item.ContainerStartPos.X, y + Item.ContainerStartPos.Y)] != -1 + if (ContainerShape->IsSlotActive(Item.ContainerStartPos.X + x, Item.ContainerStartPos.Y + y) && SlotItems.Contains(FIntPoint(x + Item.ContainerStartPos.X, y + Item.ContainerStartPos.Y)) + ///SlotItems[FIntPoint(x + Item.ContainerStartPos.X, y + Item.ContainerStartPos.Y)] != -1 ) { OverlapItems.Add(SlotItems[FIntPoint(x + Item.ContainerStartPos.X, y + Item.ContainerStartPos.Y)] ); diff --git a/ProjectFish/Source/ProjectFish/Data/PlayerInfoSaveGame.cpp b/ProjectFish/Source/ProjectFish/Data/PlayerInfoSaveGame.cpp index 60e607b..98ab076 100644 --- a/ProjectFish/Source/ProjectFish/Data/PlayerInfoSaveGame.cpp +++ b/ProjectFish/Source/ProjectFish/Data/PlayerInfoSaveGame.cpp @@ -2,3 +2,21 @@ #include "PlayerInfoSaveGame.h" + +#include "ProjectFish/Gameplay/Subsystem/GameInfoManager.h" + +void UPlayerInfoSaveGame::BeginDestroy() +{ + Super::BeginDestroy(); +} + +void UPlayerInfoSaveGame::SetShipContainerItems(TArray NewItems) +{ + ShipContainerItems = NewItems; + + if (GetOuter()) + { + GetOuter()->GetWorld()->GetGameInstance()->GetSubsystem()->SaveGameInfo(); + OnShipContainerUpdate.Broadcast(); + } +} diff --git a/ProjectFish/Source/ProjectFish/Data/PlayerInfoSaveGame.h b/ProjectFish/Source/ProjectFish/Data/PlayerInfoSaveGame.h index 44aae4d..7ee9f24 100644 --- a/ProjectFish/Source/ProjectFish/Data/PlayerInfoSaveGame.h +++ b/ProjectFish/Source/ProjectFish/Data/PlayerInfoSaveGame.h @@ -9,6 +9,7 @@ #include "ProjectFish/Quest/QuestTypes.h" #include "PlayerInfoSaveGame.generated.h" +DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnShipContainerUpdate); /** * */ @@ -17,6 +18,11 @@ class PROJECTFISH_API UPlayerInfoSaveGame : public USaveGame { GENERATED_BODY() public: + + virtual void BeginDestroy() override; + UPROPERTY(SaveGame, BlueprintReadWrite, meta = (ToolTip = "开启教程模式")) + bool TutorialMode = true; + UPROPERTY(SaveGame, BlueprintReadWrite, meta = (ToolTip = "船只资源")) FPrimaryAssetId ShipAssetID; @@ -28,7 +34,7 @@ public: FPrimaryAssetId ShipContainerShapeID; // 船舱物品存档数据 - UPROPERTY(SaveGame, BlueprintReadWrite, meta = (ToolTip = "船舱物品数据")) + UPROPERTY(SaveGame, BlueprintReadOnly, meta = (ToolTip = "船舱物品数据")) TArray ShipContainerItems; UPROPERTY(SaveGame, BlueprintReadWrite, meta = (ToolTip = "仓库资源")) @@ -52,4 +58,12 @@ public: //已战胜的鱼 UPROPERTY(SaveGame, BlueprintReadWrite, meta = (ToolTip = "被击败的鱼")) TArray Fish_defeated_IDS; + +public: + UPROPERTY(BlueprintAssignable, Category = "PlayerInfoSaveGame") + FOnShipContainerUpdate OnShipContainerUpdate; + +protected: + UFUNCTION(BlueprintCallable, Category= "PlayerInfoSaveGame") + void SetShipContainerItems(TArray NewItems); }; diff --git a/ProjectFish/Source/ProjectFish/DataAsset/QuestAsset.cpp b/ProjectFish/Source/ProjectFish/DataAsset/QuestAsset.cpp index d9e124f..d766961 100644 --- a/ProjectFish/Source/ProjectFish/DataAsset/QuestAsset.cpp +++ b/ProjectFish/Source/ProjectFish/DataAsset/QuestAsset.cpp @@ -3,9 +3,11 @@ #include "QuestAsset.h" #if WITH_EDITOR +#include "DialogueAsset.h" #include "AssetRegistry/AssetRegistryModule.h" #include "AssetRegistry/AssetData.h" #include "Misc/MessageDialog.h" +#include "ProjectFish/Gameplay/Subsystem/QuestManager.h" void UQuestAsset::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { @@ -84,5 +86,31 @@ void UQuestAsset::PostDuplicate(bool bDuplicateForPIE) QuestID = MaxQuestID + 1; } } + + #endif +void UQuestAsset::OnDialogueComplete() +{ + bDialogueComplete = true; + QuestManager->AcceptQuest(this, true); +} + +void UQuestAsset::Initialize(class UQuestManager* InQuestManager) +{ + this->QuestManager = InQuestManager; + bDialogueComplete = false; + if (IsValid(DialogueTrigger)) + { + DialogueTrigger->OnDialogueComplete.AddDynamic(this, &UQuestAsset::OnDialogueComplete); + } +} + +bool UQuestAsset::Acceptable() +{ + if (IsValid(DialogueTrigger) &&bDialogueComplete) + { + return true; + } + return AcceptAble; +} \ No newline at end of file diff --git a/ProjectFish/Source/ProjectFish/DataAsset/QuestAsset.h b/ProjectFish/Source/ProjectFish/DataAsset/QuestAsset.h index a4bdc56..8b71132 100644 --- a/ProjectFish/Source/ProjectFish/DataAsset/QuestAsset.h +++ b/ProjectFish/Source/ProjectFish/DataAsset/QuestAsset.h @@ -23,6 +23,11 @@ public: virtual void PostDuplicate(bool bDuplicateForPIE) override; #endif + UFUNCTION() + void OnDialogueComplete(); + + void Initialize(class UQuestManager* InQuestManager); + bool Acceptable(); /** 任务编号ID */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Quest|Basic") int32 QuestID = 0; @@ -31,17 +36,20 @@ public: UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Quest|Basic") FText QuestName; + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Quest|Basic", meta = (ToolTip = "当满足所有条件时,是否可接受")) + bool AcceptAble = true; + /** 任务描述 */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Quest|Basic", meta = (MultiLine = true)) FText QuestDescription; - /** 前置任务ID列表() */ + /** 前置触发任务ID列表() */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Quest|Trigger", meta = (ToolTip = "完成这些任务后才能触发当前任务, 不填写默认激活接受")) TArray PrerequisiteQuestIDs; - /** 任务触发条件(预留,可在蓝图中实现) */ + /** 对话完成后触发 */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Quest|Trigger") - FString TriggerCondition; + class UDialogueAsset* DialogueTrigger; /** 任务目标列表 */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Quest|Objectives") @@ -54,5 +62,9 @@ public: /** 任务完成条件描述(预留,可在蓝图中实现) */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Quest|Completion") FString CompletionCondition; - + +private: + UPROPERTY() + class UQuestManager* QuestManager; + bool bDialogueComplete; }; diff --git a/ProjectFish/Source/ProjectFish/Definations.h b/ProjectFish/Source/ProjectFish/Definations.h index 34dbc27..6d299b5 100644 --- a/ProjectFish/Source/ProjectFish/Definations.h +++ b/ProjectFish/Source/ProjectFish/Definations.h @@ -478,5 +478,44 @@ public: int32 QuestID; }; +USTRUCT(BlueprintType) +struct FTutorialUIControlParam +{ + GENERATED_BODY() +public: + UPROPERTY(EditAnywhere, BlueprintReadOnly) + TSubclassOf TutorialControlUIClass; + UPROPERTY(EditAnywhere, BlueprintReadOnly) + TArray HiddenUINames; + // UPROPERTY(EditAnywhere, BlueprintReadOnly) + // TArray ShowUINames; +}; +USTRUCT(BlueprintType) +struct FTutorialStep: public FTableRowBase +{ + GENERATED_BODY() +public: + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Tutorial Asset") + FName StepName; + //教程对话 + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Tutorial Asset") + class UDialogueAsset* Dialogue; + //教程任务 + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Tutorial Asset") + class UQuestAsset* Quest; + //需要显示的ui + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Tutorial Asset") + TSubclassOf TutorialUI; + //控制ui的显示 + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Tutorial Asset") + TArray UIControlParams; + + //引导task + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Tutorial Asset") + TSubclassOf TutorialTaskClass; + + UPROPERTY() + UTutorialTask_Base* TutorialTaskObject; +}; diff --git a/ProjectFish/Source/ProjectFish/Gameplay/FishGameInstance.cpp b/ProjectFish/Source/ProjectFish/Gameplay/FishGameInstance.cpp new file mode 100644 index 0000000..9a2c2de --- /dev/null +++ b/ProjectFish/Source/ProjectFish/Gameplay/FishGameInstance.cpp @@ -0,0 +1,10 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "FishGameInstance.h" + +void UFishGameInstance::OnStart() +{ + Super::OnStart(); + InitTutorialSteps(); +} diff --git a/ProjectFish/Source/ProjectFish/Gameplay/FishGameInstance.h b/ProjectFish/Source/ProjectFish/Gameplay/FishGameInstance.h new file mode 100644 index 0000000..79d5bcd --- /dev/null +++ b/ProjectFish/Source/ProjectFish/Gameplay/FishGameInstance.h @@ -0,0 +1,22 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Engine/GameInstance.h" +#include "FishGameInstance.generated.h" + +/** + * + */ +UCLASS(Blueprintable) +class PROJECTFISH_API UFishGameInstance : public UGameInstance +{ + GENERATED_BODY() +public: + /** GameInstace */ + virtual void OnStart() override; + + UFUNCTION(BlueprintImplementableEvent) + void InitTutorialSteps(); +}; diff --git a/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/FishingRodConfigSubsystem.cpp b/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/FishingRodConfigSubsystem.cpp index 8397843..d914981 100644 --- a/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/FishingRodConfigSubsystem.cpp +++ b/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/FishingRodConfigSubsystem.cpp @@ -17,6 +17,11 @@ void UFishingRodConfigSubsystem::CreateFishingRodInventory(class UShapeAsset* Fi playerInventoryConfig = DuplicateObject(PlayerBagConfig, this); } +void UFishingRodConfigSubsystem::SetFishingRodConfig(UBagConfigAsset* FishingRodConfig) +{ + fishingRodInventoryConfig = FishingRodConfig; +} + UBagConfigAsset* UFishingRodConfigSubsystem::GetFishingRodInventory() { return this->fishingRodInventoryConfig; diff --git a/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/FishingRodConfigSubsystem.h b/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/FishingRodConfigSubsystem.h index 0bd075f..69d223f 100644 --- a/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/FishingRodConfigSubsystem.h +++ b/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/FishingRodConfigSubsystem.h @@ -18,6 +18,8 @@ class PROJECTFISH_API UFishingRodConfigSubsystem : public UGameInstanceSubsystem public: UFUNCTION(BlueprintCallable, Category = "PlayerInventorySubsystem") void CreateFishingRodInventory(class UShapeAsset* FishingRodShape, class UBagConfigAsset* PlayerBagConfig); + UFUNCTION(BlueprintCallable, Category = "PlayerInventorySubsystem") + void SetFishingRodConfig(UBagConfigAsset* FishingRodConfig); UFUNCTION(BlueprintPure, Category = "PlayerInventorySubsystem") UBagConfigAsset* GetFishingRodInventory(); diff --git a/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/GameInfoManager.cpp b/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/GameInfoManager.cpp index fcea9b0..43378ea 100644 --- a/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/GameInfoManager.cpp +++ b/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/GameInfoManager.cpp @@ -24,9 +24,10 @@ void UGameInfoManager::NewSaveGame() void UGameInfoManager::SaveGameInfo() { - if (!IsValid(PlayerInfo.Get())) + if (!IsValid(PlayerInfo)) { - PlayerInfo = NewObject(this); + + PlayerInfo = NewObject(GetGameInstance()); } // 保存任务数据 if (UQuestManager* QuestManager = GetGameInstance()->GetSubsystem()) @@ -34,7 +35,7 @@ void UGameInfoManager::SaveGameInfo() PlayerInfo->QuestSaveData = QuestManager->GetQuestSaveData(); } - if (UGameplayStatics::SaveGameToSlot(PlayerInfo.Get(), SaveGameSlotName, 0)) + if (UGameplayStatics::SaveGameToSlot(PlayerInfo, SaveGameSlotName, 0)) { UE_LOG(LogTemp, Warning, TEXT("存档保存成功")); } @@ -49,7 +50,7 @@ bool UGameInfoManager::LoadGameInfo() if (UGameplayStatics::DoesSaveGameExist(SaveGameSlotName, 0)) { PlayerInfo = Cast(UGameplayStatics::LoadGameFromSlot(SaveGameSlotName, 0)); - + PlayerInfo->Rename(nullptr, this); // 加载任务数据 if (UQuestManager* QuestManager = GetGameInstance()->GetSubsystem()) { @@ -74,7 +75,8 @@ bool UGameInfoManager::LoadGameInfo() void UGameInfoManager::CreateGameInfo(UContainerInfo* ShipContainer, UContainerInfo* PlayerContainer) { - PlayerInfo = NewObject(this); + if (!IsValid(PlayerInfo)) + PlayerInfo = NewObject(GetGameInstance()); PlayerInfo->ShipContainerItems = ShipContainer->GetSaveData(); PlayerInfo->ShipContainerShapeID = ShipContainer->ContainerShape->GetPrimaryAssetId(); @@ -93,7 +95,7 @@ void UGameInfoManager::CreateGameInfoAndSave(UShapeAsset* ShipContainerShape, US PlayerContainer->InitContainerByShape(PlayerContainerShape); //创建要保存的额存档信息 - PlayerInfo = NewObject(this); + PlayerInfo = NewObject(GetGameInstance()); PlayerInfo->ShipContainerItems = ShipContainer->GetSaveData(); PlayerInfo->ShipContainerShapeID = ShipContainer->ContainerShape->GetPrimaryAssetId(); @@ -103,6 +105,27 @@ void UGameInfoManager::CreateGameInfoAndSave(UShapeAsset* ShipContainerShape, US SaveGameInfo(); } +bool UGameInfoManager::IsTutorialMode() +{ + if (!IsValid(PlayerInfo)) + { + return true; + } + else + { + return PlayerInfo->TutorialMode; + } +} + +void UGameInfoManager::SetTutorialMode(bool bTutorialMode) +{ + if (!IsValid(PlayerInfo)) + { + PlayerInfo = NewObject(GetGameInstance()); + } + PlayerInfo->TutorialMode = bTutorialMode; +} + bool UGameInfoManager::IsFishDefeated(int32 FishID) { if (!IsValid(PlayerInfo)) @@ -116,7 +139,7 @@ void UGameInfoManager::AddDefeatedFish(int32 FishID) { if (!IsValid(PlayerInfo)) { - PlayerInfo = NewObject(this); + PlayerInfo = NewObject(GetGameInstance()); } PlayerInfo->Fish_defeated_IDS.Add(FishID); SaveGameInfo(); diff --git a/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/GameInfoManager.h b/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/GameInfoManager.h index adf74ac..26cf5be 100644 --- a/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/GameInfoManager.h +++ b/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/GameInfoManager.h @@ -3,6 +3,7 @@ #pragma once #include "CoreMinimal.h" +#include "DialogueAsset.h" #include "ProjectFish/Data/PlayerInfoSaveGame.h" #include "Subsystems/GameInstanceSubsystem.h" #include "GameInfoManager.generated.h" @@ -30,14 +31,22 @@ public: UFUNCTION(BlueprintCallable) void CreateGameInfoAndSave(UShapeAsset* ShipContainerShape, UShapeAsset* PlayerContainerShape); + UFUNCTION(BlueprintPure, Category= "GameInfo") + bool IsTutorialMode(); + + UFUNCTION(Blueprintable, Category= "GameInfo") + void SetTutorialMode(bool bTutorialMode); + UFUNCTION(BlueprintPure) bool IsFishDefeated(int32 FishID); UFUNCTION(BlueprintCallable) void AddDefeatedFish(int32 FishID); + + TObjectPtr GetPlayerInfo() {return PlayerInfo;} protected: - UPROPERTY(BlueprintReadOnly) - TObjectPtr PlayerInfo; + UPROPERTY(BlueprintReadWrite) + class UPlayerInfoSaveGame* PlayerInfo; static FString SaveGameSlotName; }; diff --git a/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/QuestManager.cpp b/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/QuestManager.cpp index 010e293..72e6739 100644 --- a/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/QuestManager.cpp +++ b/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/QuestManager.cpp @@ -2,6 +2,7 @@ #include "QuestManager.h" +#include "DialogueAsset.h" #include "GameInfoManager.h" #include "AssetRegistry/AssetRegistryModule.h" #include "Engine/AssetManager.h" @@ -38,6 +39,7 @@ void UQuestManager::LoadAllQuests() LoadHandle->GetLoadedAssets(Assets); // 获取所有加载成功的资源 for (auto questAsset : Assets) { + Cast(questAsset)->Initialize(this); QuestAssets.Add(Cast(questAsset)); } } @@ -76,14 +78,7 @@ bool UQuestManager::AcceptQuest(UQuestAsset* QuestAsset, bool bSaveGameInfo ) return false; } - // 创建运行时数据 - FQuestRuntimeData RuntimeData = CreateRuntimeData(QuestAsset); - RuntimeData.State = EQuestState::InProgress; - ActiveQuestsMap.Add(QuestAsset->QuestID, RuntimeData); - - // 触发事件 - OnQuestStateChanged.Broadcast(QuestAsset->QuestID, EQuestState::InProgress); - UE_LOG(LogTemp, Warning, TEXT("Quest %s Accepted"), *QuestAsset->QuestName.ToString()); + AcceptQuestInternal(QuestAsset); if (bSaveGameInfo) { @@ -94,6 +89,22 @@ bool UQuestManager::AcceptQuest(UQuestAsset* QuestAsset, bool bSaveGameInfo ) return true; } +void UQuestManager::AcceptQuestInternal(UQuestAsset* QuestAsset) +{ + if (QuestAsset) + { + // 创建运行时数据 + FQuestRuntimeData RuntimeData = CreateRuntimeData(QuestAsset); + RuntimeData.State = EQuestState::InProgress; + ActiveQuestsMap.Add(QuestAsset->QuestID, RuntimeData); + + // 触发事件 + OnQuestStateChanged.Broadcast(QuestAsset->QuestID, EQuestState::InProgress); + UE_LOG(LogTemp, Warning, TEXT("Quest %s Accepted"), *QuestAsset->QuestName.ToString()); + } + +} + bool UQuestManager::FollowQuest(int32 QuestID, bool Follow) { FQuestRuntimeData* RuntimeData = ActiveQuestsMap.Find(QuestID); @@ -330,17 +341,21 @@ bool UQuestManager::CanAcceptQuest(UQuestAsset* QuestAsset) const { return false; } - - // 检查所有前置任务是否完成 - for (int32 PrerequisiteID : QuestAsset->PrerequisiteQuestIDs) + + if (QuestAsset->AcceptAble) { - if (!CompletedQuestIDs.Contains(PrerequisiteID)) + // 检查所有前置任务是否完成 + for (int32 PrerequisiteID : QuestAsset->PrerequisiteQuestIDs) { - return false; + if (!CompletedQuestIDs.Contains(PrerequisiteID)) + { + return false; + } } + } - - return true; + return QuestAsset->Acceptable(); + } TArray UQuestManager::GetQuestSaveData() const @@ -415,7 +430,6 @@ void UQuestManager::LoadFromSaveData(const TArray& SaveData) } } - } CheckAcceptableQuests(true); diff --git a/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/QuestManager.h b/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/QuestManager.h index c82292c..5b58b71 100644 --- a/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/QuestManager.h +++ b/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/QuestManager.h @@ -55,10 +55,13 @@ public: UFUNCTION(BlueprintCallable, Category = "Quest") void CheckAcceptableQuests(bool bSaveGameInfo = false); - /** 接受任务 */ + /** 检测并接受任务 */ UFUNCTION(BlueprintCallable, Category = "Quest") bool AcceptQuest(UQuestAsset* QuestAsset, bool bSaveGameInfo = false); + UFUNCTION(BlueprintCallable, Category = "Quest") + void AcceptQuestInternal(UQuestAsset* QuestAsset); + /** 接受任务 */ UFUNCTION(BlueprintCallable, Category = "Quest") bool FollowQuest(int32 QuestID, bool Follow); diff --git a/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/TutorialManagerSubsystem.cpp b/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/TutorialManagerSubsystem.cpp new file mode 100644 index 0000000..86bd884 --- /dev/null +++ b/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/TutorialManagerSubsystem.cpp @@ -0,0 +1,174 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "TutorialManagerSubsystem.h" + +#include "GameInfoManager.h" +#include "QuestManager.h" +#include "Blueprint/UserWidget.h" +#include "Blueprint/WidgetBlueprintLibrary.h" +#include "Engine/AssetManager.h" +#include "Engine/StreamableManager.h" +#include "Kismet/GameplayStatics.h" +#include "ProjectFish/DataAsset/QuestAsset.h" +#include "ProjectFish/Gameplay/TutorialSystem/TutorialTask_Base.h" +#include "ProjectFish/Widget/DialogueWidget_Base.h" +#include "ProjectFish/Widget/TutorialControlWidget_Base.h" + +void UTutorialManagerSubsystem::Initialize(FSubsystemCollectionBase& Collection) +{ + Super::Initialize(Collection); + Collection.InitializeDependency(); + + DialogueUIPath = (TEXT("/Game/UI/Dialogue/UMG_DialogueWindow.UMG_DialogueWindow_C")); +} + +void UTutorialManagerSubsystem::InitSteps(TArray Steps) +{ + TutorialSteps = Steps; +} + +void UTutorialManagerSubsystem::ApplyCurrentTutorialStep() +{ + if (TutorialSteps.Num() > CurrentTutorialStep ) + { + UE_LOG(LogTemp, Warning, TEXT("执行 CurrentTutorialStep = %d || name = %s "), CurrentTutorialStep, *GetCurrentTutorialStep().StepName.ToString() ); + bTutorialing = true; + FTutorialStep CurrentStep = TutorialSteps[CurrentTutorialStep]; + if (IsValid(CurrentStep.Dialogue)) + { + ShowDialogue(); + } + if (IsValid(CurrentStep.Quest)) + { + AccepAndFollowQuest(); + } + if (IsValid(CurrentStep.TutorialUI)) + { + ShowTutorialUI(); + } + if (CurrentStep.UIControlParams.Num() != 0) + { + ControlUI(); + } + if (IsValid(CurrentStep.TutorialTaskClass)) + { + CreateTask(); + } + } + else + { + UE_LOG(LogTemp, Warning, TEXT("所有教程已结束")); + GetGameInstance()->GetSubsystem()->SetTutorialMode(false); + } +} + +void UTutorialManagerSubsystem::CompleteTutorialStep() +{ + if (bTutorialing) + { + if (TutorialTaskObject) + { + TutorialTaskObject->BeforTutorialComplete(); + TutorialTaskObject->ConditionalBeginDestroy(); + TutorialTaskObject = nullptr; + } + if (TutorialWidget) + { + TutorialWidget->RemoveFromViewport(); + TutorialWidget = nullptr; + } + UE_LOG(LogTemp, Warning, TEXT(" 教程进行下一步")); + OnStepComplete.Broadcast(CurrentTutorialStep); + CurrentTutorialStep++; + ApplyCurrentTutorialStep(); + } + +} + +FTutorialStep UTutorialManagerSubsystem::GetCurrentTutorialStep() const +{ + return TutorialSteps[CurrentTutorialStep]; +} + +void UTutorialManagerSubsystem::SetTutorialCompleteOnLevelLoaded() +{ + LevelLoadedDelegateHandle = FCoreUObjectDelegates::PostLoadMapWithWorld.AddUObject( + this, + &UTutorialManagerSubsystem::OnLevelLoaded + ); +} + +void UTutorialManagerSubsystem::OnLevelLoaded(UWorld* World) +{ + FCoreUObjectDelegates::PostLoadMapWithWorld.Remove(LevelLoadedDelegateHandle); + CompleteTutorialStep(); +} + +void UTutorialManagerSubsystem::OnDialogueCompleted() +{ + DialogueWidget->RemoveFromParent(); + CompleteTutorialStep(); +} + + + +void UTutorialManagerSubsystem::ShowDialogue() +{ + FTutorialStep CurrentStep = TutorialSteps[CurrentTutorialStep]; + UClass* WidgetClass = LoadClass(this, *DialogueUIPath); + if (WidgetClass) + { + DialogueWidget = CreateWidget(GetWorld(), WidgetClass); + if (DialogueWidget) + { + DialogueWidget->DialogueAsset = CurrentStep.Dialogue; + DialogueWidget->DialogueAsset->OnDialogueComplete.AddDynamic(this, &UTutorialManagerSubsystem::OnDialogueCompleted); + DialogueWidget->AddToViewport(99); + UGameplayStatics::GetPlayerController(GetWorld(), 0)->SetShowMouseCursor(true); + } + } + else + { + UE_LOG(LogTemp, Error, TEXT("dialogue path is error")); + } +} + +void UTutorialManagerSubsystem::AccepAndFollowQuest() +{ + FTutorialStep CurrentStep = TutorialSteps[CurrentTutorialStep]; + GetGameInstance()->GetSubsystem()->AcceptQuestInternal(CurrentStep.Quest); + GetGameInstance()->GetSubsystem()->FollowQuest(CurrentStep.Quest->QuestID, true); + CompleteTutorialStep(); +} + +void UTutorialManagerSubsystem::ShowTutorialUI() +{ + FTutorialStep CurrentStep = TutorialSteps[CurrentTutorialStep]; + TutorialWidget = CreateWidget(GetWorld(), CurrentStep.TutorialUI); + TutorialWidget->AddToViewport(99); +} + +void UTutorialManagerSubsystem::ControlUI() +{ + FTutorialStep CurrentStep = TutorialSteps[CurrentTutorialStep]; + + for (auto ControlParam: CurrentStep.UIControlParams) + { + TArray FoundWidgets; + UWidgetBlueprintLibrary::GetAllWidgetsOfClass(this, FoundWidgets, ControlParam.TutorialControlUIClass, false); + for (auto Widget: FoundWidgets) + { + Cast(Widget)->TutorialHideUI(ControlParam.HiddenUINames); + //Cast(Widget)->TutorialShowUI(ControlParam.ShowUINames); + } + } + +} + +void UTutorialManagerSubsystem::CreateTask() +{ + FTutorialStep CurrentStep = TutorialSteps[CurrentTutorialStep]; + TutorialTaskObject = NewObject(this, CurrentStep.TutorialTaskClass); + TutorialTaskObject->Execute(GetWorld()->GetAuthGameMode()); +} diff --git a/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/TutorialManagerSubsystem.h b/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/TutorialManagerSubsystem.h new file mode 100644 index 0000000..fd164e7 --- /dev/null +++ b/ProjectFish/Source/ProjectFish/Gameplay/Subsystem/TutorialManagerSubsystem.h @@ -0,0 +1,69 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Subsystems/GameInstanceSubsystem.h" +#include "TutorialManagerSubsystem.generated.h" + + +struct FTutorialStep; +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnStepComplete, int32, CompleteIndex); +/** + * + */ +UCLASS(BlueprintType) +class PROJECTFISH_API UTutorialManagerSubsystem : public UGameInstanceSubsystem +{ + GENERATED_BODY() +public: + /** GameInstanceSubsystem */ + virtual void Initialize(FSubsystemCollectionBase& Collection) override; + + + UFUNCTION(BlueprintCallable, Category= TutorialSubsystem) + void InitSteps(TArray Steps); + + UFUNCTION(BlueprintCallable, Category= TutorialSubsystem) + void ApplyCurrentTutorialStep(); + UFUNCTION(BlueprintCallable, Category= TutorialSubsystem) + void CompleteTutorialStep(); + UFUNCTION(BlueprintPure, Category= TutorialSubsystem) + FTutorialStep GetCurrentTutorialStep() const; + + UFUNCTION(BlueprintCallable, Category= TutorialSubsystem) + void SetTutorialCompleteOnLevelLoaded(); + void OnLevelLoaded(UWorld* World); +protected: + + //显示对话框UI + void ShowDialogue(); + UFUNCTION() + void OnDialogueCompleted(); + //接受任务 + void AccepAndFollowQuest(); + //显示教程UI + void ShowTutorialUI(); + //控制指定UI + void ControlUI(); + //创建task + void CreateTask(); +protected: + FDelegateHandle LevelLoadedDelegateHandle; + UPROPERTY(BlueprintAssignable) + FOnStepComplete OnStepComplete; + UPROPERTY(BlueprintReadOnly) + bool bTutorialing; + + TArray TutorialSteps; + int32 CurrentTutorialStep = 0; + FString DialogueUIPath; + UPROPERTY() + class UDialogueWidget_Base* DialogueWidget; + + UPROPERTY() + UUserWidget* TutorialWidget; + + UPROPERTY() + class UTutorialTask_Base* TutorialTaskObject; +}; diff --git a/ProjectFish/Source/ProjectFish/Gameplay/TutorialSystem/TutorialTask_Base.cpp b/ProjectFish/Source/ProjectFish/Gameplay/TutorialSystem/TutorialTask_Base.cpp new file mode 100644 index 0000000..a334d51 --- /dev/null +++ b/ProjectFish/Source/ProjectFish/Gameplay/TutorialSystem/TutorialTask_Base.cpp @@ -0,0 +1,21 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "TutorialTask_Base.h" + +#include "ProjectFish/Gameplay/Subsystem/TutorialManagerSubsystem.h" + +// void UTutorialTask_Base::BeginDestroy() +// { +// UObject::BeginDestroy(); +// //task 删除的时候,自动完成该引导step +// if (GetWorld()) +// GetWorld()->GetGameInstance()->GetSubsystem()->CompleteTutorialStep(); +// } +void UTutorialTask_Base::Execute_Implementation(class AGameModeBase* GameMode) +{ +} + +void UTutorialTask_Base::BeforTutorialComplete_Implementation() +{ +} diff --git a/ProjectFish/Source/ProjectFish/Gameplay/TutorialSystem/TutorialTask_Base.h b/ProjectFish/Source/ProjectFish/Gameplay/TutorialSystem/TutorialTask_Base.h new file mode 100644 index 0000000..b5a2291 --- /dev/null +++ b/ProjectFish/Source/ProjectFish/Gameplay/TutorialSystem/TutorialTask_Base.h @@ -0,0 +1,28 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/Object.h" +#include "TutorialTask_Base.generated.h" + +/** + * + */ +UCLASS(Blueprintable) +class PROJECTFISH_API UTutorialTask_Base : public UObject +{ + GENERATED_BODY() +public: + /** UObject */ + //virtual void BeginDestroy() override; + + UFUNCTION(BlueprintNativeEvent, Category= "TutorialTask") + void Execute(class AGameModeBase* GameMode); + virtual void Execute_Implementation(class AGameModeBase* GameMode); + + UFUNCTION(BlueprintNativeEvent, Category= "TutorialTask") + void BeforTutorialComplete(); + virtual void BeforTutorialComplete_Implementation(); + +}; diff --git a/ProjectFish/Source/ProjectFish/Gameplay/TutorialSystem/TutorialTask_CheckShipContainer.cpp b/ProjectFish/Source/ProjectFish/Gameplay/TutorialSystem/TutorialTask_CheckShipContainer.cpp new file mode 100644 index 0000000..952d6e5 --- /dev/null +++ b/ProjectFish/Source/ProjectFish/Gameplay/TutorialSystem/TutorialTask_CheckShipContainer.cpp @@ -0,0 +1,34 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "TutorialTask_CheckShipContainer.h" + +#include "GameFramework/GameModeBase.h" +#include "ProjectFish/Gameplay/Subsystem/GameInfoManager.h" +#include "ProjectFish/Gameplay/Subsystem/TutorialManagerSubsystem.h" + + +void UTutorialTask_CheckShipContainer::Execute_Implementation(class AGameModeBase* GameMode) +{ + Super::Execute_Implementation(GameMode); + if (GameMode) + { + GameMode->GetGameInstance()->GetSubsystem()->GetPlayerInfo() + ->OnShipContainerUpdate.AddDynamic(this, &UTutorialTask_CheckShipContainer::OnShipContainerUpdate); + } +} + +void UTutorialTask_CheckShipContainer::BeforTutorialComplete_Implementation() +{ + Super::BeforTutorialComplete_Implementation(); +} + +void UTutorialTask_CheckShipContainer::OnShipContainerUpdate() +{ + //只有3个物品时才完成当前的引导步骤 + int CurrentItemCount = GetOuter()->GetWorld()->GetGameInstance()->GetSubsystem()->GetPlayerInfo()->ShipContainerItems.Num(); + if (CurrentItemCount == ReqItemsCount) + { + Cast(GetOuter())->CompleteTutorialStep(); + } +} diff --git a/ProjectFish/Source/ProjectFish/Gameplay/TutorialSystem/TutorialTask_CheckShipContainer.h b/ProjectFish/Source/ProjectFish/Gameplay/TutorialSystem/TutorialTask_CheckShipContainer.h new file mode 100644 index 0000000..5185d23 --- /dev/null +++ b/ProjectFish/Source/ProjectFish/Gameplay/TutorialSystem/TutorialTask_CheckShipContainer.h @@ -0,0 +1,26 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "TutorialTask_Base.h" +#include "TutorialTask_CheckShipContainer.generated.h" + +/** + * + */ +UCLASS() +class PROJECTFISH_API UTutorialTask_CheckShipContainer : public UTutorialTask_Base +{ + GENERATED_BODY() + + virtual void Execute_Implementation(class AGameModeBase* GameMode) override; + + virtual void BeforTutorialComplete_Implementation() override; + + UFUNCTION() + void OnShipContainerUpdate(); +protected: + UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "TutorialTask_CheckShipContainer") + int32 ReqItemsCount; +}; diff --git a/ProjectFish/Source/ProjectFish/Gameplay/TutorialSystem/TutorialTask_GamePause.cpp b/ProjectFish/Source/ProjectFish/Gameplay/TutorialSystem/TutorialTask_GamePause.cpp new file mode 100644 index 0000000..d698df7 --- /dev/null +++ b/ProjectFish/Source/ProjectFish/Gameplay/TutorialSystem/TutorialTask_GamePause.cpp @@ -0,0 +1,37 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "TutorialTask_GamePause.h" + +#include "Kismet/GameplayStatics.h" + +void UTutorialTask_GamePause::Execute_Implementation(class AGameModeBase* GameMode) +{ + Super::Execute_Implementation(GameMode); + // LevelLoadedDelegateHandle = FCoreUObjectDelegates::PostLoadMapWithWorld.AddUObject( + // this, + // &UTutorialTask_GamePause::OnLevelLoaded + // ); + UGameplayStatics::SetGamePaused(this, true); +} + +void UTutorialTask_GamePause::BeforTutorialComplete_Implementation() +{ + Super::BeforTutorialComplete_Implementation(); + UGameplayStatics::SetGamePaused(this, false); +} + +void UTutorialTask_GamePause::OnLevelLoaded(UWorld* World) +{ + // FCoreUObjectDelegates::PostLoadMapWithWorld.Remove(LevelLoadedDelegateHandle); + // FTimerHandle LambdaTimerHandle; + // GetWorld()->GetTimerManager().SetTimer( + // LambdaTimerHandle, + // [this]() + // { + // UGameplayStatics::SetGamePaused(this, true); + // }, + // 1.5f, + // false + // ); +} diff --git a/ProjectFish/Source/ProjectFish/Gameplay/TutorialSystem/TutorialTask_GamePause.h b/ProjectFish/Source/ProjectFish/Gameplay/TutorialSystem/TutorialTask_GamePause.h new file mode 100644 index 0000000..35a99c1 --- /dev/null +++ b/ProjectFish/Source/ProjectFish/Gameplay/TutorialSystem/TutorialTask_GamePause.h @@ -0,0 +1,25 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "TutorialTask_Base.h" +#include "TutorialTask_GamePause.generated.h" + +/** + * + */ +UCLASS() +class PROJECTFISH_API UTutorialTask_GamePause : public UTutorialTask_Base +{ + GENERATED_BODY() +public: + + virtual void Execute_Implementation(class AGameModeBase* GameMode) override; + + virtual void BeforTutorialComplete_Implementation() override; + + void OnLevelLoaded(UWorld* World); +private: + FDelegateHandle LevelLoadedDelegateHandle; +}; diff --git a/ProjectFish/Source/ProjectFish/ProjectFish.Build.cs b/ProjectFish/Source/ProjectFish/ProjectFish.Build.cs index 36e7055..aa2de8e 100644 --- a/ProjectFish/Source/ProjectFish/ProjectFish.Build.cs +++ b/ProjectFish/Source/ProjectFish/ProjectFish.Build.cs @@ -6,6 +6,7 @@ public class ProjectFish : ModuleRules { public ProjectFish(ReadOnlyTargetRules Target) : base(Target) { + PrivateDependencyModuleNames.AddRange(new string[] { "Dialogue" }); PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; OptimizeCode = CodeOptimization.Never; PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", diff --git a/ProjectFish/Source/ProjectFish/Skill/SkillManager.cpp b/ProjectFish/Source/ProjectFish/Skill/SkillManager.cpp index ea668dd..c4c31e4 100644 --- a/ProjectFish/Source/ProjectFish/Skill/SkillManager.cpp +++ b/ProjectFish/Source/ProjectFish/Skill/SkillManager.cpp @@ -23,7 +23,7 @@ void USkillManager::Tick(float DeltaTime) bool USkillManager::IsTickable() const { - return !bGameEnd && IsValid(GameMode) && GameMode->GetBattleIsBegin(); + return !bGameEnd && IsValid(GameMode) && GameMode->GetBattleIsBegin() && !GameMode->IsPaused(); } TStatId USkillManager::GetStatId() const diff --git a/ProjectFish/Source/ProjectFish/Utils/FishFunctionLibrary.cpp b/ProjectFish/Source/ProjectFish/Utils/FishFunctionLibrary.cpp index ae988b4..3e5378f 100644 --- a/ProjectFish/Source/ProjectFish/Utils/FishFunctionLibrary.cpp +++ b/ProjectFish/Source/ProjectFish/Utils/FishFunctionLibrary.cpp @@ -3,6 +3,7 @@ #include "FishFunctionLibrary.h" +#include "EngineUtils.h" #include "Kismet/GameplayStatics.h" #include "ProjectFish/Data/ContainerInfo.h" @@ -25,3 +26,20 @@ int32 UFishFunctionLibrary::GetContainerItemHeight(const UObject* WorldContextOb { return ContainerItem.GetItemHeight(); } + +AActor* UFishFunctionLibrary::GetActorOfClass(const UObject* WorldContextObject, TSubclassOf ActorClass) +{ + if (ActorClass) + { + if (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull)) + { + for (TActorIterator It(World, ActorClass); It; ++It) + { + AActor* Actor = *It; + return Actor; + } + } + } + + return nullptr; +} diff --git a/ProjectFish/Source/ProjectFish/Utils/FishFunctionLibrary.h b/ProjectFish/Source/ProjectFish/Utils/FishFunctionLibrary.h index 27eb7e3..4d9c307 100644 --- a/ProjectFish/Source/ProjectFish/Utils/FishFunctionLibrary.h +++ b/ProjectFish/Source/ProjectFish/Utils/FishFunctionLibrary.h @@ -21,6 +21,9 @@ public: UFUNCTION(BlueprintPure, Category = "FishFunctionLibrary", meta=(WorldContext="WorldContextObject")) static int32 GetContainerItemWiidth(const UObject* WorldContextObject, FContainerItem ContainerItem); - UFUNCTION(BlueprintPure, Category = "FishFunctionLibrary", meta=(WorldContext="WorldContextObject")) - static int32 GetContainerItemHeight(const UObject* WorldContextObject, FContainerItem ContainerItem); + UFUNCTION(BlueprintPure, Category = "FishFunctionLibrary", meta=(WorldContext="WorldContextObject")) + static int32 GetContainerItemHeight(const UObject* WorldContextObject, FContainerItem ContainerItem); + + UFUNCTION(BlueprintCallable, Category="Actor") + static AActor* GetActorOfClass(const UObject* WorldContextObject, TSubclassOf ActorClass); }; diff --git a/ProjectFish/Source/ProjectFish/Widget/DialogueWidget_Base.cpp b/ProjectFish/Source/ProjectFish/Widget/DialogueWidget_Base.cpp new file mode 100644 index 0000000..0c52a3d --- /dev/null +++ b/ProjectFish/Source/ProjectFish/Widget/DialogueWidget_Base.cpp @@ -0,0 +1,4 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "DialogueWidget_Base.h" diff --git a/ProjectFish/Source/ProjectFish/Widget/DialogueWidget_Base.h b/ProjectFish/Source/ProjectFish/Widget/DialogueWidget_Base.h new file mode 100644 index 0000000..9503646 --- /dev/null +++ b/ProjectFish/Source/ProjectFish/Widget/DialogueWidget_Base.h @@ -0,0 +1,20 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "DialogueAsset.h" +#include "Blueprint/UserWidget.h" +#include "DialogueWidget_Base.generated.h" + +/** + * + */ +UCLASS(Blueprintable) +class PROJECTFISH_API UDialogueWidget_Base : public UUserWidget +{ + GENERATED_BODY() +public: + UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Dialogue Widget") + UDialogueAsset* DialogueAsset; +}; diff --git a/ProjectFish/Source/ProjectFish/Widget/TutorialControlWidget_Base.cpp b/ProjectFish/Source/ProjectFish/Widget/TutorialControlWidget_Base.cpp new file mode 100644 index 0000000..d362aa1 --- /dev/null +++ b/ProjectFish/Source/ProjectFish/Widget/TutorialControlWidget_Base.cpp @@ -0,0 +1,5 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "TutorialControlWidget_Base.h" + diff --git a/ProjectFish/Source/ProjectFish/Widget/TutorialControlWidget_Base.h b/ProjectFish/Source/ProjectFish/Widget/TutorialControlWidget_Base.h new file mode 100644 index 0000000..49a2d3c --- /dev/null +++ b/ProjectFish/Source/ProjectFish/Widget/TutorialControlWidget_Base.h @@ -0,0 +1,22 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "TutorialControlWidget_Base.generated.h" + +/** + * + */ +UCLASS(Blueprintable) +class PROJECTFISH_API UTutorialControlWidget_Base : public UUserWidget +{ + GENERATED_BODY() +public: + + UFUNCTION(BlueprintImplementableEvent) + void TutorialHideUI(const TArray& UINames); + // UFUNCTION(BlueprintImplementableEvent) + // void TutorialShowUI(const TArray& UINames); +};