1060 lines
28 KiB
Markdown
1060 lines
28 KiB
Markdown
# 新手引导系统设计文档
|
||
|
||
## 一、系统整体架构
|
||
|
||
### 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<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)
|
||
|
||
#### 数据资产设计
|
||
```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<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)
|
||
|
||
```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<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)
|
||
|
||
```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<FName, UWidget*> 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<FName, UWidget*> 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<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 地图系统引导模式
|
||
|
||
```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<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: 家园初始对话
|
||
```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<UTutorialSubsystem>();
|
||
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改造。
|