Procedural Dialogue Engine - UE4程序化对话系统的技术实现
纯开源,仅供学习参考,逐步迭代。
一、引言
在游戏开发中,NPC对话系统是一个核心组成部分。传统的对话树(Dialogue Tree)方案需要开发者为每个NPC手动编写大量的对话内容,不仅工作量大,而且难以实现个性化的NPC性格表现。
Procedural Dialogue Engine 是一个基于 6维人格向量(6D Personality Vector) 的UE4程序化对话系统,它能够:
- 根据NPC的人格特质动态生成差异化回应
- 追踪玩家对话选择对全局叙事的影响
- 支持上下文感知的对话分支
- 完全 Blueprint 暴露,无需C++即可使用
二、核心概念
2.1 6维人格向量
每个NPC都有一个6维人格向量,范围为 [-1.0, +1.0]:
| 维度 | 负极 (-1.0) | 正极 (+1.0) |
|---|---|---|
| Extraversion | 内敛安静 | 外向健谈 |
| Emotionality | 冷静沉着 | 情绪丰富 |
| Rationality | 直觉创意 | 理性逻辑 |
| Humor | 严肃呆板 | 幽默诙谐 |
| Formality | 随意友好 | 正式尊重 |
| Optimism | 悲观谨慎 | 乐观积极 |
系统会根据NPC在每个维度上的位置,从预定义的**响应变体(Response Variants)**中选择最合适的文本:
cpp
// FShapedResponse.h - 响应变体结构
struct FShapedResponse {
FText BaseText; // 默认文本
FText VariantIntrovert; // 内敛版
FText VariantExtravert; // 外向版
FText VariantStoic; // 冷静版
FText VariantEmotional; // 情绪版
FText VariantCasual; // 随意版
FText VariantFormal; // 正式版
FText VariantSerious; // 严肃版
FText VariantHumorous; // 幽默版
};
人格向量的文本塑形逻辑:
cpp
// PDETypes.cpp - ShapeWithPersonality
FText FShapedResponse::ShapeWithPersonality(const FPersonalityVector& Personality) const {
FText Result = BaseText;
// 根据Extraversion选择变体
if (Personality.Extraversion < -0.3f && !VariantIntrovert.IsEmpty())
Result = VariantIntrovert;
else if (Personality.Extraversion > 0.3f && !VariantExtravert.IsEmpty())
Result = VariantExtravert;
// 根据Emotionality选择变体
if (Personality.Emotionality < -0.3f && !VariantStoic.IsEmpty())
Result = VariantStoic;
else if (Personality.Emotionality > 0.3f && !VariantEmotional.IsEmpty())
Result = VariantEmotional;
// ... 其他维度的处理类似
return Result;
}
2.2 上下文标签系统
对话响应可以声明RequiredContextTags (必须满足的上下文)和ExcludedContextTags(排除的上下文):
cpp
struct FShapedResponse {
TArray<FName> RequiredContextTags; // 必须全部满足
TArray<FName> ExcludedContextTags; // 任一满足则排除
};
上下文标签可以来自:
- 全局事件 :
AddGlobalContextTag(影响所有NPC) - 玩家对话选择 :
ContextTagsToAdd - 世界触发器:战斗开始、任务接受、物品获得等
2.3 叙事影响追踪
FNarrativeInfluence 追踪玩家对话选择对全局叙事的影响:
cpp
struct FNarrativeInfluence {
TMap<FName, float> InfluenceFlags; // 叙事标记
TMap<FName, float> ReputationMap; // 角色声望 [-100, +100]
TArray<FName> TopicsIntroduced; // 讨论过的话题
TArray<FName> AlliedCharacters; // 联盟角色
TArray<FName> OpposedCharacters; // 对立角色
};
这使得跨NPC的叙事联动成为可能:玩家对NPC A的欺骗会影响NPC B对该玩家的态度。
2.4 实时情绪追踪
每个对话回合都有一个 EmotionalImpact 值,会话氛围 ConversationMood 是累积和:
cpp
struct FDialogueSessionState {
float ConversationMood = 0.0f; // [-1.0, +1.0]
// ...
};
void FDialogueSessionState::RecordTurn(const FDialogueTurn& Turn) {
TurnHistory.Add(Turn);
TurnCount++;
ConversationMood = FMath::Clamp(ConversationMood + Turn.EmotionalImpact, -1.0f, 1.0f);
}
这允许:
- NPC检测玩家的挫败感或友好度
- 氛围跨越阈值时触发特殊分支
- 氛围驱动的NPC反应(如"你似乎很沮丧,让我帮你")
三、系统架构
3.1 核心类设计
ProceduralDialogue/
├── Source/ProceduralDialogue/
│ ├── Public/DialogueSystem/
│ │ ├── PDETypes.h # 核心数据类型(枚举、结构体)
│ │ ├── PDENPCCharacter.h # NPC角色类
│ │ ├── PDEGMBlueprintLib.h # 全局Blueprint工具库
│ │ └── PDEDialogueSubsystem.h # 世界子系统
│ └── Private/DialogueSystem/
│ ├── PDETypes.cpp
│ ├── PDENPCCharacter.cpp # 核心处理逻辑
│ ├── PDEGMBlueprintLib.cpp
│ └── PDEDialogueSubsystem.cpp
3.2 NPC角色类(APDENPCCharacter)
继承自 ACharacter,是程序化对话的核心执行者:
cpp
UCLASS(Blueprintable, meta=(DisplayName="Procedural Dialogue NPC"))
class APDENPCCharacter : public ACharacter {
// 6D人格向量 - 驱动响应塑形
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FPersonalityVector Personality;
// 可选的对话数据表(CSV/JSON)
UPROPERTY(EditAnywhere, BlueprintReadWrite)
UDataTable* DialogueDataTable;
// Blueprint事件
UPROPERTY(BlueprintAssignable)
FOnDialogueStarted OnDialogueStarted;
UPROPERTY(BlueprintAssignable)
FOnNPCTurnReady OnNPCTurnReady;
UPROPERTY(BlueprintAssignable)
FOnPlayerOptionsGenerated OnPlayerOptionsGenerated;
};
3.3 对话组件(UProceduralDialogueComponent)
可挂载到任意 Actor 的组件形式:
cpp
UCLASS(Blueprintable, meta=(DisplayName="Procedural Dialogue"))
class UProceduralDialogueComponent : public UActorComponent {
// 6D人格向量
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FPersonalityVector Personality;
// 对话数据表
UPROPERTY(EditAnywhere, BlueprintReadWrite)
UDataTable* DialogueDataTable;
};
3.4 全局上下文子系统
UDialogueGlobalContextSubsystem 管理全局上下文标签:
cpp
UCLASS()
class UDialogueGlobalContextSubsystem : public UWorldSubsystem {
UFUNCTION(BlueprintCallable)
void AddTag(FName Tag); // 添加全局标签
UFUNCTION(BlueprintCallable)
void RemoveTag(FName Tag);
UFUNCTION(BlueprintPure)
TArray<FName> GetAllTags() const;
};
四、预设人格
系统内置7种预设人格,可直接使用或混合:
| 预设 | Extraversion | Emotionality | Rationality | Humor | Formality | Optimism |
|---|---|---|---|---|---|---|
| Scholar | +0.3 | +0.2 | +0.8 | +0.1 | +0.4 | +0.2 |
| Engineer | -0.2 | -0.4 | +0.9 | -0.1 | +0.5 | +0.0 |
| Empath | +0.6 | +0.8 | -0.3 | +0.0 | -0.2 | +0.6 |
| Joker | +0.5 | +0.4 | -0.4 | +0.9 | -0.6 | +0.7 |
| Commander | +0.4 | -0.3 | +0.3 | -0.4 | +0.9 | +0.1 |
| Loner | -0.8 | +0.1 | +0.5 | -0.3 | +0.2 | -0.4 |
| Neutral | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
Blueprint库提供了人格混合功能:
cpp
// 线性插值
FPersonalityVector NewPersonality = BlendPersonalityVectors(
Scholar, Joker, 0.5f, EBPBlendMode::Linear);
// 余弦插值(更平滑的过渡)
FPersonalityVector SmoothBlend = BlendPersonalityVectors(
Loner, Empath, 0.3f, EBPBlendMode::Cosine);
五、使用方法
5.1 安装
- 克隆到 UE4 项目的
Plugins/目录 - 重新生成项目文件并编译
- 在场景中放置
ProceduralDialogueCharacter - 在 Details 面板配置人格向量
- 从交互系统调用
StartDialogue()
5.2 Blueprint 调用
cpp
// 开始对话
NPC->StartDialogue(PlayerNarrativeInfluence);
// 玩家选择选项(索引从0开始)
NPC->SelectPlayerOption(0);
// 切换话题
NPC->SwitchToTopic(FName("QuestDetails"));
// 结束对话
NPC->EndDialogue(true);
5.3 数据驱动配置
可以通过 DataTable 配置对话内容:
csv
TopicId,BaseText,VariantIntrovert,VariantExtravert,Priority
Greeting,"Hello.","...","Hey there!",1.0
QuestDetails,"I need your help.","Could you...","Listen up!",0.8
六、技术特点
- 零外部依赖 --- 仅使用UE4核心模块(Core、CoreUObject、Engine、Json)
- 完整Blueprint暴露 --- 无需C++即可在蓝图中调用所有核心功能
- 数据驱动 --- 对话内容通过 UDataTable 管理(CSV/JSON兼容)
- 线程安全 --- 全局上下文通过世界子系统管理
- 确定性选择 --- 相同上下文下响应选择可复现
- 存档兼容 --- FNarrativeInfluence 是简单 UStruct,可直接复制到 SaveGame
七、总结
Procedural Dialogue Engine 为UE4游戏提供了一个灵活、智能的程序化对话解决方案。通过6维人格向量、上下文标签和叙事影响追踪三大核心机制,开发者可以创建出具有真实人格 和叙事深度的NPC对话系统。
相比传统的手写对话树方案,它能显著减少内容创作的工作量,同时为每个NPC赋予独特的"灵魂"。
GitHub :https://github.com/zhangxuhan
博客:https://blog.csdn.net/zxh1592000
MIT License --- 可免费用于商业和非商业项目