← 返回项目技术总览

TransformCharacter 代码逐行解析

本文对 TransformCharacter.hTransformCharacter.cpp 中的每一行代码进行解释,帮助理解角色形态变换与飞行遁速系统的实现。


一、TransformCharacter.h

1  // TransformCharacter.h - 角色形态变换系统
2  // 支持多形态切换、Niagara 特效、Enhanced Input 绑定

解释:文件头注释,说明本文件是角色形态变换系统的头文件,原设计支持多形态切换、Niagara 特效和 Enhanced Input(实际使用传统 BindKey)。

3  
4  #pragma once

解释:预处理指令,确保头文件在单个编译单元中只被包含一次,避免重复定义。

5  
6  #include "CoreMinimal.h"

解释:包含 UE 核心最小头文件,提供 UCLASSUFUNCTIONUPROPERTY 等反射宏和基础类型。

7  #include "GameFramework/Character.h"

解释:包含 UE 的角色基类头文件,ATransformCharacter 继承自 ACharacter

8  #include "TransformCharacter.generated.h"

解释:包含 UHT(Unreal Header Tool)生成的反射代码头文件,必须放在所有 include 的最后。

9  
10 class UNiagaraSystem;

解释:前置声明 Niagara 系统类,避免在头文件中直接 include,减少编译依赖。

11 class UInputAction;

解释:前置声明 Enhanced Input 的输入动作类(保留属性,实际未在 C++ 中使用)。

12 class UInputMappingContext;

解释:前置声明 Enhanced Input 的输入映射上下文类(保留属性,实际未在 C++ 中使用)。

13 class UMaterialParameterCollection;

解释:前置声明材质参数集合类,用于溶解效果。

14 
15 /** 形态变换事件委托 */
16 DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnFormChanged, int32, OldFormIndex, int32, NewFormIndex);

解释:声明一个动态多播委托。FOnFormChanged 有两个 int32 参数:OldFormIndexNewFormIndex,用于在形态切换时广播事件给蓝图。

17 
18 UCLASS()

解释:UE 反射宏,标记下面的类需要被 UHT 处理并生成反射代码。

19 class TEST_API ATransformCharacter : public ACharacter

解释:定义 ATransformCharacter 类,继承自 ACharacter,并导出模块 API(TEST_API 由模块名自动生成)。

20 {
21     GENERATED_BODY()

解释GENERATED_BODY() 是 UHT 生成的反射代码插入点,替代旧的 GENERATED_UCLASS_BODY()

22 
23 public:
24     ATransformCharacter();

解释:声明默认构造函数,用于初始化组件和默认值。

25 
26 protected:
27     virtual void BeginPlay() override;

解释:重写 AActor::BeginPlay(),在游戏开始时执行一些初始化逻辑(如速度兜底、资产引用修复)。

28     virtual void PossessedBy(AController* NewController) override;

解释:重写 APawn::PossessedBy(),当角色被控制器控制时调用。当前实现为空,保留扩展性。

29 
30     /** 输入绑定:C++ 覆盖后也会调用蓝图中的 SetupPlayerInputComponent 实现 */
31     virtual void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) override;

解释:重写 ACharacter::SetupPlayerInputComponent(),用于绑定 T 键和鼠标右键。

32 
33 public:
34     virtual void Tick(float DeltaTime) override;

解释:重写 AActor::Tick(),每帧调用一次,处理溶解动画、飞行移动、Niagara 速度传递等持续逻辑。

35 
36     // ========== 形态系统 ==========

解释:注释分隔线,标记下面是形态系统相关属性。

37 
38     /** 可用形态列表(Character 子类) */
39     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform|Forms")
40     TArray<TSubclassOf<ACharacter>> Forms;

解释:声明一个可在编辑器中编辑、可在蓝图中读写的数组,存储多个角色形态类。TSubclassOf 保证数组元素必须是 ACharacter 的子类。

41 
42     /** 当前形态索引 */
43     UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Transform|Forms")
44     int32 FormIndex = 0;

解释:当前形态的数组索引,默认值为 0。VisibleAnywhere 表示在属性面板中可见但不可编辑,只读。

45 
46     /** 切换到下一个形态 */
47     UFUNCTION(BlueprintCallable, Category = "Transform")
48     void TransformToNextForm();

解释:声明一个蓝图可调用的函数,用于切换到下一个形态。当前实现直接调用 OnTransformInput()

49 
50     /** 切换到指定形态 */
51     UFUNCTION(BlueprintCallable, Category = "Transform")
52     void TransformToForm(int32 Index);

解释:声明一个蓝图可调用的函数,按指定索引切换形态。当前实现也直接调用 OnTransformInput()

53 
54     /** 获取当前形态类 */
55     UFUNCTION(BlueprintPure, Category = "Transform")
56     TSubclassOf<ACharacter> GetCurrentFormClass() const;

解释:声明一个纯函数(无副作用),返回当前形态对应的 Character 子类。当前未实际使用。

57 
58     /** 形态变化事件 */
59     UPROPERTY(BlueprintAssignable, Category = "Transform")
60     FOnFormChanged OnFormChanged;

解释:声明形态变化委托变量,蓝图可以绑定这个事件来响应形态切换。

61 
62     // ========== 特效 ==========

解释:注释分隔线,标记下面是特效相关属性。

63 
64     /** 变身爆发 Niagara 系统 */
65     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform|VFX")
66     TObjectPtr<UNiagaraSystem> TransformEffect;

解释:声明 Niagara 系统引用,用于变身爆发特效。当前未在 C++ 中使用,保留给蓝图配置。

67 
68     /** 变身特效持续时间 */
69     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform|VFX")
70     float TransformEffectDuration = 1.5f;

解释:变身特效持续时间,默认 1.5 秒。当前未在 C++ 中使用。

71 
72     // ========== 输入 ==========

解释:注释分隔线,标记下面是输入相关属性。

73 
74     /** 变身输入动作 */
75     UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
76     TObjectPtr<UInputAction> IA_Transform;

解释:Enhanced Input 的输入动作引用。当前未在 C++ 中使用,保留属性以便未来迁移。

77 
78     /** 默认输入映射上下文 */
79     UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
80     TObjectPtr<UInputMappingContext> DefaultMappingContext;

解释:Enhanced Input 的输入映射上下文引用。当前未在 C++ 中使用。

81 
82     /** 输入映射上下文优先级 */
83     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
84     int32 MappingContextPriority = 0;

解释:输入映射上下文优先级。当前未在 C++ 中使用。

85 
86     // ========== 相机 ==========

解释:注释分隔线,标记下面是相机相关属性。

87 
88     /** 变身时相机过渡时间 */
89     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform|Camera")
90     float CameraBlendTime = 0.3f;

解释:相机混合过渡时间,默认 0.3 秒。当前未在 C++ 中使用。

91 
92 protected:
93     /** 变身输入处理 */
94     void OnTransformInput();

解释:声明变身输入处理函数,按 T 键时调用。

95 
96     /** 鼠标右键加速输入处理 */
97     void OnBoostInput();

解释:声明鼠标右键加速输入处理函数。

98 
99     /** 服务端执行变身(RPC) */
100    UFUNCTION(Server, Reliable)
101    void Server_ExecuteTransform(int32 NewFormIndex);

解释:声明一个可靠的服务端 RPC 函数,用于多玩家环境下的变身同步。当前实现为空。

102 
103    /** 执行变身逻辑(C++ 实现) */
104    void ExecuteTransform(int32 NewFormIndex);

解释:声明执行变身逻辑的函数。当前实现为空。

105 
106    /** 多播播放变身特效 */
107    UFUNCTION(NetMulticast, Reliable)
108    void Multicast_PlayTransformEffect();

解释:声明一个可靠的多播 RPC 函数,用于多玩家环境下同步播放变身特效。当前调用 PlayTransformEffect()

109 
110    /** 播放特效 */
111    void PlayTransformEffect();
112 
113    /** 变身冷却计时器 */
114    FTimerHandle TransformCooldownTimer;

解释:声明变身冷却计时器句柄,用于防止连续触发变身。

115 
116    /** 是否正在变身中 */
117    bool bIsTransforming = false;

解释:标记是否正在执行变身流程,防止在冷却期间重复触发。

118 
119    /** 变身冷却时间 */
120    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform")
121    float TransformCooldown = 0.5f;

解释:变身冷却时间,默认 0.5 秒。

122 
123    /** 变身形态显示网格(Sphere 等,已废弃改用 FormNiagaraComponent) */
124    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Transform|Components")
125    TObjectPtr<class UStaticMeshComponent> FormMeshComponent;

解释:声明静态网格组件,用于早期变身形态的球体显示。当前已废弃,但保留兼容。

126 
127    /** 加速 Niagara 特效(按鼠标右键进入加速时播放一次) */
128    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform|VFX")
129    TObjectPtr<UNiagaraSystem> BoostEffect;

解释:声明加速特效 Niagara 系统引用,按鼠标右键进入加速时播放一次。

130 
131    /** 变身闪光过渡特效(按 T 瞬间播放,0.4s 后才正式变身) */
132    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform|VFX")
133    TObjectPtr<UNiagaraSystem> TransformFlashEffect;

解释:声明变身闪光过渡特效,按 T 瞬间播放,延迟后才正式变身。

134 
135    /** 变身闪光过渡延迟(秒),闪光播放多久后才正式变身 */
136    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform|VFX")
137    float TransformFlashDelay = 0.4f;

解释:闪光过渡延迟时间,默认 0.4 秒。

138 
139    /** 溶解效果 MPC 资源(Material Parameter Collection,驱动角色材质从四周→中间溶解) */
140    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform|VFX")
141    TObjectPtr<UMaterialParameterCollection> DissolveMPC;

解释:声明材质参数集合引用,用于驱动角色溶解效果。

142 
143    /** MPC 中的溶解进度参数名 */
144    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform|VFX")
145    FName DissolveParamName = "DissolveProgress";

解释:声明 MPC 中控制溶解进度的参数名,默认 "DissolveProgress"

146 
147    /** 变身特效蓝图 Actor 类(包含所有 Niagara 组件) */
148    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform|VFX")
149    TSubclassOf<AActor> TransformVFXBlueprint;

解释:声明变身特效蓝图 Actor 类,变身后会生成该 Actor。

150 
151    /** 已生成的变身特效 Actor 实例 */
152    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Transform|VFX")
153    AActor* SpawnedTransformVFX = nullptr;

解释:声明已生成的变身特效 Actor 实例指针,用于后续销毁。

154 
155    /** 是否处于变身形态 */
156    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Transform")
157    bool bIsInTransformedForm = false;

解释:标记当前是否处于变身形态。

158 
159    /** 是否处于加速状态 */
160    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Transform")
161    bool bIsBoosting = false;

解释:标记当前是否处于加速状态。

162 
163    /** 普通飞行速度 */
164    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform|Flight")
165    float NormalFlySpeed = 1200.0f;

解释:普通飞行状态下的自动前进速度,默认 1200 cm/s。

166 
167    /** 普通操控速度 */
168    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform|Flight")
169    float NormalSteerSpeed = 800.0f;
170 ```

**解释**:普通飞行状态下的 WASD 操控速度,默认 800 cm/s。

```cpp
171 
172    /** 加速飞行速度 */
173    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform|Flight")
174    float BoostFlySpeed = 3000.0f;

解释:加速状态下的自动前进速度,默认 3000 cm/s(运行时被 BeginPlay() 覆盖为 5000)。

175 
176    /** 加速操控速度 */
177    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform|Flight")
178    float BoostSteerSpeed = 2000.0f;

解释:加速状态下的 WASD 操控速度,默认 2000 cm/s(运行时被 BeginPlay() 覆盖为 4000)。

179 
180 private:
181    /** 闪光过渡计时器 */
182    FTimerHandle TransformFlashTimer;

解释:声明闪光过渡计时器句柄,用于延迟执行正式变身/退出。

183 
184    /** 正式执行进入变身逻辑(闪光结束后调用) */
185    void ExecuteEnterTransform();

解释:声明闪光结束后执行的进入变身函数。

186 
187    /** 正式执行退出变身逻辑(瞬间) */
188    void ExecuteExitTransform();

解释:声明闪光结束后执行的退出变身函数。

189 
190    /** 溶解动画是否激活 */
191    bool bDissolveActive = false;

解释:标记溶解动画是否正在进行。

192 
193    /** 溶解消失的起始值(0=可见,1=溶解消失) */
194    float DissolveStart = 0.0f;

解释:溶解起始值,0 表示完全可见,1 表示完全溶解消失。

195 
196    /** 溶解消失的目标值 */
197    float DissolveTarget = 0.0f;

解释:溶解目标值。

198 
199    /** 溶解已用时间 */
200    float DissolveElapsed = 0.0f;

解释:溶解动画已经过的时间。

201 
202    /** 开始溶解(进入变身,0→1) */
203    void StartDissolveOut();
204 
205    /** 开始反溶解(退出变身,1→0) */
206    void StartDissolveIn();
207 };

解释:声明开始溶解和反溶解的辅助函数。最后的 }; 结束类定义。


二、TransformCharacter.cpp

1  // TransformCharacter.cpp - 角色形态变换系统实现

解释:文件头注释,说明本文件是 TransformCharacter.h 的实现文件。

2  
3  #include "TransformCharacter.h"

解释:包含对应的头文件。

4  #include "Components/StaticMeshComponent.h"

解释:包含静态网格组件头文件,用于创建 FormMeshComponent

5  #include "GameFramework/CharacterMovementComponent.h"

解释:包含角色移动组件头文件,用于控制移动模式和速度。

6  #include "NiagaraFunctionLibrary.h"

解释:包含 Niagara 函数库头文件,用于生成 Niagara 特效。

7  #include "NiagaraComponent.h"

解释:包含 Niagara 组件头文件,用于获取和操作 Niagara 组件。

8  #include "NiagaraSystem.h"

解释:包含 Niagara 系统头文件,用于声明 Niagara 系统引用。

9  #include "Engine/World.h"

解释:包含世界头文件,用于生成 Actor、射线检测等。

10 #include "TimerManager.h"

解释:包含计时器管理器头文件,用于设置延迟调用。

11 #include "Components/CapsuleComponent.h"

解释:包含胶囊体组件头文件,用于获取角色胶囊体做地面检测。

12 #include "Camera/CameraComponent.h"
13 #include "GameFramework/PlayerController.h"

解释:包含相机组件和玩家控制器头文件(相机组件 include 保留,实际 FOV 通过 PlayerCameraManager 设置)。

14 #include "Kismet/GameplayStatics.h"

解释:包含 Gameplay 静态工具头文件,当前未使用,保留。

15 #include "Engine/Blueprint.h"

解释:包含蓝图头文件,用于加载 UBlueprint 资产。

16 #include "GameFramework/ProjectileMovementComponent.h"

解释:包含投射物移动组件头文件,用于禁用变身特效 Actor 上的投射物移动。

17 #include "Materials/MaterialParameterCollectionInstance.h"

解释:包含材质参数集合实例头文件,用于写入 MPC 参数值。

18 
19 ATransformCharacter::ATransformCharacter()

解释:定义默认构造函数。

20 {
21     PrimaryActorTick.bCanEverTick = true;

解释:启用每帧 Tick 调用。

22 
23     // 创建变身形态显示网格组件(保留兼容,实际使用蓝图 Actor 中的 Niagara 组件)
24     FormMeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("FormMesh"));

解释:在构造函数中创建静态网格子对象,命名为 FormMesh

25     FormMeshComponent->SetupAttachment(RootComponent);

解释:将网格组件附加到根组件(胶囊体)上。

26     FormMeshComponent->SetVisibility(false);

解释:默认隐藏该网格组件。

27     FormMeshComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);

解释:关闭该网格组件的碰撞。

28 
29     // 加载引擎内置 Sphere 网格(已废弃,保留兼容)
30     static ConstructorHelpers::FObjectFinder<UStaticMesh> SphereMesh(TEXT("/Engine/BasicShapes/Sphere"));

解释:使用 ConstructorHelpers::FObjectFinder 在构造函数中加载引擎内置球体网格。

31     if (SphereMesh.Succeeded())

解释:如果加载成功。

32     {
33         FormMeshComponent->SetStaticMesh(SphereMesh.Object);

解释:将球体网格设置到组件上。

34         FormMeshComponent->SetRelativeLocation(FVector(0, 0, -40));

解释:设置网格相对位置,向下偏移 40 单位。

35         FormMeshComponent->SetRelativeScale3D(FVector(1.2f));

解释:设置网格相对缩放为 1.2 倍。

36     }
37 
38     // 加载默认变身特效蓝图:BP_Projectile_Ice(Niagara 结合而成的变身特效 Actor)
39     static ConstructorHelpers::FObjectFinder<UBlueprint> DefaultVFXBlueprint(TEXT("/Game/Test_02/VFX/VFXBP/BP_Projectile_Ice"));

解释:加载变身特效蓝图 Actor 类。

40     if (DefaultVFXBlueprint.Succeeded() && DefaultVFXBlueprint.Object->GeneratedClass)

解释:如果加载成功且已生成类存在。

41     {
42         TransformVFXBlueprint = DefaultVFXBlueprint.Object->GeneratedClass;
43     }

解释:将生成的 UClass 赋值给 TransformVFXBlueprint

44 
45     // 加载默认变身闪光过渡特效:NS_SpriteSpawn(闪光后 0.4s 才正式变身)
46     static ConstructorHelpers::FObjectFinder<UNiagaraSystem> DefaultFlashEffect(TEXT("/Game/Test_02/VFX/FlashVFX/NS_SpriteSpawn"));

解释:加载变身闪光过渡特效 Niagara 系统。

47     if (DefaultFlashEffect.Succeeded())
48     {
49         TransformFlashEffect = DefaultFlashEffect.Object;
50     }

解释:加载成功则赋值给 TransformFlashEffect

51 
52     // 加载默认加速特效:NS_JiaSu(变身后按右键加速时播放一次)
53     static ConstructorHelpers::FObjectFinder<UNiagaraSystem> DefaultBoostEffect(TEXT("/Game/Test_02/VFX/FeiDunLight/NS/NS_JiaSu"));

解释:加载加速特效 Niagara 系统。

54     if (DefaultBoostEffect.Succeeded())
55     {
56         BoostEffect = DefaultBoostEffect.Object;
57     }

解释:加载成功则赋值给 BoostEffect

58 
59     // 配置移动组件
60     UCharacterMovementComponent* Movement = GetCharacterMovement();

解释:获取角色的移动组件。

61     if (Movement)
62     {
63         Movement->bOrientRotationToMovement = true;

解释:设置角色朝向移动方向。

64         Movement->RotationRate = FRotator(0.0f, 540.0f, 0.0f);

解释:设置旋转速率为每秒 540 度(只在 Yaw 轴)。

65     }
66 
67     bUseControllerRotationPitch = false;
68     bUseControllerRotationYaw = false;
69     bUseControllerRotationRoll = false;

解释:关闭角色使用控制器旋转,避免与自定义飞行控制冲突。

70 }
71 
72 void ATransformCharacter::BeginPlay()
73 {
74     Super::BeginPlay();

解释:调用父类的 BeginPlay()

75 
76     // 兜底:若蓝图 CDO 把 TransformVFXBlueprint 序列化为空,则用 BP_Projectile_Ice 覆盖
77     if (!TransformVFXBlueprint)
78     {
79         UBlueprint* IceBP = LoadObject<UBlueprint>(nullptr, TEXT("/Game/Test_02/VFX/VFXBP/BP_Projectile_Ice"));

解释:如果蓝图 CDO 导致 TransformVFXBlueprint 为空,则运行时重新加载。

80         if (IceBP && IceBP->GeneratedClass)
81         {
82             TransformVFXBlueprint = IceBP->GeneratedClass;
83         }
84     }
85 
86     // 兜底:强制设置飞行速度(防止蓝图 CDO 序列化覆盖为 0)
87     NormalFlySpeed  = 1200.0f;
88     NormalSteerSpeed = 800.0f;
89     BoostFlySpeed   = 5000.0f;
90     BoostSteerSpeed = 4000.0f;
91 }

解释:在 BeginPlay() 中强制设置速度值,覆盖可能因蓝图 CDO 序列化导致的 0 值。

92 
93 void ATransformCharacter::PossessedBy(AController* NewController)
94 {
95     Super::PossessedBy(NewController);
96 }

解释:重写 PossessedBy,当前为空实现。

97 
98 void ATransformCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
99 {
100    Super::SetupPlayerInputComponent(PlayerInputComponent);

解释:调用父类的输入绑定。

101 
102    if (PlayerInputComponent)
103    {
104        PlayerInputComponent->BindKey(EKeys::T, IE_Pressed, this, &ATransformCharacter::OnTransformInput);

解释:绑定 T 键按下事件到 OnTransformInput

105        PlayerInputComponent->BindKey(EKeys::RightMouseButton, IE_Pressed, this, &ATransformCharacter::OnBoostInput);

解释:绑定鼠标右键按下事件到 OnBoostInput

106        GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, TEXT("[Transform] T / RMB bound"));
107    }
108 }

解释:在屏幕上打印输入绑定成功的调试信息。

109 
110 void ATransformCharacter::Tick(float DeltaTime)
111 {
112    Super::Tick(DeltaTime);

解释:调用父类 Tick,然后执行自定义逻辑。

113 
114    // ========== 溶解动画(进入变身/退出变身的过渡效果)==========
115    if (bDissolveActive)
116    {
117        DissolveElapsed += DeltaTime;

解释:如果溶解动画激活,累加经过的时间。

118        float Progress = FMath::Clamp(DissolveElapsed / TransformFlashDelay, 0.0f, 1.0f);

解释:计算溶解进度,限制在 0~1 之间。

119        float DissolveValue = FMath::Lerp(DissolveStart, DissolveTarget, Progress);

解释:在起始值和目标值之间插值,得到当前溶解值。

120 
121        // 写入 MPC,驱动材质溶解
122        if (DissolveMPC)
123        {
124            UMaterialParameterCollectionInstance* MPCInstance = GetWorld()->GetParameterCollectionInstance(DissolveMPC);

解释:如果配置了 MPC,获取其世界实例。

125            if (MPCInstance)
126            {
127                MPCInstance->SetScalarParameterValue(DissolveParamName, DissolveValue);
128            }
129        }

解释:将溶解值写入 MPC 的标量参数。

130 
131        if (Progress >= 1.0f)
132        {
133            bDissolveActive = false;
134        }
135    }

解释:溶解完成,关闭标记。

136 
137    // ========== 变身飞行系统 ==========
138    if (!bIsInTransformedForm)
139    {
140        return;
141    }

解释:如果不在变身形态,跳过飞行逻辑。

142 
143    // 获取移动组件
144    UCharacterMovementComponent* MoveComp = GetCharacterMovement();
145    if (!MoveComp)
146    {
147        return;
148    }
149 
150    // 清除蓝图 WASD 移动输入(否则按 S 会同时触发"后退",与飞行方向控制冲突)
151    MoveComp->ConsumeInputVector();

解释:获取移动组件,并清空已有的输入向量,避免与蓝图输入冲突。

152 
153    // 获取玩家视角方向
154    APlayerController* PC = Cast<APlayerController>(GetController());
155    if (!PC)
156    {
157        return;
158    }

解释:获取玩家控制器并转换为 APlayerController,用于读取视角方向和按键状态。

159    const FRotator ControlRot = PC->GetControlRotation();
160    const FVector ViewForward = FRotationMatrix(ControlRot).GetUnitAxis(EAxis::X);
161    const FVector ViewRight   = FRotationMatrix(ControlRot).GetUnitAxis(EAxis::Y);

解释:获取控制器旋转,并计算视角的前向量和右向量。

162 
163    const float AutoFlySpeed = bIsBoosting ? BoostFlySpeed : NormalFlySpeed;
164    const float SteerSpeed = bIsBoosting ? BoostSteerSpeed : NormalSteerSpeed;

解释:根据是否加速,选择对应的速度值。

165 
166    // 调试:显示当前速度和加速状态
167    FString DebugMsg = FString::Printf(TEXT("[Transform] Speed=%.0f | Boost=%s"),
168        AutoFlySpeed, bIsBoosting ? TEXT("ON") : TEXT("OFF"));
169    GEngine->AddOnScreenDebugMessage(-1, 0.05f, FColor::Cyan, DebugMsg);
170 }

解释:在屏幕上打印当前速度和加速状态。

171 
172    // ========== 直接计算目标速度并设置 Velocity(绕过 MaxAcceleration 限制)==========
173    FVector DesiredVelocity = ViewForward * AutoFlySpeed;

解释:根据视角前向量和自动飞行速度计算基础目标速度。

174 
175    // W → 向上飞(世界 Z+)
176    if (PC->IsInputKeyDown(EKeys::W))
177    {
178        DesiredVelocity += FVector::UpVector * SteerSpeed;
179    }
180 
181    // S → 向下飞(世界 Z-,地面限制)
182    if (PC->IsInputKeyDown(EKeys::S))
183    {
184        UCapsuleComponent* Capsule = GetCapsuleComponent();

解释:如果按下 W,给目标速度加上向上的分量;如果按下 S,准备做地面检测。

185        if (Capsule)
186        {
187            const float HalfHeight = Capsule->GetScaledCapsuleHalfHeight();
188            const FVector FeetPos = GetActorLocation() - FVector(0, 0, HalfHeight);
189            const FVector TraceEnd = FeetPos - FVector(0, 0, HalfHeight * 2.0f);

解释:获取胶囊体半高,计算脚底位置和射线终点。

190 
191            FHitResult Hit;
192            FCollisionQueryParams Params;
193            Params.AddIgnoredActor(this);
194 
195            bool bNearGround = GetWorld()->LineTraceSingleByChannel(Hit, FeetPos, TraceEnd, ECC_Visibility, Params);

解释:从脚底向下做射线检测,判断角色是否接近地面。

196            if (!bNearGround)
197            {
198                DesiredVelocity += FVector::DownVector * SteerSpeed;
199            }
200        }
201    }
202 
203    // A → 向左飞(视角左方)
204    if (PC->IsInputKeyDown(EKeys::A))
205    {
206        DesiredVelocity += -ViewRight * SteerSpeed;
207    }
208 
209    // D → 向右飞(视角右方)
210    if (PC->IsInputKeyDown(EKeys::D))
211    {
212        DesiredVelocity += ViewRight * SteerSpeed;
213    }

解释:根据 A/D 按键给目标速度加上左右分量。

214 
215    // 平滑过渡到目标速度(避免瞬间突变,加速/减速都有手感)
216    const float SpeedLerpRate = 8.0f;
217    FVector NewVelocity = FMath::VInterpTo(MoveComp->Velocity, DesiredVelocity, DeltaTime, SpeedLerpRate);
218    MoveComp->Velocity = NewVelocity;

解释:使用 VInterpTo 在 8.0 的速率下平滑过渡当前速度到目标速度,然后赋值给移动组件。

219 
220    // 提高上限,防止 CharacterMovement 把速度压回去
221    MoveComp->MaxFlySpeed = FMath::Max(MoveComp->MaxFlySpeed, AutoFlySpeed * 1.2f);
222    MoveComp->MaxAcceleration = FMath::Max(MoveComp->MaxAcceleration, 10000.0f);
223 }

解释:确保 MaxFlySpeedMaxAcceleration 足够大,防止 CharacterMovement 限制速度。

224 
225    // ========== 将角色世界速度传给 Niagara User 参数 ==========
226    if (SpawnedTransformVFX)
227    {
228        TArray<UNiagaraComponent*> NiagaraComps;
229        SpawnedTransformVFX->GetComponents<UNiagaraComponent>(NiagaraComps);

解释:如果变身特效 Actor 存在,获取其下所有 Niagara 组件。

230 
231        const FVector FlyVel = GetVelocity();
232 
233        for (UNiagaraComponent* NiagaraComp : NiagaraComps)
234        {
235            // 传飞行速度给 Niagara(拖尾惯性甩尾 + Projectile 朝向)
236            NiagaraComp->SetVariableVec3(FName("User.FlyVelocity"), FlyVel);
237        }
238    }
239 }

解释:遍历 Niagara 组件,将角色世界速度传递给名为 User.FlyVelocity 的用户参数。

240 
241 // ========== 形态切换 ==========
242 
243 void ATransformCharacter::OnTransformInput()
244 {
245    GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Cyan, TEXT("[Transform] FIRED!"));

解释:定义变身输入处理函数,先在屏幕上打印触发信息。

246 
247    if (bIsTransforming)
248    {
249        return;
250    }

解释:如果正在变身流程中(冷却中),直接返回。

251 
252    bIsTransforming = true;
253 
254    if (bIsInTransformedForm)
255    {
256        // 退出变身:立刻标记为非变身状态,停止 Tick 中的飞行逻辑
257        bIsInTransformedForm = false;

解释:如果当前处于变身形态,执行退出变身逻辑。先立即标记为非变身,停止飞行 Tick。

258 
259        // 立刻停止飞行移动,避免延迟期间继续往前飞
260        UCharacterMovementComponent* PreMoveComp = GetCharacterMovement();
261        if (PreMoveComp)
262        {
263            PreMoveComp->Velocity = FVector::ZeroVector;
264            PreMoveComp->ConsumeInputVector();
265            PreMoveComp->StopMovementImmediately();
266            PreMoveComp->SetMovementMode(MOVE_Walking);
267        }

解释:立即停止移动:清零速度、清空输入、停止移动、切换为行走模式。

268 
269        // 退出变身:先播闪光 + 销毁特效 + 显示角色网格 + 溶解出现
270        if (SpawnedTransformVFX)
271        {
272            SpawnedTransformVFX->Destroy();
273            SpawnedTransformVFX = nullptr;
274        }

解释:销毁变身特效 Actor。

275 
276        // 显示网格(溶解会从 1→0 让角色逐渐出现)
277        USkeletalMeshComponent* BodyMesh = GetMesh();
278        if (BodyMesh)
279        {
280            BodyMesh->SetVisibility(true);
281        }

解释:显示角色身体网格。

282 
283        StartDissolveIn();

解释:开始从 1→0 的反溶解动画。

284 
285        // 播放闪光
286        if (TransformFlashEffect)
287        {
288            UNiagaraFunctionLibrary::SpawnSystemAtLocation(
289                GetWorld(),
290                TransformFlashEffect,
291                GetActorLocation(),
292                GetActorRotation(),
293                FVector(1.0f),
294                true,
295                true
296            );
297        }

解释:如果配置了闪光特效,在角色位置生成一次性闪光特效。

298 
299        // 延迟 0.4s 后正式退出变身
300        GetWorldTimerManager().SetTimer(
301            TransformFlashTimer,
302            this,
303            &ATransformCharacter::ExecuteExitTransform,
304            TransformFlashDelay,
305            false
306        );
307    }

解释:设置 0.4 秒延迟计时器,时间到后调用 ExecuteExitTransform()

308    else
309    {
310        // 进入变身:先播闪光透特 + 溶解消失,延迟后才正式变身
311        GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Orange, TEXT("[Transform] Playing flash VFX..."));

解释:如果当前不是变身形态,执行进入变身逻辑。

312 
313        StartDissolveOut();

解释:开始从 0→1 的溶解动画。

314 
315        // 播放一次性闪光特效(不附着、不跟随移动)
316        if (TransformFlashEffect)
317        {
318            UNiagaraFunctionLibrary::SpawnSystemAtLocation(
319                GetWorld(),
320                TransformFlashEffect,
321                GetActorLocation(),
322                GetActorRotation(),
323                FVector(1.0f),
324                true,  // bAutoDestroy
325                true   // bAutoActivate
326            );
327        }

解释:在角色位置生成闪光特效,true, true 表示自动销毁和自动激活。

328 
329        // 延迟 0.4s 后正式变身
330        GetWorldTimerManager().SetTimer(
331            TransformFlashTimer,
332            this,
333            &ATransformCharacter::ExecuteEnterTransform,
334            TransformFlashDelay,
335            false
336        );
337    }
338 }

解释:设置 0.4 秒延迟计时器,时间到后调用 ExecuteEnterTransform()

339 
340 void ATransformCharacter::OnBoostInput()
341 {
342    GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Yellow,
343        FString::Printf(TEXT("[Transform] RMB pressed | bIsInTransformedForm=%s"),
344            bIsInTransformedForm ? TEXT("true") : TEXT("false")));

解释:定义鼠标右键加速输入处理函数,打印当前是否处于变身形态。

345 
346    // 只有变身状态下才能切换加速
347    if (!bIsInTransformedForm)
348    {
349        return;
350    }

解释:如果不在变身形态,不处理右键加速。

351 
352    bIsBoosting = !bIsBoosting;

解释:切换加速状态。

353 
354    // 同步修改 CharacterMovementComponent 的最大飞行速度上限
355    if (UCharacterMovementComponent* MoveComp = GetCharacterMovement())
356    {
357        MoveComp->MaxFlySpeed = bIsBoosting ? BoostFlySpeed : NormalFlySpeed;
358    }

解释:根据加速状态同步修改 MaxFlySpeed

359 
360    // 只在进入加速时播放一次特效,退出加速不播放
361    if (bIsBoosting)
362    {
363        if (BoostEffect)
364        {
365            FVector SpawnLoc = GetActorLocation() + FVector(0, 0, 50);

解释:如果进入加速状态且配置了加速特效,计算生成位置(角色位置上方 50 单位)。

366            UNiagaraFunctionLibrary::SpawnSystemAtLocation(
367                GetWorld(),
368                BoostEffect,
369                SpawnLoc,
370                GetActorRotation(),
371                FVector(2.0f),
372                true,  // bAutoDestroy
373                true   // bAutoActivate
374            );
375            GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Green, TEXT("[Transform] Boost VFX spawned"));
376        }
377        else
378        {
379            GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Red, TEXT("[Transform] BoostEffect is NULL!"));
380        }
381    }

解释:生成加速特效并打印调试信息;如果特效为空则打印错误。

382 
383    GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Yellow,
384        bIsBoosting ? TEXT("[Transform] Boost ON") : TEXT("[Transform] Boost OFF"));
385 }

解释:打印当前加速状态。

386 
387 void ATransformCharacter::ExecuteEnterTransform()
388 {
389    bIsInTransformedForm = true;

解释:定义闪光结束后的正式进入变身函数,标记为变身形态。

390 
391    // 隐藏角色网格
392    USkeletalMeshComponent* BodyMesh = GetMesh();
393    if (BodyMesh)
394    {
395        BodyMesh->SetVisibility(false);
396    }

解释:隐藏角色身体网格。

397 
398    // 生成变身特效蓝图 Actor
399    if (TransformVFXBlueprint && !SpawnedTransformVFX)
400    {
401        FActorSpawnParameters SpawnParams;
402        SpawnParams.Owner = this;

解释:如果配置了变身特效蓝图且尚未生成,准备生成参数并设置 Owner 为当前角色。

403        SpawnedTransformVFX = GetWorld()->SpawnActor<AActor>(TransformVFXBlueprint, FVector::ZeroVector, FRotator::ZeroRotator, SpawnParams);
404        if (SpawnedTransformVFX)
405        {
406            SpawnedTransformVFX->AttachToActor(this, FAttachmentTransformRules::KeepRelativeTransform);

解释:在世界原点生成特效 Actor,并附加到角色上,保持相对变换。

407            SpawnedTransformVFX->SetActorRelativeLocation(FVector(0.0f, 0.0f, 0.0f));
408 
409            // 禁用投射物移动组件
410            UProjectileMovementComponent* ProjMove = SpawnedTransformVFX->FindComponentByClass<UProjectileMovementComponent>();
411            if (ProjMove)
412            {
413                ProjMove->Deactivate();
414            }

解释:设置相对位置为原点,并禁用特效 Actor 上的投射物移动组件。

415 
416            // 强制激活所有 Niagara 组件
417            TArray<UNiagaraComponent*> NiagaraComps;
418            SpawnedTransformVFX->GetComponents<UNiagaraComponent>(NiagaraComps);
419            for (UNiagaraComponent* NiagaraComp : NiagaraComps)
420            {
421                NiagaraComp->Activate(true);
422            }
423        }
424    }

解释:获取特效 Actor 下所有 Niagara 组件并强制激活。

425 
426    // 切换飞行模式
427    UCharacterMovementComponent* MoveComp = GetCharacterMovement();
428    if (MoveComp)
429    {
430        MoveComp->SetMovementMode(MOVE_Flying);
431        MoveComp->MaxFlySpeed = NormalFlySpeed;
432    }

解释:切换移动模式为飞行,并初始化最大飞行速度为普通速度。

433 
434    // FOV 放大
435    APlayerController* PC_FOV = Cast<APlayerController>(GetController());
436    if (PC_FOV && PC_FOV->PlayerCameraManager)
437    {
438        PC_FOV->PlayerCameraManager->SetFOV(120.0f);
439    }

解释:通过玩家相机管理器将 FOV 设置为 120。

440 
441    // 广播事件
442    OnFormChanged.Broadcast(FormIndex, 1);

解释:广播形态变化事件,参数为旧形态索引和新形态索引。

443 
444    // 冷却
445    GetWorldTimerManager().SetTimer(
446        TransformCooldownTimer,
447        [this]() { bIsTransforming = false; },
448        TransformCooldown,
449        false
450    );
451 }

解释:设置冷却计时器,时间到后重置 bIsTransforming

452 
453 void ATransformCharacter::ExecuteExitTransform()
454 {
455    bIsInTransformedForm = false;

解释:定义闪光结束后的正式退出变身函数,标记为非变身形态。

456 
457    // 切换行走模式并立即停止移动
458    UCharacterMovementComponent* MoveComp = GetCharacterMovement();
459    if (MoveComp)
460    {
461        MoveComp->SetMovementMode(MOVE_Walking);
462        MoveComp->Velocity = FVector::ZeroVector;
463        MoveComp->ConsumeInputVector();
464        MoveComp->StopMovementImmediately();
465    }

解释:切换为行走模式并立即停止移动。

466 
467    // 退出变身时重置加速状态
468    bIsBoosting = false;

解释:退出变身时重置加速状态。

469 
470    // FOV 恢复
471    APlayerController* PC_FOV = Cast<APlayerController>(GetController());
472    if (PC_FOV && PC_FOV->PlayerCameraManager)
473    {
474        PC_FOV->PlayerCameraManager->SetFOV(80.0f);
475    }

解释:恢复 FOV 为 80。

476 
477    // 广播事件
478    OnFormChanged.Broadcast(FormIndex, 0);

解释:广播形态变化事件。

479 
480    // 冷却
481    GetWorldTimerManager().SetTimer(
482        TransformCooldownTimer,
483        [this]() { bIsTransforming = false; },
484        TransformCooldown,
485        false
486    );
487 }

解释:设置冷却计时器。

488 
489 void ATransformCharacter::StartDissolveOut()
490 {
491    DissolveStart = 0.0f;
492    DissolveTarget = 1.0f;
493    DissolveElapsed = 0.0f;
494    bDissolveActive = true;
495 }

解释:开始溶解消失:从 0 过渡到 1,并重置经过时间,激活溶解标记。

496 
497 void ATransformCharacter::StartDissolveIn()
498 {
499    DissolveStart = 1.0f;
500    DissolveTarget = 0.0f;
501    DissolveElapsed = 0.0f;
502    bDissolveActive = true;
503 }

解释:开始反溶解出现:从 1 过渡到 0。

504 
505 void ATransformCharacter::Server_ExecuteTransform_Implementation(int32 NewFormIndex)
506 {
507 }

解释:服务端 RPC 实现,当前为空。

508 
509 void ATransformCharacter::TransformToNextForm()
510 {
511    OnTransformInput();
512 }
513 
514 void ATransformCharacter::TransformToForm(int32 Index)
515 {
516    OnTransformInput();
517 }

解释:形态切换函数实现,当前都直接调用 OnTransformInput()

518 
519 TSubclassOf<ACharacter> ATransformCharacter::GetCurrentFormClass() const
520 {
521    if (Forms.IsValidIndex(FormIndex))
522    {
523        return Forms[FormIndex];
524    }
525    return nullptr;
526 }

解释:返回当前形态类,如果索引无效则返回空。

527 
528 void ATransformCharacter::ExecuteTransform(int32 NewFormIndex)
529 {
530 }

解释:变身逻辑执行函数,当前为空。

531 
532 void ATransformCharacter::Multicast_PlayTransformEffect_Implementation()
533 {
534    PlayTransformEffect();
535 }
536 
537 void ATransformCharacter::PlayTransformEffect()
538 {
539    // 不再动态生成,使用常驻组件 TransformNiagaraComponent
540    // 激活/停用逻辑已在 OnTransformInput() 中处理
541 }

解释:多播 RPC 实现调用 PlayTransformEffect(),后者已废弃,特效生成逻辑已移到 OnTransformInput() 中。


三、废弃代码清单与迁移注意事项

A 类:安全可删除(无任何引用,删除零风险)

代码位置 废弃说明
TransformEffect / TransformEffectDuration(.h 特效区) 变身爆发特效属性,C++ 从未读写,纯死属性
CameraBlendTime(.h 相机区) 相机过渡时间,从未使用
PossessedBy() 空实现(.cpp) 无逻辑的空重写,删掉不影响
#include "Camera/CameraComponent.h"(.cpp) FOV 实际通过 PlayerCameraManager 设置,未用到该头文件
#include "Kismet/GameplayStatics.h"(.cpp) 未使用的 include
Server_ExecuteTransform 空实现(.cpp) 多玩家 RPC,单人项目未启用
ExecuteTransform(int32) 空实现(.cpp) 未使用的空函数
Multicast_PlayTransformEffect / PlayTransformEffect(.cpp) 已废弃,特效逻辑已迁至 OnTransformInput()

B 类:谨慎删除(需连带处理蓝图引用)

代码位置 废弃说明 删除前检查
FormMeshComponent(Sphere 网格,.h + 构造函数) 已废弃,实际用 Niagara。但蓝图 BP_ThirdPersonCharacter 组件列表里有 FormMesh 实例 删除 C++ 成员后必须重新编译蓝图,否则蓝图残留该组件引用
Forms / FormIndex / TransformToNextForm() / TransformToForm() / GetCurrentFormClass() 多形态系统,当前只用单形态 检查蓝图是否调用过这些 BlueprintCallable 函数
OnFormChanged 事件委托 ExecuteEnter/ExitTransformBroadcast 检查蓝图是否绑定了该事件,绑定则需同步处理
IA_Transform / DefaultMappingContext / MappingContextPriority Enhanced Input 属性,实际用传统 BindKey 检查蓝图属性面板是否设置了引用,删前需清空

C 类:不可删除(当前功能正在依赖)

迁移到正式项目(CultivatingImmortality_RPG)注意事项

  1. 模块 API 宏TEST_API 需替换为正式项目的模块名宏(如 CULTIVATINGIMMORTALITY_RPG_API),类名建议一并改为正式项目风格。

  2. 资产路径硬编码:构造函数中的 FObjectFinder 路径(/Game/Test_02/...)是测试项目路径,迁移时要么改成正式项目路径,要么保留 UPROPERTY 由蓝图配置(推荐后者,更灵活)。

  3. 模块依赖.Build.cs 中需包含 Niagara 相关模块(NiagaraNiagaraCoreNiagaraShader 等),迁移时在正式项目 .Build.cs 补上。

  4. 变身特效 ActorBP_Projectile_Ice 及其内部引用的 Niagara 资产(NS_StarSkill002、NS_Dash、NS_JiaSu、NS_SpriteSpawn 等)需要一并复制到正式项目,并保持相对路径一致。

  5. 输入绑定冲突:测试项目用传统 BindKey。正式项目若用 Enhanced Input 且已占用 T 键 / 鼠标右键,需要调整键位或改用 Enhanced Input 方式绑定。

  6. 角色材质:溶解效果需要在正式项目的角色材质中自行添加 CollectionParameterOneMinusOpacityMask 节点链,并创建对应的 MPC 资产。

  7. 蓝图 CDO 覆盖陷阱:正式项目中若在蓝图属性面板修改过飞行速度等参数,会覆盖 C++ 默认值(本项目就踩过坑:速度被序列化为 0)。保持 BeginPlay() 兜底赋值即可规避。

  8. FOV 与速度是测试值:120/80 FOV、1200/800/5000/4000 速度均为测试项目调参结果,正式项目按手感重新调整。

  9. 编译方式:修改 .h(新增/删除 UPROPERTY、UFUNCTION)必须关闭编辑器完整重编译;只改 .cpp 函数体可用 Live Coding。