vault backup: 2026-05-31 12:41:10

This commit is contained in:
2026-05-31 12:41:10 +08:00
parent 824806cd61
commit a1691d8bb7
8 changed files with 1875 additions and 1 deletions

View File

@@ -0,0 +1,565 @@
# AIDM 项目技术分析报告
> 测试用提示词(标准化,两组完全一致)
> 项目AIDM — UE5.7 修仙 ARPG
> 日期2026-05-30
---
# 任务 1NPC 系统完整架构
## 1.1 分级机制A/B/C 三级)
NPC 分级体系定义在 `LWSNpcTypes.h:111-219` 和设计文档中:
| 级别 | 数量 | 跨存档保留 | 模拟深度 | 更新频率 |
|------|------|-----------|---------|---------|
| **A级** | 20~40 | 姓名/外貌/性格/资质永不改变(`IdentitySeedId` 种子驱动) | 完整(情绪/关系/目标/记忆/决策) | 每个时间单位 |
| **B级** | 100~300 | 角色定位保留,人物更换 | 中度(目标/关系/基础决策) | 每2~5个时间单位 |
| **C级** | 数百~上千 | 无 | 轻度(位置/状态/基础反应) | 按需/低频 |
关键字段:`FLWSNpcData``Tier``ELWSNpcTier`)、`IdentitySeedId`A级跨存档种子ID。C→B 涌现升级通过 `InteractionCount``SignificanceScore` 追踪。
## 1.2 决策模型Utility AI
核心文件:`LWSNpcDecisionSystem.h/.cpp`
**评分公式**`LWSNpcDecisionSystem.h:19-31`
```
BaseScore(a) = Σᵢ NeedRelevance[a][i] × Npc.Needs[i] (i ∈ 6维需求)
PersonalityFactor = f(Temperament, Caution, Morality, a)
GoalFactor = Π(Goal Type → bonus, a)
ScoreWithNoise(a) = BaseScore × PersonalityFactor × GoalFactor + Uniform[0, 0.10]
ChosenAction = argmaxₐ ScoreWithNoise(a)
```
**6 维需求**`Survival / Safety / Social / Power / Resource / Cultivation`,每日衰减(缺乏度↑),存储在 `FLWSNpcData::Needs``TArray<float>` 长度固定为6
**7 种高层 Action**`Wander / GoHome / Cultivate / SeekResource / Socialize / Flee / Hunt`
**3 种 Behavior**(执行层翻译):
- `Idle` → 原地不动
- `Wander` → 6邻居随机跳转20%概率原地不动)
- `MoveTo` → 贪心1-hop向目标移动
**3D 性格**`LWSNpcPersonalityTypes.h``Temperament`(冷→热)、`Caution`(慎→莽)、`Morality`(义→私),影响各 Action 的权重系数。
**行为持续性**(设计文档 §八,待实施):承诺期 `MinDuration`、完成判定 `IsFinished`、Need 分摊扣减、Stickiness 黏性、Hysteresis 迟滞、中断保护。
## 1.3 与时间系统的关系
时间系统定义在 `RPGCommon/Public/Time/RPGGameTimeTypes.h``RPGGameTimeSubsystem.h`
- **日历常量**12月/年30天/月360天/年(`FRPGGameRawTime`
- **驱动方式**`URPGGameTimeSubsystem::OnGameTimeChanged` 广播,每次 `DeltaDays > 0` 触发
- **NPC 模拟入口**`ULWSNpcSimulationSubsystem::HandleGameTimeChanged` 订阅时间广播 → 按 `DeltaDays` 次调用 `SimulateOneDay()`
- **数据流**
```
GameTimeSubsystem.OnGameTimeChanged (DeltaDays>0)
→ NpcSim.SimulateDays(N)
→ 对每个存活 NPC:
→ DecisionSystem.DecayNeeds (需求衰减)
→ DecisionSystem.DecideAction (评分→选Action→翻译Behavior)
→ TickNpcOneDay (Idle/Wander/MoveTo 物理推进)
→ SpatialIndex.Move (更新空间索引)
→ PushTrail (环形buffer记录轨迹)
→ 同格NPC关系建立 / 关系衰减 / 信息传播
```
## 1.4 存档方式
存档系统定义在 `RPGSaveSystem/Public/RPGSaveSubsystem.h`
- **两级存档**`URPGSaveSystemData`(系统级,槽位列表)+ `URPGSaveDataObject`(玩家级,`TMap<FName, FSaveDataContainer>`
- **接口机制**:各模块通过 `ISaveableInterface` 注册到 `URPGSaveSubsystem`,以 `FName SaveID` 标识
- **NPC 数据持久化**`ULWSNpcSimulationSubsystem` 提供 `GetActiveNpcsCopy()`(返回 `TArray<FLWSNpcData>` 副本)和 `RestoreNpcsFromSave()`从存档还原NPC 数据作为 `FLWSNpcData[]` 序列化存入 `FSaveDataContainer`
- **玩家数据持久化**:通过 `URPGAttributeSet` + `FRPGCharacterAttributeData` 保存属性,`FRPGSkilllActionPoseTree` 保存技能映射,`FSkilllTreeDataPool` 保存技能树
---
# 任务 2商队 NPC 跨系统依赖分析
新增「商队 NPC」类型需要修改以下模块
## 必须修改的模块
### 1. LivingWorldSystem 模块
| 文件 | 修改内容 |
|------|---------|
| `LWSNpcTypes.h` | `ELWSNpcBehavior` 新增 `Trade`/`Caravan` 枚举值;`FLWSNpcData` 新增字段:`TradeRoute`(起点→终点)、`TradeGoods`(货物数组)、`CaravanOwnerFactionId` |
| `LWSNpcPersonalityTypes.h` | `ELWSNpcAction` 新增 `TradeCaravan``ELWSNpcNeed` 可能需要新增 `Wealth` 维度;`ELWSNpcGoal` 新增 `TradeGoal` |
| `LWSNpcDecisionSystem.h/.cpp` | `NeedRelevance` 矩阵新增 `TradeCaravan` 行Personality/Goal 权重新增商队相关因子;`ApplyActionToBehavior` 新增 `TradeCaravan → MoveTo` 翻译逻辑 |
| `LWSNpcSimulationSubsystem.h/.cpp` | `TickNpcOneDay` 新增 Trade 行为处理(沿路线移动、到达后触发交易结算) |
| `LWSNpcGenerator.h/.cpp` | 新增商队生成逻辑(沿贸易路线撒点、分配货物) |
| `LWSNpcRelationSubsystem.h/.cpp` | 商队与聚落/势力的交易关系类型 |
| `LWSNpcSpatialIndex.h` | 如无结构变化则无需修改 |
### 2. 势力/聚落模块
| 文件 | 修改内容 |
|------|---------|
| `LWSFactionTypes.h` | 势力新增贸易路线数据、商队所有权 |
| `LWSSettlementGenerator.h` | 聚落间贸易路线生成(可为商队提供路径) |
| `LWSFactionGenerator.h` | 势力初始化时分配商队 |
### 3. 世界/资源模块
| 文件 | 修改内容 |
|------|---------|
| `LWSResourceGenerator.h` | 商队可携带/交易资源类型定义 |
| `LWSChronicleSubsystem.h` | 商队到达/被劫事件写入编年史 |
## 存档结构变化
**需要变化**`FLWSNpcData` 新增字段TradeRoute、TradeGoods 等)会影响序列化。由于 `FLWSNpcData` 是 `USTRUCT` 且直接存储在 `ActiveNpcs``TArray<FLWSNpcData>`),新增字段后旧存档将不兼容。需要:
1. 在 `URPGSaveDataObject::VersionMap` 中递增 NPC 数据版本号
2. 提供存档迁移逻辑(`RestoreNpcsFromSave` 中检测旧版本并填充默认值)
**`FRPGCharacterAttributeData` 和 `URPGAttributeSet`**:如果商队不涉及玩家属性变化,这两处不需要修改。
## 对 GAS 属性系统的影响
**直接影响:无。** GAS 属性系统(`URPGAttributeSet`)当前服务于玩家/Actor 角色的战斗属性,而 LivingWorld NPC 是纯数据 `USTRUCT`,不挂载 `AbilitySystemComponent`。
**间接影响(可选扩展)**:如果未来商队 NPC 需要与玩家发生战斗交互(如劫商队),则需要为商队守卫创建 `RPGAICharacter`Actor此时会走 GAS 属性系统。但这属于"商队战斗"子功能,不属于商队基础框架。
## 涉及文件汇总(约 12 个文件)
```
Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSNpcTypes.h
Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSNpcPersonalityTypes.h
Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSNpcDecisionSystem.h
Plugins/RPGGameCore/Source/LivingWorldSystem/Private/LWSNpcDecisionSystem.cpp
Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSNpcSimulationSubsystem.h
Plugins/RPGGameCore/Source/LivingWorldSystem/Private/LWSNpcSimulationSubsystem.cpp
Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSNpcGenerator.h
Plugins/RPGGameCore/Source/LivingWorldSystem/Private/LWSNpcGenerator.cpp
Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSNpcRelationSubsystem.h
Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSFactionTypes.h
Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSSettlementGenerator.h
Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSChronicleSubsystem.h
```
---
# 任务 3「御剑飞行」ActionPose 代码生成
## 3.1 枚举扩展
在 `RPGTypes.h:202-260` 的 `ELM_ActionPose` 中,`AirFlying` 已存在,但「御剑飞行」作为一种特殊的战斗移动姿态,建议新增专用枚举值:
```cpp
// RPGTypes.h — 在 AirFlying 之后、AirDodge 之前新增
UENUM(BlueprintType)
enum class ELM_ActionPose : uint8
{
// ... 现有值保持不变 ...
//空行
AirFlying,
//御剑飞行(战斗移动态,可在飞行中施法/攻击)
AirSwordFlying, // <-- 新增
//空避
AirDodge,
// ...
};
```
## 3.2 头文件声明
```cpp
// SwordFlyingActionPose.h
#pragma once
#include "CoreMinimal.h"
#include "LogicStateMachine/SDHGAState.h"
#include "GameplayTagContainer.h"
#include "SwordFlyingActionPose.generated.h"
/**
* 御剑飞行 ActionPose 状态
*
* 设计要点:
* - 继承自 USDHGAState挂载在 USkillSMInstance 状态机中
* - 进入此状态时:角色踩上飞剑,移动速度切换为飞行速度,消耗法力
* - 在此状态中可施放子架势技能SubPose0~5如御剑冲刺、御剑斩击
* - 退出此状态时:飞剑消散,恢复地面移动速度
* - 法力耗尽或受到重击时强制退出
*/
UCLASS(Blueprintable, BlueprintType)
class RPGGAMEPLAYABILITY_API USwordFlyingActionPoseState : public USDHGAState
{
GENERATED_BODY()
public:
USwordFlyingActionPoseState();
// Begin USDHGAState
virtual void OnEnterState(const FGameplayTagContainer& ContextTags) override;
virtual void OnExitState(const FGameplayTagContainer& ContextTags) override;
virtual void TickState(float DeltaTime) override;
// End USDHGAState
// 御剑飞行专属 GameplayEffect持续消耗法力、修改移动速度
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "SwordFlying")
TSubclassOf<class UGameplayEffect> SwordFlyingGE;
// 法力每秒消耗量
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "SwordFlying", meta = (ClampMin = "0"))
float ManaCostPerSecond = 5.0f;
// 飞行移动速度倍率(基于基础 MoveSpeed
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "SwordFlying", meta = (ClampMin = "1.0"))
float FlightSpeedMultiplier = 1.5f;
// 受击时是否强制退出御剑
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "SwordFlying")
bool bForceExitOnHit = true;
// 最小法力阈值(低于此值强制退出)
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "SwordFlying", meta = (ClampMin = "0"))
float MinManaThreshold = 10.0f;
protected:
// 进入时应用的 GE Handle用于退出时移除
FActiveGameplayEffectHandle ActiveFlyingGEHandle;
// 检查是否满足持续飞行条件
bool CanSustainFlying() const;
// 获取 OwningCharacter 的属性集
class URPGAttributeSet* GetOwnerAttributeSet() const;
};
```
## 3.3 关键逻辑实现
```cpp
// SwordFlyingActionPose.cpp
#include "SwordFlyingActionPose.h"
#include "Character/RPGCharacterBase.h"
#include "Character/Attribute/RPGAttributeSet.h"
#include "Abilities/RPGAbilitySystemComponent.h"
#include "LogicStateMachine/SDHSkillStateMachine.h"
USwordFlyingActionPoseState::USwordFlyingActionPoseState()
{
ManaCostPerSecond = 5.0f;
FlightSpeedMultiplier = 1.5f;
bForceExitOnHit = true;
MinManaThreshold = 10.0f;
}
void USwordFlyingActionPoseState::OnEnterState(const FGameplayTagContainer& ContextTags)
{
Super::OnEnterState(ContextTags);
ARPGCharacterBase* Owner = Cast<ARPGCharacterBase>(GetOuterActor());
if (!Owner) return;
URPGAbilitySystemComponent* ASC = Owner->GetRPGAbilitySystemComponent();
URPGAttributeSet* AttrSet = GetOwnerAttributeSet();
if (!ASC || !AttrSet) return;
// 1. 应用御剑飞行 GE持续消耗法力 + 移动速度修改)
if (SwordFlyingGE)
{
FGameplayEffectContextHandle Context = ASC->MakeEffectContext();
FGameplayEffectSpecHandle Spec = ASC->MakeOutgoingSpec(SwordFlyingGE, 1.0f, Context);
if (Spec.IsValid())
{
ActiveFlyingGEHandle = ASC->ApplyGameplayEffectSpecToSelf(*Spec.Data.Get());
}
}
// 2. 标记角色进入御剑飞行状态
AttrSet->RPGFlags.AddTag(FGameplayTag::RequestGameplayTag(TEXT("State.ActionPose.SwordFlying")));
// 3. 播放踩剑动画 / 飞剑视觉效果(此处接入动画蓝图和 Niagara
// Owner->PlayMontageSection(...);
}
void USwordFlyingActionPoseState::OnExitState(const FGameplayTagContainer& ContextTags)
{
ARPGCharacterBase* Owner = Cast<ARPGCharacterBase>(GetOuterActor());
if (!Owner) return;
URPGAbilitySystemComponent* ASC = Owner->GetRPGAbilitySystemComponent();
URPGAttributeSet* AttrSet = GetOwnerAttributeSet();
if (!ASC || !AttrSet) return;
// 1. 移除御剑飞行 GE
if (ActiveFlyingGEHandle.IsValid())
{
ASC->RemoveActiveGameplayEffect(ActiveFlyingGEHandle);
ActiveFlyingGEHandle = FActiveGameplayEffectHandle();
}
// 2. 移除状态 Tag
AttrSet->RPGFlags.RemoveTag(FGameplayTag::RequestGameplayTag(TEXT("State.ActionPose.SwordFlying")));
// 3. 恢复地面姿态 → GroundDefault
USkillSMInstance* SkillSM = Owner->GetSkillSMInstance();
if (SkillSM)
{
SkillSM->SetDynamicSkillByActionPose(
SkillSM->GetDynamicSkillClassByActionPose(ELM_ActionPose::GroundDefault, ELM_SubActionPose::NotSubPose),
ELM_ActionPose::GroundDefault,
ELM_SubActionPose::NotSubPose
);
}
Super::OnExitState(ContextTags);
}
void USwordFlyingActionPoseState::TickState(float DeltaTime)
{
Super::TickState(DeltaTime);
// 检查持续飞行条件
if (!CanSustainFlying())
{
// 法力不足或生命值危险 → 强制退出
USkillSMInstance* SkillSM = Cast<USkillSMInstance>(GetStateMachineInstance());
if (SkillSM)
{
SkillSM->ConsumeCommandTransmitter(
FGameplayTag::RequestGameplayTag(TEXT("SkillTransmitter.QuitState.ForceGround"))
);
}
}
}
bool USwordFlyingActionPoseState::CanSustainFlying() const
{
URPGAttributeSet* AttrSet = GetOwnerAttributeSet();
if (!AttrSet) return false;
// 法力不足
if (AttrSet->GetMana() < MinManaThreshold) return false;
// 生命值过低
if (AttrSet->GetHealth() <= 0.0f) return false;
return true;
}
URPGAttributeSet* USwordFlyingActionPoseState::GetOwnerAttributeSet() const
{
ARPGCharacterBase* Owner = Cast<ARPGCharacterBase>(GetOuterActor());
if (!Owner) return nullptr;
URPGAbilitySystemComponent* ASC = Owner->GetRPGAbilitySystemComponent();
if (!ASC) return nullptr;
return const_cast<URPGAttributeSet*>(
ASC->GetSet<URPGAttributeSet>()
);
}
```
## 3.4 注册到 ActionPoseTree
在 `SystemData.h:113-134` 的 `FRPGSkilllActionPoseTree` 中注册:
```cpp
// 玩家学习御剑飞行技能时:
ActionPoseTree.SetActionPoseSkillClass(
FLMActionPose(ELM_ActionPose::AirSwordFlying, ELM_SubActionPose::NotSubPose),
SwordFlyingSkillSMClass
);
```
---
# 任务 4「符修」技能效果系统设计 → GAS Effect 映射
## 4.1 符修核心机制回顾
来自 `修仙流派.md`
- **符修定位**:将法术预先封存于符纸,战斗中瞬发不消耗灵力,但一次性使用
- **加成流派**:魔修、鬼修、音修制符时产出 +20%
- **道艺等级**Lv.0~Lv.5未入门→登峰造极Lv.3+ 生效流派加成
- **产出**:攻击符、防御符、遁术符、控制符、召唤符
## 4.2 符箓类型 → GAS Effect 映射
### 类型 A攻击符Instant GameplayEffect
```
攻击符 = UGameplayEffect (DurationPolicy = Instant)
├── Modifiers:
│ ├── Attribute = RPGAttributeSet.Damage (Magnitude = SetByCaller, Tag = "Data.Talisman.Damage")
│ └── Execution = URPGDamageExecution (复用现有伤害结算)
├── SetByCaller Magnitudes:
│ ├── Data.Damage.Base = 符纸基础伤害
│ ├── Data.Damage.Bonus = 符修加成百分比
│ └── Data.Damage.Tough = 韧性伤害
└── GameplayTags:
├── GrantedTags: Effect.Damage.Talisman
└── AssetTags: SkillType.Talisman.Attack
```
### 类型 B防御符Duration GameplayEffect — 有限时间 Buff
```
防御符 = UGameplayEffect (DurationPolicy = HasDuration, Duration = 符纸时长)
├── Modifiers:
│ ├── Attribute = RPGAttributeSet.Shield (ModifierOp = Additive, 护盾值增加)
│ ├── Attribute = RPGAttributeSet.DefensePower (ModifierOp = Multiplicitive, 防御力+%)
│ └── Attribute = RPGAttributeSet.BlockRate (ModifierOp = Additive, 格挡率+N%)
└── GameplayTags:
├── GrantedTags: State.Buff.Talisman.Defense
└── AssetTags: SkillType.Talisman.Defense
```
### 类型 C遁术符Instant + Duration 混合)
```
遁术符 = 两段式:
1) Instant GE: 瞬移(位置修改,走 GameplayCue 或直接 SetActorLocation
2) Duration GE: 移速加成 + 闪避率提升(持续 N 秒)
├── Modifiers:
│ ├── Attribute = RPGAttributeSet.MoveSpeed (Multiplicitive, +x%)
│ └── Attribute = RPGAttributeSet.DodgeRate (Additive, +y%)
└── Tags: State.Buff.Talisman.Escape
```
### 类型 D控制符Duration GameplayEffect — 施加于目标)
```
控制符 = UGameplayEffect (DurationPolicy = HasDuration, 施加于 Enemy ASC)
├── Modifiers:
│ ├── Attribute = RPGAttributeSet.MoveSpeed (Multiplicitive, -x%, 减速)
│ └── Attribute = RPGAttributeSet.AttackPower (Multiplicitive, -y%, 降攻)
└── GameplayTags:
├── GrantedTags: State.Debuff.Talisman.Snare (定身/减速)
└── AssetTags: SkillType.Talisman.Control
```
### 类型 E召唤符Infinite Duration GameplayEffect
```
召唤符 = UGameplayEffect (DurationPolicy = Infinite)
├── 触发 GameplayCue → 生成召唤物 Actor
├── Granted Abilities: 召唤物专属技能(如傀儡自动攻击)
└── Tags: State.Buff.Talisman.Summon
```
## 4.3 符箓消耗机制
符箓作为一种**可消耗物品**而非技能冷却资源:
```cpp
// FTalismanItemData — 符箓道具数据结构
USTRUCT(BlueprintType)
struct FTalismanItemData
{
GENERATED_BODY()
// 符箓等级(对应道艺等级 Lv.1~5
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 TalismanLevel = 1;
// 对应的 GameplayEffect Class
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSubclassOf<UGameplayEffect> EffectClass;
// 符箓所属大道属性(决定伤害/效果类型标签)
UPROPERTY(EditAnywhere, BlueprintReadWrite)
ERPGElementType DaoElement = ERPGElementType::SRT_Fire;
// 使用符箓 → ApplyGameplayEffectToTarget/Self → 从背包移除 1 张 → 触发 OnTalismanUsed
};
```
**关键设计差异**:符箓不是通过 GAS Ability 释放,而是通过**物品系统 Apply Effect**——这确保了"不消耗灵力"的设计意图,因为 `UGameplayEffect` 的 `Cost` 策略可以为空。
## 4.4 符修专属 GameplayTag 体系
```
SkillType.Talisman.Attack // 攻击符
SkillType.Talisman.Defense // 防御符
SkillType.Talisman.Escape // 遁术符
SkillType.Talisman.Control // 控制符
SkillType.Talisman.Summon // 召唤符
Effect.Damage.Talisman // 符箓伤害来源标记
State.Buff.Talisman.Defense // 符箓防御 Buff
State.Buff.Talisman.Escape // 符箓遁术 Buff (MoveSpeed/Dodge)
State.Debuff.Talisman.Snare // 符箓减速 Debuff
State.Buff.Talisman.Summon // 符箓召唤 Buff
DaoElement.Fire.Talisman // 火系符箓
DaoElement.Water.Talisman // 水系符箓
// ... 按大道属性细分
Profession.Talisman.Specialist // 符修身份标签(挂载到 RPGFlags
```
## 4.5 属性映射汇总
| 符修机制 | GAS 组件 | 涉及属性 |
|---------|---------|---------|
| 攻击符 | `Instant GE` + `URPGDamageExecution` | Damage, AttackPowerMin/Max, FatalRate, ElementFire... |
| 防御符 | `Duration GE` | Shield, DefensePower, BlockRate |
| 遁术符 | `Instant GE` (位移) + `Duration GE` | MoveSpeed, DodgeRate |
| 控制符 | `Duration GE` (施加目标) | MoveSpeed (减益), AttackPower (减益) |
| 召唤符 | `Infinite GE` + `GameplayCue` | — (生成 Actor) |
| 符纸消耗 | 物品系统 `URPGItemDataAsset` | — (从背包移除) |
| 制符加成 | `CraftingSkillData` + Modifier | 产出数量 ×1.2(流派加成) |
| 符修身份 | `RPGFlags` GameplayTag | `Profession.Talisman.Specialist` |
**核心洞察**:符修的 GAS 映射极简——不需要自定义 `ExecutionCalculation`,不需要新建 `AttributeSet`,不需要修改 `SDHGameplayAbility`。它完全依赖现有的 `UGameplayEffect` + `GameplayTag` + 物品系统三层即可实现,是十个修仙流派中与现有 GAS 架构耦合度最低的一个。
---
# 附录:项目关键文件索引
## 架构文档
| 文件 | 说明 |
|------|------|
| `.trae/documents/02_程序架构/spec.md` | 总体架构(表现层/游戏逻辑/核心框架/引擎) |
| `.trae/documents/02_程序架构/spec_modules.md` | 模块详细规格 |
| `.trae/specs/LivingWorldSystem/spec.md` | LivingWorld 系统完整规格 |
## NPC 系统
| 文件 | 说明 |
|------|------|
| `.trae/documents/01_策划设计/NPC系统与势力/NPC系统设计.md` | NPC 模拟系统完整设计 |
| `Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSNpcTypes.h` | NPC 数据结构FLWSNpcData |
| `Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSNpcPersonalityTypes.h` | 性格/目标/秘密/Need/Action 定义 |
| `Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSNpcSimulationSubsystem.h` | 模拟主循环 |
| `Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSNpcDecisionSystem.h` | 决策系统Utility AI |
| `Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSNpcRelationSubsystem.h` | 关系网络 |
## GAS / 属性系统
| 文件 | 说明 |
|------|------|
| `Plugins/RPGGameCore/Source/RPGGameplayAbility/Public/Character/Attribute/RPGAttributeSet.h` | 单一属性容器5子集 |
| `Plugins/RPGGameCore/Source/RPGGameplayAbility/Public/Character/Attribute/RPGAttributeData.h` | ERPGPhase 境界枚举、灵根 |
| `Plugins/RPGGameCore/Source/RPGGameplayAbility/Public/Config/RPGDamageExecution.h` | GAS 伤害结算 |
| `Plugins/RPGGameCore/Source/RPGGameplayAbility/Public/Abilities/SDHGameplayAbility.h` | 技能基类 |
## ActionPose / 技能系统
| 文件 | 说明 |
|------|------|
| `Plugins/RPGGameCore/Source/RPGGameplayAbility/Public/RPGTypes.h` | ELM_ActionPose 枚举、FLMActionPose |
| `Plugins/RPGGameCore/Source/RPGGameplayAbility/Public/System/SystemData.h` | FRPGSkilllActionPoseTree |
| `Plugins/RPGGameCore/Source/RPGGameplayAbility/Public/LogicStateMachine/SDHSkillStateMachine.h` | 技能状态机 |
## 修仙设计文档
| 文件 | 说明 |
|------|------|
| `.trae/documents/01_策划设计/修仙基础概念设定/修仙概念体系.md` | 流派/道艺/大道属性三层体系 |
| `.trae/documents/01_策划设计/修仙基础概念设定/修仙流派.md` | 十大流派详细设计 |
| `.trae/documents/01_策划设计/修仙基础概念设定/大道元素属性.md` | 37种大道属性 |

View File

@@ -0,0 +1,814 @@
# AIDM 项目测试分析报告
> 生成日期2026-05-30
> 项目D:\MatrixTA\AIGameDev\AIDM
> 基于LivingWorldSystem Spec、ARPGGameDevelopment Research Report、修仙流派设计文档
---
[TOC]
---
## 任务 1NPC 系统完整架构
### 一、分级机制
项目采用 **三层 NPC 分级模型**(来自 `rationale_npc_tier_abc` 超边):
| 级别 | 名称 | 特征 | 数据承载 |
|------|------|------|----------|
| **A 级** | 核心 NPC玩家、重要角色 | 完整 3D Actor、行为树、动画、GAS 属性 | `ARPGCharacterBase` + `URPGAbilitySystemComponent` |
| **B 级** | 势力 NPC宗门弟子、城镇守卫 | 轻量 Actor + 基础动画,结合纯数据模拟 | `ARPGAICharacter` + 部分 `FLWSNpcData` 字段 |
| **C 级** | 泛 NPC平民、路人、妖兽 | **纯数据 USTRUCT**,无 Actor不走 NavMesh六边形坐标跳转 | `FLWSNpcData`(位于 `LWSNpcTypes.h` |
分级依据文档 `00_项目总控/游戏核心创意.md` 中的"NPC锚点和分级"概念,结合 `LivingWorldSystem Spec` 中明确的"NPC 纯数据模拟"策略。
### 二、决策模型
**核心架构Utility AI + 性格权重 + 目标驱动**
```
每个 NPC 每日 Tick:
┌────────────────────────────────────────────┐
│ ULWSNpcSimulationSubsystem (UWorldSubsystem) │
│ ├─ 订阅 URPGGameTimeSubsystem::OnGameTimeChanged │
│ ├─ SimulateDays(DeltaDays) │
│ └─ 每日遍历 ActiveNpcs │
└──────────────┬─────────────────────────────┘
┌────────────────────────────────────────────┐
│ ULWSNpcDecisionSystem (决策引擎) │
│ ├─ DecayNeeds() — 需求自然衰减(生存↑/安全↑/社交↑...
│ ├─ ScoreAction() — 对所有可能行为打分 │
│ │ ├─ 当前需求值 × 行为匹配度 │
│ │ └─ 性格权重因子 PersonalityFactor │
│ └─ ApplyActionToBehavior() — 选最高分执行 │
└──────────────┬─────────────────────────────┘
ELWSNpcAction { Idle, Wander, MoveTo, Trade, Fight, Socialize, Cultivate, ... }
```
**性格三维度**`FLWSNpcPersonality`,位于 `LWSNpcPersonalityTypes.h`
- **冷 ↔ 热**:影响社交行为权重
- **慎 ↔ 莽**:影响战斗/探索行为权重
- **私 ↔ 义**:影响帮助盟友 vs 追求私利
**需求六维度**`ELWSNpcNeed`):生存、安全、社交、权力、资源、修炼
**决策实现位置**
- `Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSNpcDecisionSystem.h`
- `Plugins/RPGGameCore/Source/LivingWorldSystem/Private/LWSNpcDecisionSystem.cpp`
### 三、与时间系统的关系
**时间模型:回合+实时混合制,日为原子单位**Community 15策划世界系统
```
URPGGameTimeSubsystem (UGameInstanceSubsystem跨 World 持续)
├─ FSDHGameRawTime / FSDHGameTime (30天/月, 12月/年)
├─ OnGameTimeChanged 委托广播
├─ 世界时间推进 → AddGameTime(DeltaDays)
└──→ ULWSNpcSimulationSubsystem::SimulateDays(DeltaDays)
├─ 每日遍历所有 NPC
├─ Wander 算法WanderStayProbability 概率不动,否则随机六邻一步)
├─ 调用决策系统 DecideAction
└─ 缓存轨迹(环形 buffer 32 步)
```
**关键时间常量**
- 1 日 = 最小原子单位
- 2 帧完成一轮完整 NPC 模拟60 FPS 下每帧处理 1/30 NPC
- NPC 模拟在**后台线程**批量执行
### 四、存档方式
**双层存档架构**Community 5 + Community 15
```
第一层:世界永久层(跨存档持久化)
├─ LWSWorldSaveGame (USaveGame 子类)
│ ├─ CachedTileCoords / TileBuffer → 瓦片数据
│ ├─ CachedRivers / CachedLakes → 水文数据
│ ├─ CachedFactions → 势力数据
│ ├─ CachedNpcs → NPC 数据 (TArray<FLWSNpcData>)
│ ├─ CachedSpiritualVeins → 灵脉数据
│ └─ CachedSettlements / Resources → 聚落/资源数据
└─ 世界编年史(跨存档的人生记录,待阶段二实现)
第二层:角色存档层(可删除/重建)
└─ URPGPlayerSaveGame
├─ RPGAttributeData → FRPGCharacterAttributeData
├─ SkillTreeDataPool → 技能树学习状态
├─ RPGSkilllActionPoseTree → 架势-技能装配绑定
├─ RPGItemDataMap → 物品容器数据
└─ TalentComponentSaveData → 天赋数据
```
**存档关键实现**
- `ALWSWorldGenerator::SaveWorld()` 写入 `ULWSWorldSaveGame`(蓝图可调用)
- `ALWSWorldGenerator::LoadWorld()` 从存档完整还原世界
- NPC 数据通过 `ULWSNpcSimulationSubsystem::GetActiveNpcsCopy()` / `RestoreNpcsFromSave()` 同步
- **角色存档删除后,世界永久层不受影响**——势力变迁、NPC 死亡/成长、资源消耗写入世界层
---
## 任务 2新增「商队 NPC」跨系统依赖分析
### 一、需要修改的模块及文件
#### 1. NPC 数据结构层(核心修改)
| 文件 | 修改内容 |
|------|---------|
| `Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSNpcTypes.h` | `FLWSNpcData` 新增字段:`TradeRouteIndex`(商队路线 ID`TradeGoods`(货物数组 TMap`TradeFactionOrigin`/`TradeFactionDestination`(起止势力)、`TradeSpeed`(行进速度,单位 hex/日) |
| `Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSNpcPersonalityTypes.h` | 新增 `ELWSNpcBehavior::Trade` 行为枚举值;新增 `ELWSNpcRole::CaravanTrader` 角色类型 |
#### 2. 行为/决策系统
| 文件 | 修改内容 |
|------|---------|
| `Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSNpcDecisionSystem.h` | `DecideAction()` 新增商队逻辑:到达目的地时触发交易结算、反向切换目的地 |
| `Plugins/RPGGameCore/Source/LivingWorldSystem/Private/LWSNpcDecisionSystem.cpp` | 实现 `ScoreAction_Trade()`:需求权重(资源需求×商队距离评分) |
| `Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSNpcSimulationSubsystem.h` | `SimulateDays()` 新增商队每日推进:沿 A* 路径逐格移动(有别于 Wander 随机) |
#### 3. 势力与生成
| 文件 | 修改内容 |
|------|---------|
| `Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSNpcGenerator.h/.cpp` | `GenerateNpcs()` 新增商队生成逻辑:势力边界间配对生成商队 NPC泊松碟约束 + 路线预计算 |
| `Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSFactionTypes.h` | `FLWSFaction` 新增 `TradePartners`(贸易伙伴势力 ID 列表)、`TradeRouteFrequencies`(每条路线商队生成频率) |
| `Plugins/RPGGameCore/Source/LivingWorldSystem/Private/LWSFactionGenerator.cpp` | 势力生成后自动配对邻近势力建立贸易路线 |
#### 4. 寻路系统
| 文件 | 修改内容 |
|------|---------|
| `Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSPathfinder.h` | 商队寻路需在 `FLWSPathfindingParams` 中支持路线成本(避开高危势力区、倾向道路/平原) |
#### 5. 存档
| 文件 | 修改内容 |
|------|---------|
| `Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSWorldSaveGame.h` | `FLWSNpcData` 新增字段会自然序列化;新增 `CachedTradeRoutes``TArray<FLWSTradeRoute>` 路线拓扑数据) |
| `Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSTradeTypes.h` | **新建**`FLWSTradeGoods`(物品 ID + 数量)、`FLWSTradeRoute`(起始势力、途经 hex 坐标数组、路线等级) |
#### 6. 调试可视化
| 文件 | 修改内容 |
|------|---------|
| `Plugins/RPGGameCore/Source/LivingWorldSystem/Private/LWSWorldGenerator.cpp` | 新增 `DebugDrawTradeRoutes()`:商队路线以黄色虚线绘制;商队位置以金色圆点标记 |
| `Plugins/RPGGameCore/Source/LivingWorldSystem/Public/LWSResourceTypes.h` | `ELWSDebugViewMode` 新增 `TradeRoutes` 枚举 |
### 二、存档结构变化
**不需要破坏性变更**`FLWSNpcData` 的字段扩展会被 UE 序列化自动兼容USTRUCT 标记 UPROPERTY 的字段)。需要新增:
```cpp
// 新建 LWSTradeTypes.h
USTRUCT()
struct FLWSTradeRoute
{
UPROPERTY() int32 RouteId;
UPROPERTY() int32 OriginFactionId;
UPROPERTY() int32 DestinationFactionId;
UPROPERTY() TArray<FLWSAxialCoord> Waypoints; // A* 预计算路径
UPROPERTY() float RouteLevel; // 路线等级(影响货物品质)
};
// LWSWorldSaveGame 新增
UPROPERTY() TArray<FLWSTradeRoute> CachedTradeRoutes;
```
**双层存档不受影响**:商队 NPC 属于 `CachedNpcs`(世界永久层),角色存档删除不影响商队。
### 三、对 GAS 属性系统的影响
**不影响 GAS 属性系统。** 原因:
1. 商队 NPC 是**纯数据 C 级 NPC**`FLWSNpcData`),不持有 `URPGAbilitySystemComponent`,不参与 GAS Attribute 同步
2. 商队交易结算是**数值运算**(货物价值 = 距离 × 势力稀缺度 × 路线等级),不涉及战斗伤害管线
3. 如果未来需要"商队被劫掠"战斗,才需要通过 C→B 级 NPC 升级机制接入 GAS
---
## 任务 3为「御剑飞行」新增 ActionPose
### 设计分析
当前 `ELM_ActionPose` 已有 `AirFlying`(空行)作为通用空中飞行态,但**御剑飞行**是剑修专属的战斗力+移动融合态:站在飞剑上高速移动,可在飞行中发起剑技攻击。需要作为独立主架势加入。
**状态流转:**
```
地面起势 (GroundDefault)
│ 释放御剑飞行技能
御剑飞行 (AirSwordFlight) ← 新增
├── 输入方向 → 高速移动(飞行移动速度 ×2.5
├── 左键 (SubPose0) → 飞剑斩(空中剑技连段)
├── 右键 (SubPose1) → 剑罡护体
├── 闪避键 → 御剑急转 (AirSwordFlight → AirDash)
├── 耐力耗尽/被击落 → AirFalling
└── 主动取消/落地 → GroundLanding
```
### 1. RPGTypes.h — 新增枚举值
```cpp
// RPGTypes.h — 在 ELM_ActionPose 枚举中 AirDash 之后插入
enum class ELM_ActionPose : uint8
{
// ... 现有值不变 ...
AirDash, // 空冲(飞行状态下的快速冲击)
// === 御剑飞行(新增)===
AirSwordFlight, // 御剑飞行态:剑修站在飞剑上高速移动+空中战斗
AirSpring, // 空遁
// ... 后续值不变 ...
};
```
### 2. RPGSwordFlightAbility.h — 御剑飞行 GA 头文件(新建)
```cpp
// Plugins/RPGGameCore/Source/RPGGameplayAbility/Public/Abilities/RPGSwordFlightAbility.h
#pragma once
#include "SDHGameplayAbility.h"
#include "GameplayTagContainer.h"
#include "RPGSwordFlightAbility.generated.h"
class AWeaponBase;
class UNiagaraSystem;
class UGameplayEffect;
/**
* 御剑飞行 GameplayAbility
* Lifecycle: ActivateAbility → UpdateAbility(每帧) → EndAbility
* 角色站在飞剑上进入 AirSwordFlight 架势,高速飞行 + 空中剑技
*/
UCLASS(BlueprintType, Blueprintable)
class RPGGAMEPLAYABILITY_API URPGSwordFlightAbility : public URPGGameplayAbility
{
GENERATED_BODY()
public:
URPGSwordFlightAbility();
virtual void ActivateAbility() override;
virtual void UpdateAbility(float DeltaTime) override;
virtual void EndAbility() override;
// === 配置属性 ===
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SwordFlight|Movement")
float FlightSpeed = 2000.f; // 基础飞行速度 (cm/s)
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SwordFlight|Movement")
float MaxAcceleration = 3000.f; // 最大加速度
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SwordFlight|Movement")
float TurnRate = 180.f; // 转向速率 (deg/s)
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SwordFlight|Resource")
float ManaDrainPerSecond = 8.f; // 每秒法力消耗
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SwordFlight|Resource")
float MinManaToActivate = 30.f; // 最低法力阈值(低于此值强制解除)
// === 视觉 ===
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SwordFlight|Visual")
TSubclassOf<AActor> SwordFlightActorClass; // 飞剑 Actor附着到角色脚下
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SwordFlight|Visual")
UNiagaraSystem* FlightTrailEffect; // 飞行拖尾 Niagara 特效
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SwordFlight|Visual")
FRotator SwordMountRotation = FRotator(0.f, 0.f, 15.f); // 飞剑倾斜角度
// === GAS Effect ===
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SwordFlight|Effects")
TSubclassOf<UGameplayEffect> SwordFlightMovementGE; // 修改 MoveSpeed、GravityScale
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SwordFlight|Effects")
TSubclassOf<UGameplayEffect> SwordFlightDamageBonusGE; // 飞剑期间剑技伤害 +25%
// === 蓝图事件 ===
UFUNCTION(BlueprintImplementableEvent, Category = "SwordFlight")
void OnSwordMounted(AActor* SwordActor);
UFUNCTION(BlueprintImplementableEvent, Category = "SwordFlight")
void OnSwordDismounted();
UFUNCTION(BlueprintImplementableEvent, Category = "SwordFlight")
void OnFlightManaDepleted(); // 法力耗尽事件
private:
// === 内部状态 ===
UPROPERTY()
TObjectPtr<AActor> MountedSword; // 当前挂载的飞剑 Actor
UPROPERTY()
TObjectPtr<UNiagaraComponent> TrailNiagara; // 拖尾特效组件
UPROPERTY()
FActiveGameplayEffectHandle MovementGEHandle; // 移动 GE 句柄
UPROPERTY()
FActiveGameplayEffectHandle DamageBonusHandle; // 伤害加成 GE 句柄
bool bIsActive = false;
float CurrentManaDrainAccum = 0.f; // 法力消耗累加器
// --- 内部方法 ---
void SpawnAndMountSword();
void ApplyFlightEffects();
void RemoveFlightEffects();
bool CheckManaSufficient() const;
void DrainManaPerTick(float DeltaTime);
};
```
### 3. RPGSwordFlightAbility.cpp — 关键逻辑实现(核心)
```cpp
// Plugins/RPGGameCore/Source/RPGGameplayAbility/Private/Abilities/RPGSwordFlightAbility.cpp
#include "Abilities/RPGSwordFlightAbility.h"
#include "Character/RPGCharacterBase.h"
#include "Character/Attribute/RPGAttributeSet.h"
#include "Character/Attribute/RPGAttributeComponent.h"
#include "NiagaraComponent.h"
#include "NiagaraFunctionLibrary.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "AbilitySystemComponent.h"
URPGSwordFlightAbility::URPGSwordFlightAbility()
{
// 标记为持续型 Ability非一次性瞬发
}
void URPGSwordFlightAbility::ActivateAbility()
{
Super::ActivateAbility();
ARPGCharacterBase* Owner = Cast<ARPGCharacterBase>(GetSkillUser());
if (!Owner || !CheckManaSufficient())
{
SetAbilityValid(false);
return;
}
// 1. 生成飞剑并挂载到角色脚底
SpawnAndMountSword();
// 2. 应用飞行 GE修改 CMC MoveSpeed、GravityScale
ApplyFlightEffects();
// 3. 角色进入飞行模式
if (UCharacterMovementComponent* CMC = Owner->GetCharacterMovement())
{
CMC->SetMovementMode(MOVE_Flying);
CMC->MaxFlySpeed = FlightSpeed;
CMC->MaxAcceleration = MaxAcceleration;
CMC->BrakingDecelerationFlying = 1000.f;
}
// 4. 设置架势为 御剑飞行
if (USkillSMInstance* SM = Owner->GetSkillSMInstance())
{
SM->SetActionPose(ELM_ActionPose::AirSwordFlight);
}
// 5. 生成长剑拖尾特效
if (FlightTrailEffect)
{
TrailNiagara = UNiagaraFunctionLibrary::SpawnSystemAttached(
FlightTrailEffect,
Owner->GetRootComponent(),
NAME_None,
FVector::ZeroVector,
FRotator::ZeroRotator,
EAttachLocation::KeepRelativeOffset,
true
);
}
bIsActive = true;
SetAbilityValid(true);
}
void URPGSwordFlightAbility::UpdateAbility(float DeltaTime)
{
Super::UpdateAbility(DeltaTime);
if (!bIsActive) return;
// 法力消耗检查
DrainManaPerTick(DeltaTime);
if (!CheckManaSufficient())
{
OnFlightManaDepleted();
EndAbility();
return;
}
// 可选:根据输入方向调整飞剑倾斜
// 通过蓝图 OnFlightUpdate 事件暴露给设计师微调
}
void URPGSwordFlightAbility::EndAbility()
{
Super::EndAbility();
ARPGCharacterBase* Owner = Cast<ARPGCharacterBase>(GetSkillUser());
if (!Owner) return;
// 1. 恢复地面移动模式
if (UCharacterMovementComponent* CMC = Owner->GetCharacterMovement())
{
CMC->SetMovementMode(MOVE_Walking);
CMC->MaxFlySpeed = 600.f; // 恢复默认
}
// 2. 移除 GE
RemoveFlightEffects();
// 3. 销毁拖尾
if (TrailNiagara)
{
TrailNiagara->Deactivate();
TrailNiagara = nullptr;
}
// 4. 拆卸飞剑 → 切换到 AirFalling
OnSwordDismounted();
if (MountedSword)
{
MountedSword->Destroy();
MountedSword = nullptr;
}
// 5. 架势切换:御剑 → 空中下落
if (USkillSMInstance* SM = Owner->GetSkillSMInstance())
{
SM->SetActionPose(ELM_ActionPose::AirFalling);
}
bIsActive = false;
}
// ============ 内部实现 ============
void URPGSwordFlightAbility::SpawnAndMountSword()
{
ARPGCharacterBase* Owner = Cast<ARPGCharacterBase>(GetSkillUser());
if (!Owner || !SwordFlightActorClass) return;
FActorSpawnParameters SpawnParams;
SpawnParams.Owner = Owner;
SpawnParams.Instigator = Owner;
// 飞剑生成在角色脚下
FVector SpawnLoc = Owner->GetActorLocation() + FVector(0, 0, -70.f);
MountedSword = GetWorld()->SpawnActor<AActor>(
SwordFlightActorClass, SpawnLoc, FRotator::ZeroRotator, SpawnParams
);
if (MountedSword)
{
MountedSword->AttachToComponent(
Owner->GetRootComponent(),
FAttachmentTransformRules::KeepRelativeTransform
);
MountedSword->SetActorRelativeLocation(FVector(0, 0, -70.f));
MountedSword->SetActorRelativeRotation(SwordMountRotation);
OnSwordMounted(MountedSword);
}
}
void URPGSwordFlightAbility::ApplyFlightEffects()
{
ARPGCharacterBase* Owner = Cast<ARPGCharacterBase>(GetSkillUser());
if (!Owner) return;
URPGAbilitySystemComponent* ASC = Owner->GetRPGAbilitySystemComponent();
if (!ASC) return;
// 施加飞行移动 GE
if (SwordFlightMovementGE)
{
MovementGEHandle = ASC->ApplyGameplayEffectToSelf(
SwordFlightMovementGE->GetDefaultObject<UGameplayEffect>(),
1.f, ASC->MakeEffectContext()
);
}
// 施加伤害加成 GE
if (SwordFlightDamageBonusGE)
{
DamageBonusHandle = ASC->ApplyGameplayEffectToSelf(
SwordFlightDamageBonusGE->GetDefaultObject<UGameplayEffect>(),
1.f, ASC->MakeEffectContext()
);
}
}
void URPGSwordFlightAbility::RemoveFlightEffects()
{
ARPGCharacterBase* Owner = Cast<ARPGCharacterBase>(GetSkillUser());
if (!Owner) return;
URPGAbilitySystemComponent* ASC = Owner->GetRPGAbilitySystemComponent();
if (!ASC) return;
if (MovementGEHandle.IsValid())
ASC->RemoveActiveGameplayEffect(MovementGEHandle);
if (DamageBonusHandle.IsValid())
ASC->RemoveActiveGameplayEffect(DamageBonusHandle);
}
bool URPGSwordFlightAbility::CheckManaSufficient() const
{
ARPGCharacterBase* Owner = Cast<ARPGCharacterBase>(GetSkillUser());
if (!Owner) return false;
URPGAbilitySystemComponent* ASC = Owner->GetRPGAbilitySystemComponent();
if (!ASC) return false;
return ASC->GetNumericAttribute(URPGAttributeSet::GetManaAttribute()) >= MinManaToActivate;
}
void URPGSwordFlightAbility::DrainManaPerTick(float DeltaTime)
{
CurrentManaDrainAccum += ManaDrainPerSecond * DeltaTime;
if (CurrentManaDrainAccum >= 1.f)
{
int32 DrainAmount = FMath::FloorToInt(CurrentManaDrainAccum);
CurrentManaDrainAccum -= DrainAmount;
ARPGCharacterBase* Owner = Cast<ARPGCharacterBase>(GetSkillUser());
if (Owner)
{
Owner->GetRPGAbilitySystemComponent()->SetNumericAttributeBase(
URPGAttributeSet::GetManaAttribute(),
FMath::Max(0.f, Owner->GetRPGAbilitySystemComponent()->
GetNumericAttribute(URPGAttributeSet::GetManaAttribute()) - DrainAmount)
);
}
}
}
```
---
## 任务 4「符修」技能效果系统设计映射到 GAS Effect
### 设计前提
符修在流派体系中的定位:通过绘制**符文符箓**将法术封印在纸上,战斗中消耗符箓瞬间释放。核心机制是**制符(战前准备)→ 释符(战斗中消耗)**。符文效果通过 GAS GameplayEffect 实现。
### 一、符修核心机制数据模型
#### FRTalismanData.h新建
```cpp
// Plugins/RPGGameCore/Source/RPGGameplayAbility/Public/Abilities/Talisman/FRTalismanData.h
#pragma once
#include "GameplayTagContainer.h"
#include "GameplayEffect.h"
#include "FRTalismanData.generated.h"
/** 符箓等级 */
UENUM(BlueprintType)
enum class ERPGTalismanTier : uint8
{
YellowPaper UMETA(DisplayName = "黄纸符"), // 基础符,单次效果
SilverScript UMETA(DisplayName = "银篆符"), // 中级符可叠加2层
GoldSeal UMETA(DisplayName = "金印符"), // 高级符,效果翻倍
JadeEdict UMETA(DisplayName = "玉敕符"), // 顶级符,范围效果
};
/** 符箓触发方式 */
UENUM(BlueprintType)
enum class ERPGTalismanTrigger : uint8
{
OnUse UMETA(DisplayName = "主动释放"), // 主动从快捷栏使用
OnHit UMETA(DisplayName = "受击触发"), // 被攻击时自动触发
OnLowHP UMETA(DisplayName = "濒死护身"), // HP < 30% 自动触发
OnTimer UMETA(DisplayName = "定时触发"), // 定时延迟触发
};
/** 单个符箓效果定义 — 映射到 1 个 GAS GameplayEffect */
USTRUCT(BlueprintType)
struct RPGGAMEPLAYABILITY_API FRPGTalismanEffect
{
GENERATED_BODY()
// 符箓配置
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Talisman|Identity")
FName TalismanId; // 符箓唯一 ID
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Talisman|Identity")
FText TalismanName; // 显示名:"烈火符"、"金刚符"
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Talisman|Identity")
ERPGTalismanTier Tier = ERPGTalismanTier::YellowPaper;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Talisman|Identity")
ERPGTalismanTrigger TriggerType = ERPGTalismanTrigger::OnUse;
// === GAS 映射 ===
// 核心:每个符箓效果对应一个 UGameplayEffect 蓝图子类
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Talisman|GAS")
TSubclassOf<UGameplayEffect> PrimaryEffectClass; // 主 GE修改属性/施加 Tag
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Talisman|GAS")
TSubclassOf<UGameplayEffect> SecondaryEffectClass; // 副 GE可选范围/连锁效果)
// SetByCaller 动态参数(符箓等级越高,倍率越大)
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Talisman|GAS")
TMap<FGameplayTag, float> SetByCallerMagnitudes; // Tag → 基础数值
// 消耗
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Talisman|Cost")
int32 ManaCost = 20; // 释符消耗法力
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Talisman|Cost")
float CooldownSeconds = 3.f; // 释符冷却
};
```
### 二、符修五大符系 → GAS GameplayEffect 映射表
| 符系 | 代表符箓 | GAS GE 类型 | 修改的 Attribute | Duration Policy | 关键 SetByCaller Tag |
|------|---------|------------|-----------------|-----------------|---------------------|
| **攻击符系** | 烈火符 | `GE_Talisman_Fire` (Instant) | `Damage` (临时变量) | Instant | `SetByCaller.FireDamage` |
| | 冰封符 | `GE_Talisman_Ice` (HasDuration) | `MoveSpeed` (减速 %) | 5s Duration | `SetByCaller.SlowPercent` |
| | 雷击符 | `GE_Talisman_Thunder` (Instant) | `Damage` + `Tough` (削韧) | Instant | `SetByCaller.ThunderDamage` |
| **防御符系** | 金刚符 | `GE_Talisman_Barrier` (HasDuration) | `Shield` (+护盾值) | 10s Duration | `SetByCaller.ShieldAmount` |
| | 铁壁符 | `GE_Talisman_IronWall` (HasDuration) | `DefensePower` (+%) | 15s Duration | `SetByCaller.DefenseBonus` |
| | 净身符 | `GE_Talisman_Cleanse` (Instant) | 移除 `State.Debuff.*` Tag | Instant | — |
| **辅助符系** | 神行符 | `GE_Talisman_Speed` (HasDuration) | `MoveSpeed` (+%) | 20s Duration | `SetByCaller.SpeedBonus` |
| | 聚灵符 | `GE_Talisman_Meditate` (HasDuration) | `Mana` (回复速率) | 30s Duration | `SetByCaller.ManaRegenRate` |
| | 破阵符 | `GE_Talisman_Break` (Instant) | 移除敌方 Buff Tag | Instant | `SetByCaller.DispelCount` |
| **诅咒符系** | 虚弱符 | `GE_Talisman_Weaken` (HasDuration) | `AttackPower` (-%敌方) | 8s Duration | `SetByCaller.AttackReduce` |
| | 定身符 | `GE_Talisman_Root` (HasDuration) | 施加 `State.CrowdControl.Root` Tag | 3s Duration | `SetByCaller.RootDuration` |
| | 噬魂符 | `GE_Talisman_SoulDrain` (HasDuration) | 敌方 `Health` DoT + 自身 `Health` Heal | 6s Duration | `SetByCaller.DrainAmount` |
| **阵法符系** | 困龙阵符 | `GE_Talisman_TrapField` (Infinite/HasDuration) | 范围 `MoveSpeed` -80% | 15s 范围 Duration | `SetByCaller.TrapRadius` |
| | 万剑阵符 | `GE_Talisman_SwordArray` (Instant × N) | 范围内 N 次 `Damage` 结算 | Instant (周期触发) | `SetByCaller.PerSwordDamage` |
| | 回春阵符 | `GE_Talisman_HealField` (Infinite) | 范围内友方 `Health` 持续回复 | UntilCancelled | `SetByCaller.HealPerTick` |
### 三、与现有 GAS Effect 架构的融合
#### 3.1 对接伤害管线
符修攻击符接入现有五段伤害管线(来自 Community 5
```
释符(烈火符)
│ URPGGameplayAbility::ActivateAbility()
├── 构造 FSkillDamageData
│ ├─ SkillDamage = SetByCaller.FireDamage × TierMultiplier
│ ├─ SkillToughDamage = SetByCaller.FireDamage × 0.3
│ └─ HitLevel = ERPGHitLevel::LV1黄纸~ LV3玉敕
└── ARPGCharacterBase::ReceiveDamageEvent()
├── USDHDamageCaculationSetting::CaculateDamage()
│ ├─ 暴击 / 命中 / 闪避 / 防御减伤
│ └─ 输出 { ShieldDamage, ToughDamage, HealthDamage }
├── 优先扣 Shield → 再扣 Tough → 最后扣 Health
└── URPGAttributeSet::PostGameplayEffectExecute
```
#### 3.2 符箓充能与释放系统
```cpp
// URPGFuXiuAbilityComponent — 符修专属 ASC 扩展
UCLASS()
class URPGFuXiuAbilityComponent : public UActorComponent
{
// 符箓背包(最多携带 N 张符)
UPROPERTY()
TArray<FRPGTalismanEffect> EquippedTalismans;
// 制符(非战斗状态,消耗材料+法力+时间)
UFUNCTION(BlueprintCallable)
FRPGTalismanEffect CraftTalisman(FName TalismanId, ERPGTalismanTier Tier);
// 释符(战斗中触发 GAS GE
UFUNCTION(BlueprintCallable)
bool ActivateTalisman(int32 TalismanIndex, AActor* Target = nullptr);
// 符箓等级倍率
float GetTierMultiplier(ERPGTalismanTier Tier) const
{
static const TMap<ERPGTalismanTier, float> Multipliers = {
{ ERPGTalismanTier::YellowPaper, 1.0f },
{ ERPGTalismanTier::SilverScript, 1.5f },
{ ERPGTalismanTier::GoldSeal, 2.2f },
{ ERPGTalismanTier::JadeEdict, 3.0f },
};
return Multipliers.FindRef(Tier);
}
};
```
#### 3.3 SetByCaller 符箓 Tag 体系
```cpp
// 在 SDHGameTagSettings 中新增符箓专属 SetByCaller Tag
namespace RPGSetByCaller
{
// 攻击符
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Talisman_Fire_Damage);
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Talisman_Ice_SlowPercent);
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Talisman_Thunder_Damage);
// 防御符
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Talisman_Barrier_ShieldAmount);
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Talisman_IronWall_DefenseBonus);
// 辅助符
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Talisman_Speed_Bonus);
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Talisman_Meditate_ManaRegen);
// 诅咒符
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Talisman_Weaken_AttackReduce);
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Talisman_SoulDrain_Amount);
// 阵法符
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Talisman_Trap_Radius);
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Talisman_SwordArray_PerSwordDamage);
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Talisman_HealField_HealPerTick);
}
```
### 四、符箓效果示例:「裂空·雷击符」
```
蓝图配置BP_GE_Talisman_Thunder_Base:
Modifiers:
├─ URPGAttributeSet::Damage
│ ├─ ModifierOp: Override
│ └─ Magnitude: SetByCaller (Tag=Talisman_Thunder_Damage)
├─ URPGAttributeSet::Tough
│ ├─ ModifierOp: Add
│ └─ Magnitude: SetByCaller (Tag=Talisman_Thunder_Damage) × -0.3
└─ GameplayTag:
└─ Grant: State.HitReaction.Stagger击退硬直
Duration Policy: Instant
Damage 计算链:
Base Damage = SetByCaller.Talisman_Thunder_Damage (50)
× TierMultiplier (黄纸=1.0 / 银篆=1.5 / 金印=2.2 / 玉敕=3.0)
× 符修修为加成 (境界 × 0.05)
× 当前灵力系数 (Mana/MaxMana × 0.5 + 0.5)
黄纸雷击符 = 50 × 1.0 × 1.0 × 1.0 = 50 伤害 + 15 削韧
玉敕雷击符 = 50 × 3.0 × 2.0 × 1.0 = 300 伤害 + 90 削韧
```
### 五、涉及文件清单
| 文件(新建/修改) | 内容 |
|------|------|
| `Public/Abilities/Talisman/FRTalismanData.h` | **新建**:符箓数据结构、枚举 |
| `Public/Abilities/Talisman/URPGFuXiuAbilityComponent.h/.cpp` | **新建**:符修 ASC 扩展组件 |
| `Public/Abilities/Talisman/GE_Talisman_*.h` | **新建**5 类 14 个 GameplayEffect 蓝图基类 |
| `Public/Config/SDHGameTagSettings.h` | **修改**:新增 SetByCaller.Talisman_* Tag |
| `Public/Character/Attribute/RPGAttributeSet.h` | **不变**:符箓几何修改现有 Health/Shield/Tough/Mana/MoveSpeed/AttackPower/DefensePower 属性 |
| `Public/Items/RPGItem.h` | **修改**:新增 `URPGTalismanItem` 物品类型 |
| `.trae/documents/01_策划设计/修仙基础概念设定/修仙流派.md` | **修改**:符修独立为独立流派(当前在魔修路径下) |
---
## 附录:关键文件速查
| 缩写 | 全称 | 核心文件位置 |
|------|------|------------|
| LWS | LivingWorldSystem | `Plugins/RPGGameCore/Source/LivingWorldSystem/` |
| GA/GAS | Gameplay Ability System | `Plugins/RPGGameCore/Source/RPGGameplayAbility/` |
| ASC | AbilitySystemComponent | `URPGAbilitySystemComponent` |
| GE | GameplayEffect | UGameplayEffect 蓝图子类 |
| CMC | CharacterMovementComponent | `LCMCharacterMovementComponent` |
| ActionPose | 架势(主姿态) | `ELM_ActionPose``RPGTypes.h:203`24 个主架势 |
| SM | SkillStateMachine | `USkillSMInstance` (LogicDriver) |

View File

@@ -0,0 +1,66 @@
---
title: NPC行为迭代 — 图谱 vs 传统 对比测试
date: 2026-05-28
tags:
- test
- npc
- benchmark
---
# 对比测试Graphify + GitNexus vs 传统 grep
## 唯一变量
两个场景使用**完全相同的提示词**,唯一的差异是 CLAUDE.md 环境:
| | 场景 A传统 | 场景 B图谱 |
|---|---|---|
| CLAUDE.md | 移除 GitNexus 段、Graphify 段、路由优先级段 | 完整的 CLAUDE.md含图谱路由规则 |
| 操作 | 备份原 CLAUDE.md → 删除 3 个段 → 测试 → 恢复 | 无需操作,直接测试 |
## 共用提示词
两个场景均使用以下提示词(直接复制粘贴):
```
我现在在 UE5.7 游戏项目 AIDM 的根目录。
我想迭代 NPC 之间的行为逻辑。帮我:
1. 总结当前 NPC 系统的设计规范和已知问题
2. 梳理 NPC 相关的代码结构和核心类
3. 告诉我从哪些类/方法开始介入开发
完成时告诉我:读取了多少文件、大致 token 消耗。
```
## 环境准备
### 场景 A传统方式
在新 CC 会话中,先移除 CLAUDE.md 中的图谱相关段:
- 删除 `<!-- gitnexus:start -->``<!-- gitnexus:end -->` 之间的整个 GitNexus 段
- 删除 `## graphify`
- 删除 `## 路由优先级`
- 删除 `## Group 配置`
然后粘贴共用提示词。
### 场景 B图谱方式
在新 CC 会话中,确保 CLAUDE.md 完整,直接粘贴共用提示词。
## 对比记录表
| 指标 | 场景 A传统 | 场景 B图谱 |
|------|---------------|---------------|
| 读取文件数 | | |
| GitNexus 调用次数 | - | |
| Graphify 调用次数 | - | |
| Token 消耗(估算) | | |
| 结果完整性 | /10 | /10 |
| 用时 | | |
# 结果
- No Graphify:[[图谱 vs 传统 NPC 对比测试]]
- Graphify:[[AIDM_NPC与修仙系统分析报告_Graphify]]