Project02/ProjectFish/Docs/TutorialSystemDesign.md

28 KiB
Raw Permalink Blame History

新手引导系统设计文档

一、系统整体架构

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)

头文件结构

// 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<class UTutorialStepAsset*> 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)

数据资产设计

// 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<FName> VisibleElementTags;

    // 高亮的UI元素标签
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    TArray<FName> HighlightedElementTags;

    // 可交互的UI元素标签
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    TArray<FName> 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)

// 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<class UUserWidget> MaskOverlayClass;

    // 提示Widget类
    UPROPERTY(EditDefaultsOnly, Category = "Tutorial UI")
    TSubclassOf<class UUserWidget> HintWidgetClass;

    UPROPERTY()
    class UUserWidget* MaskOverlayWidget;

    UPROPERTY()
    class UUserWidget* HintWidget;
};

2.4 UI引导接口 (ITutorialModeInterface)

// 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

// 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<FName, UWidget*> ElementTagMap;

    // 引导模式状态
    bool bInTutorialMode = false;
    FTutorialUIFilter CurrentFilter;

private:
    void InitializeElementTags();
    void UpdateUIVisibility();
    void UpdateUIHighlight();
};
// 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引导基类

为了简化改造,可以创建一个基类:

// 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<FName, UWidget*> ElementMap;

private:
    void UpdateAllElements();
    void ApplyHighlight(UWidget* Widget, bool bHighlight);
};

四、关卡特殊配置系统

4.1 战斗关卡配置扩展

为新手引导创建特殊的战斗配置:

// 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<FName> 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<FTutorialBattleDialogueTrigger> DialogueTriggers;

    // 奖励配置
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Battle")
    FTutorialBattleReward RewardConfig;

    // 禁用的UI元素
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Battle")
    TArray<FName> DisabledUIElements;

    // 是否禁用返航按钮
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Battle")
    bool bDisableReturnButton = false;
};

4.2 地图系统引导模式

// 在 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<int32>& VisibleNodeIDs);

    // 引导模式:高亮指定节点
    UFUNCTION(BlueprintCallable, Category = "Tutorial")
    void SetTutorialHighlightNode(int32 NodeID);

protected:
    UPROPERTY()
    bool bInTutorialMode = false;

    UPROPERTY()
    TArray<int32> TutorialVisibleNodes;

    UPROPERTY()
    int32 TutorialHighlightNodeID = -1;
};

五、实现流程步骤拆解

根据你的需求新手引导分为15个步骤。以下是每个步骤的配置示例

Step 1: 家园初始对话

// 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: 引导出航按钮

// 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: 地图界面单点

// 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: 移动按键引导

// 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: 战斗引导(特殊配置)

// 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 集成

// 在 QuestManager.h 中添加:
UFUNCTION(BlueprintCallable, Category = "Tutorial")
void AcceptTutorialQuest(UQuestAsset* QuestAsset);

DialogueAsset 集成

// 对话系统已经有事件委托,直接监听即可
// 在 TutorialSubsystem 中:
void UTutorialSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
    // 监听对话结束事件
    // UDialogueAsset::OnDialogueFinished.AddDynamic(this, &UTutorialSubsystem::OnDialogueFinished);
}

FishingMapSubSystem 集成

// 在地图生成前检查是否引导模式
void UFishingMapSubSystem::GenerateMap()
{
    UTutorialSubsystem* TutorialSys = GetGameInstance()->GetSubsystem<UTutorialSubsystem>();
    if (TutorialSys && TutorialSys->IsInTutorialMode())
    {
        // 使用引导专用地图配置
        GenerateTutorialMap(...);
        return;
    }
    // 正常地图生成
    ...
}

七、UI改造优先级

高优先级(必须改造)

  1. HomeUI - 家园界面(出航、鱼市、道具店、背包按钮)
  2. MapUI - 地图界面(节点显示和高亮)
  3. BattleUI - 战斗界面(技能、背包、返航按钮控制)
  4. RewardUI - 奖励界面(强制拾取逻辑)

中优先级

  1. MarketUI - 鱼市界面(任务提交)
  2. FishingSceneUI - 钓鱼场景UI移动提示

低优先级

  1. 其他辅助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. 高亮效果

    • 方案AMaterial + 发光参数
    • 方案BUMG Animation缩放脉冲
    • 方案CBorder + 高亮颜色
  3. 事件监听

    • 使用委托系统
    • 在 TutorialEventListener 中统一管理
    • 避免硬编码,使用数据驱动
  4. 存档集成

    • 在 PlayerInfoSaveGame 中添加:
      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改造。