TransformCharacter Line-by-Line Code Walkthrough
This article explains every line of code in TransformCharacter.h and TransformCharacter.cpp, to help you understand the implementation of the form-change and flight-dash system.
1. TransformCharacter.h
1 // TransformCharacter.h - 角色形态变换系统
2 // 支持多形态切换、Niagara 特效、Enhanced Input 绑定
Explanation: File header comment stating this file is the header of the form-change system. The original design supports multi-form switching, Niagara effects and Enhanced Input (traditional BindKey is used in practice).
3
4 #pragma once
Explanation: Preprocessor directive ensuring the header is included only once per compilation unit, avoiding redefinition.
5
6 #include "CoreMinimal.h"
Explanation: Includes the UE core minimal header, providing reflection macros such as UCLASS, UFUNCTION, UPROPERTY and base types.
7 #include "GameFramework/Character.h"
Explanation: Includes the UE character base-class header. TransformBase inherits from ACharacter.
8 #include "TransformCharacter.generated.h"
Explanation: Includes the reflection-code header generated by UHT (Unreal Header Tool). It must come after all other includes.
9
10 class UNiagaraSystem;
Explanation: Forward-declares the Niagara system class to avoid including it directly in the header and reduce compile dependencies.
11 class UInputAction;
Explanation: Forward-declares the Enhanced Input action class (retained as a property but not actually used in C++).
12 class UInputMappingContext;
Explanation: Forward-declares the Enhanced Input mapping context class (retained as a property but not actually used in C++).
13 class UMaterialParameterCollection;
Explanation: Forward-declares the material parameter collection class, used for the dissolve effect.
14
15 /** 形态变换事件委托 */
16 DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnFormChanged, int32, OldFormIndex, int32, NewFormIndex);
Explanation: Declares a dynamic multicast delegate. FOnFormChanged takes two int32 parameters: OldFormIndex and NewFormIndex, used to broadcast events to Blueprints on form change.
17
18 UCLASS()
Explanation: UE reflection macro. Marks that the class below must be processed by UHT and have reflection code generated.
19 class TEST_API TransformBase : public ACharacter
Explanation: Defines the TransformBase class, inheriting from ACharacter, and exports the module API (TEST_API is auto-generated from the module name).
20 {
21 GENERATED_BODY()
Explanation: GENERATED_BODY() is the reflection code insertion point generated by UHT, replacing the old GENERATED_UCLASS_BODY().
22
23 public:
24 TransformBase();
Explanation: Declares the default constructor for initializing components and default values.
25
26 protected:
27 virtual void BeginPlay() override;
Explanation: Overrides AActor::BeginPlay(), executing some initialization logic when the game starts (such as speed fallback and asset-reference repair).
28 virtual void PossessedBy(AController* NewController) override;
Explanation: Overrides APawn::PossessedBy(), called when the character is taken over by a controller. The current implementation is empty to preserve extensibility.
29
30 /** 输入绑定:C++ 覆盖后也会调用蓝图中的 SetupPlayerInputComponent 实现 */
31 virtual void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) override;
Explanation: Overrides ACharacter::SetupPlayerInputComponent(), used to bind the T key and the right mouse button.
32
33 public:
34 virtual void Tick(float DeltaTime) override;
Explanation: Overrides AActor::Tick(), called once per frame to handle the dissolve animation, flight movement, Niagara speed pass-through and other persistent logic.
35
36 // ========== 形态系统 ==========
Explanation: Comment divider. Marks the form-system related properties below.
37
38 /** 可用形态列表(Character 子类) */
39 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform|Forms")
40 TArray<TSubclassOf<ACharacter>> Forms;
Explanation: Declares an array that is editable in the editor and read/write in Blueprints, storing multiple form classes. TSubclassOf guarantees that each element is a subclass of ACharacter.
41
42 /** 当前形态索引 */
43 UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Transform|Forms")
44 int32 FormIndex = 0;
Explanation: Array index of the current form, default 0. VisibleAnywhere means it is visible but not editable in the property panel, read-only.
45
46 /** 切换到下一个形态 */
47 UFUNCTION(BlueprintCallable, Category = "Transform")
48 void TransformToNextForm();
Explanation: Declares a BlueprintCallable function to switch to the next form. The current implementation calls OnTransformInput() directly.
49
50 /** 切换到指定形态 */
51 UFUNCTION(BlueprintCallable, Category = "Transform")
52 void TransformToForm(int32 Index);
Explanation: Declares a BlueprintCallable function to switch form by the given index. The current implementation also calls OnTransformInput() directly.
53
54 /** 获取当前形态类 */
55 UFUNCTION(BlueprintPure, Category = "Transform")
56 TSubclassOf<ACharacter> GetCurrentFormClass() const;
Explanation: Declares a pure function (no side effects) that returns the Character subclass for the current form. Not actually used.
57
58 /** 形态变化事件 */
59 UPROPERTY(BlueprintAssignable, Category = "Transform")
60 FOnFormChanged OnFormChanged;
Explanation: Declares the form-change event delegate variable. Blueprints can bind this event to react to form switches.
61
62 // ========== 特效 ==========
Explanation: Comment divider. Marks the VFX related properties below.
63
64 /** 变身爆发 Niagara 系统 */
65 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform|VFX")
66 TObjectPtr<UNiagaraSystem> TransformEffect;
Explanation: Declares a Niagara system reference for the transform-burst effect. Not used in C++; kept for Blueprint configuration.
67
68 /** 变身特效持续时间 */
69 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform|VFX")
70 float TransformEffectDuration = 1.5f;
Explanation: Transform effect duration, default 1.5 s. Not used in C++.
71
72 // ========== 输入 ==========
Explanation: Comment divider. Marks the input related properties below.
73
74 /** 变身输入动作 */
75 UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
76 TObjectPtr<UInputAction> IA_Transform;
Explanation: Enhanced Input action reference. Not used in C++; retained for future migration.
77
78 /** 默认输入映射上下文 */
79 UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
80 TObjectPtr<UInputMappingContext> DefaultMappingContext;
Explanation: Enhanced Input mapping context reference. Not used in C++.
81
82 /** 输入映射上下文优先级 */
83 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
84 int32 MappingContextPriority = 0;
Explanation: Input mapping context priority. Not used in C++.
85
86 // ========== 相机 ==========
Explanation: Comment divider. Marks the camera related properties below.
87
88 /** 变身时相机过渡时间 */
89 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform|Camera")
90 float CameraBlendTime = 0.3f;
Explanation: Camera blend transition time, default 0.3 s. Not used in C++.
91
92 protected:
93 /** 变身输入处理 */
94 void OnTransformInput();
Explanation: Declares the transform input handler function, invoked when the T key is pressed.
95
96 /** 鼠标右键加速输入处理 */
97 void OnBoostInput();
Explanation: Declares the right-mouse-button boost input handler function.
98
99 /** 服务端执行变身(RPC) */
100 UFUNCTION(Server, Reliable)
101 void Server_ExecuteTransform(int32 NewFormIndex);
Explanation: Declares a reliable server RPC function for transform synchronization in multi-player contexts. The current implementation is empty.
102
103 /** 执行变身逻辑(C++ 实现) */
104 void ExecuteTransform(int32 NewFormIndex);
Explanation: Declares the function that executes transform logic. The current implementation is empty.
105
106 /** 多播播放变身特效 */
107 UFUNCTION(NetMulticast, Reliable)
108 void Multicast_PlayTransformEffect();
Explanation: Declares a reliable multicast RPC function to synchronously play the transform effect in multi-player contexts. Currently calls PlayTransformEffect().
109
110 /** 播放特效 */
111 void PlayTransformEffect();
112
113 /** 变身冷却计时器 */
114 FTimerHandle TransformCooldownTimer;
Explanation: Declares the transform cooldown timer handle, used to prevent consecutive transform triggers.
115
116 /** 是否正在变身中 */
117 bool bIsTransforming = false;
Explanation: Marks whether the transform flow is in progress, preventing re-triggering during cooldown.
118
119 /** 变身冷却时间 */
120 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform")
121 float TransformCooldown = 0.5f;
Explanation: Transform cooldown, default 0.5 s.
122
123 /** 变身形态显示网格(Sphere 等,已废弃改用 FormNiagaraComponent) */
124 UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Transform|Components")
125 TObjectPtr<class UStaticMeshComponent> FormMeshComponent;
Explanation: Declares the static mesh component for the early transform-form sphere display. Currently deprecated but retained for compatibility.
126
127 /** 加速 Niagara 特效(按鼠标右键进入加速时播放一次) */
128 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform|VFX")
129 TObjectPtr<UNiagaraSystem> BoostEffect;
Explanation: Declares the boost-effect Niagara system reference, played once when entering boost via the right mouse button.
130
131 /** 变身闪光过渡特效(按 T 瞬间播放,0.4s 后才正式变身) */
132 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform|VFX")
133 TObjectPtr<UNiagaraSystem> TransformFlashEffect;
Explanation: Declares the transform-flash transition effect, played instantly on T; the actual transform happens only after a delay.
134
135 /** 变身闪光过渡延迟(秒),闪光播放多久后才正式变身 */
136 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform|VFX")
137 float TransformFlashDelay = 0.4f;
Explanation: Flash transition delay, default 0.4 s.
138
139 /** 溶解效果 MPC 资源(Material Parameter Collection,驱动角色材质从四周→中间溶解) */
140 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform|VFX")
141 TObjectPtr<UMaterialParameterCollection> DissolveMPC;
Explanation: Declares the material parameter collection reference used to drive the character dissolve effect.
142
143 /** MPC 中的溶解进度参数名 */
144 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform|VFX")
145 FName DissolveParamName = "DissolveProgress";
Explanation: Declares the parameter name in the MPC that controls the dissolve progress, default "DissolveProgress".
146
147 /** 变身特效蓝图 Actor 类(包含所有 Niagara 组件) */
148 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform|VFX")
149 TSubclassOf<AActor> TransformVFXBlueprint;
Explanation: Declares the transform-effect Blueprint Actor class; that Actor is spawned after the transform.
150
151 /** 已生成的变身特效 Actor 实例 */
152 UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Transform|VFX")
153 AActor* SpawnedTransformVFX = nullptr;
Explanation: Declares the pointer to the spawned transform-effect Actor instance, used for later destruction.
154
155 /** 是否处于变身形态 */
156 UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Transform")
157 bool bIsInTransformedForm = false;
Explanation: Marks whether the character is currently in a transformed form.
158
159 /** 是否处于加速状态 */
160 UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Transform")
161 bool bIsBoosting = false;
Explanation: Marks whether the character is currently in a boost state.
162
163 /** 普通飞行速度 */
164 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform|Flight")
165 float NormalFlySpeed = 1200.0f;
Explanation: Auto-forward speed in normal flight, default 1200 cm/s.
166
167 /** 普通操控速度 */
168 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform|Flight")
169 float NormalSteerSpeed = 800.0f;
Explanation: WASD steering speed in normal flight, default 800 cm/s.
171
172 /** 加速飞行速度 */
173 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform|Flight")
174 float BoostFlySpeed = 3000.0f;
Explanation: Auto-forward speed in the boost state, default 3000 cm/s (overridden to 5000 at runtime by BeginPlay()).
175
176 /** 加速操控速度 */
177 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform|Flight")
178 float BoostSteerSpeed = 2000.0f;
Explanation: WASD control speed in the boost state, default 2000 cm/s (overridden to 4000 at runtime by BeginPlay()).
179
180 private:
181 /** 闪光过渡计时器 */
182 FTimerHandle TransformFlashTimer;
Explanation: Declares the flash transition timer handle, used to delay the actual transform enter/exit.
183
184 /** 正式执行进入变身逻辑(闪光结束后调用) */
185 void ExecuteEnterTransform();
Explanation: Declares the enter-transform function executed after the flash ends.
186
187 /** 正式执行退出变身逻辑(瞬间) */
188 void ExecuteExitTransform();
Explanation: Declares the exit-transform function executed after the flash ends.
189
190 /** 溶解动画是否激活 */
191 bool bDissolveActive = false;
Explanation: Marks whether the dissolve animation is in progress.
192
193 /** 溶解消失的起始值(0=可见,1=溶解消失) */
194 float DissolveStart = 0.0f;
Explanation: Dissolve start value: 0 means fully visible, 1 means fully dissolved out.
195
196 /** 溶解消失的目标值 */
197 float DissolveTarget = 0.0f;
Explanation: Dissolve target value.
198
199 /** 溶解已用时间 */
200 float DissolveElapsed = 0.0f;
Explanation: Elapsed time of the dissolve animation.
201
202 /** 开始溶解(进入变身,0→1) */
203 void StartDissolveOut();
204
205 /** 开始反溶解(退出变身,1→0) */
206 void StartDissolveIn();
207 };
Explanation: Declares the start-dissolve and reverse-dissolve helper functions. The final }; closes the class definition.
2. TransformCharacter.cpp
1 // TransformCharacter.cpp - 角色形态变换系统实现
Explanation: File header comment, stating this file is the implementation file for TransformCharacter.h.
2
3 #include "TransformCharacter.h"
Explanation: Includes the corresponding header file.
4 #include "Components/StaticMeshComponent.h"
Explanation: Includes the static mesh component header, used to create FormMeshComponent.
5 #include "GameFramework/CharacterMovementComponent.h"
Explanation: Includes the character movement component header, used to control movement mode and speed.
6 #include "NiagaraFunctionLibrary.h"
Explanation: Includes the Niagara function library header, used to spawn Niagara effects.
7 #include "NiagaraComponent.h"
Explanation: Includes the Niagara component header, used to retrieve and manipulate Niagara components.
8 #include "NiagaraSystem.h"
Explanation: Includes the Niagara system header, used to declare Niagara system references.
9 #include "Engine/World.h"
Explanation: Includes the world header, used to spawn Actors, perform ray tests, etc.
10 #include "TimerManager.h"
Explanation: Includes the timer manager header, used to set delayed calls.
11 #include "Components/CapsuleComponent.h"
Explanation: Includes the capsule component header, used to retrieve the character capsule for ground checks.
12 #include "Camera/CameraComponent.h"
13 #include "GameFramework/PlayerController.h"
Explanation: Includes the camera component and player controller headers (the camera component include is retained; the FOV is actually set via the PlayerCameraManager).
14 #include "Kismet/GameplayStatics.h"
Explanation: Includes the Gameplay statics header. Not currently used, retained.
15 #include "Engine/Blueprint.h"
Explanation: Includes the Blueprint header, used to load UBlueprint assets.
16 #include "GameFramework/ProjectileMovementComponent.h"
Explanation: Includes the projectile movement component header, used to disable projectile movement on the transform-effect Actor.
17 #include "Materials/MaterialParameterCollectionInstance.h"
Explanation: Includes the material parameter collection instance header, used to write MPC parameter values.
18
19 TransformBase::TransformBase()
Explanation: Defines the default constructor.
20 {
21 PrimaryActorTick.bCanEverTick = true;
Explanation: Enables per-frame Tick calls.
22
23 // 创建变身形态显示网格组件(保留兼容,实际使用蓝图 Actor 中的 Niagara 组件)
24 FormMeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("FormMesh"));
Explanation: Creates the static mesh child object in the constructor, named FormMesh.
25 FormMeshComponent->SetupAttachment(RootComponent);
Explanation: Attaches the mesh component to the root component (the capsule).
26 FormMeshComponent->SetVisibility(false);
Explanation: Hides that mesh component by default.
27 FormMeshComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
Explanation: Disables collision on that mesh component.
28
29 // 加载引擎内置 Sphere 网格(已废弃,保留兼容)
30 static ConstructorHelpers::FObjectFinder<UStaticMesh> SphereMesh(TEXT("/Engine/BasicShapes/Sphere"));
Explanation: Uses ConstructorHelpers::FObjectFinder to load the engine's built-in sphere mesh in the constructor.
31 if (SphereMesh.Succeeded())
Explanation: If the load succeeded (True branch).
32 {
33 FormMeshComponent->SetStaticMesh(SphereMesh.Object);
Explanation: Sets the sphere mesh onto the component.
34 FormMeshComponent->SetRelativeLocation(FVector(0, 0, -40));
Explanation: Sets the mesh relative position, offset 40 units downward.
35 FormMeshComponent->SetRelativeScale3D(FVector(1.2f));
Explanation: Sets the mesh relative scale to 1.2x.
36 }
37
38 // 加载默认变身特效蓝图:BP_Projectile_Ice(Niagara 结合而成的变身特效 Actor)
39 static ConstructorHelpers::FObjectFinder<UBlueprint> DefaultVFXBlueprint(TEXT("/Game/Test_02/VFX/VFXBP/BP_Projectile_Ice"));
Explanation: Loads the transform-effect Blueprint Actor class.
40 if (DefaultVFXBlueprint.Succeeded() && DefaultVFXBlueprint.Object->GeneratedClass)
Explanation: If the load succeeded and the class was generated (True branch).
41 {
42 TransformVFXBlueprint = DefaultVFXBlueprint.Object->GeneratedClass;
43 }
Explanation: Assigns the generated UClass to TransformVFXBlueprint.
44
45 // 加载默认变身闪光过渡特效:NS_SpriteSpawn(闪光后 0.4s 才正式变身)
46 static ConstructorHelpers::FObjectFinder<UNiagaraSystem> DefaultFlashEffect(TEXT("/Game/Test_02/VFX/FlashVFX/NS_SpriteSpawn"));
Explanation: Loads the transform-flash transition Niagara system.
47 if (DefaultFlashEffect.Succeeded())
48 {
49 TransformFlashEffect = DefaultFlashEffect.Object;
50 }
Explanation: If the load succeeded, assign it to TransformFlashEffect (True branch).
51
52 // 加载默认加速特效:NS_JiaSu(变身后按右键加速时播放一次)
53 static ConstructorHelpers::FObjectFinder<UNiagaraSystem> DefaultBoostEffect(TEXT("/Game/Test_02/VFX/FeiDunLight/NS/NS_JiaSu"));
Explanation: Loads the boost-effect Niagara system.
54 if (DefaultBoostEffect.Succeeded())
55 {
56 BoostEffect = DefaultBoostEffect.Object;
57 }
Explanation: If the load succeeded, assign it to BoostEffect (True branch).
58
59 // 配置移动组件
60 UCharacterMovementComponent* Movement = GetCharacterMovement();
Explanation: Retrieves the character's movement component.
61 if (Movement)
62 {
63 Movement->bOrientRotationToMovement = true;
Explanation: Orients the character toward the movement direction.
64 Movement->RotationRate = FRotator(0.0f, 540.0f, 0.0f);
Explanation: Sets the rotation rate to 540 degrees per second (Yaw axis only).
65 }
66
67 bUseControllerRotationPitch = false;
68 bUseControllerRotationYaw = false;
69 bUseControllerRotationRoll = false;
Explanation: Disables the character's use of controller rotation to avoid conflicts with custom flight control.
70 }
71
72 void TransformBase::BeginPlay()
73 {
74 Super::BeginPlay();
Explanation: Calls the parent class's 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"));
Explanation: If the Blueprint CDO left TransformVFXBlueprint empty, reload it at runtime.
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 }
Explanation: Force-sets the speed values in BeginPlay(), overriding values that may have been serialized to 0 due to the Blueprint CDO.
92
93 void TransformBase::PossessedBy(AController* NewController)
94 {
95 Super::PossessedBy(NewController);
96 }
Explanation: Overrides PossessedBy; the current implementation is empty.
97
98 void TransformBase::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
99 {
100 Super::SetupPlayerInputComponent(PlayerInputComponent);
Explanation: Calls the parent class's input bindings.
101
102 if (PlayerInputComponent)
103 {
104 PlayerInputComponent->BindKey(EKeys::T, IE_Pressed, this, &TransformBase::OnTransformInput);
Explanation: Binds the press event of the T key to OnTransformInput.
105 PlayerInputComponent->BindKey(EKeys::RightMouseButton, IE_Pressed, this, &TransformBase::OnBoostInput);
Explanation: Binds the right mouse button press event to OnBoostInput.
106 GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, TEXT("[Transform] T / RMB bound"));
107 }
108 }
Explanation: Prints a debug message on screen confirming the input bindings succeeded.
109
110 void TransformBase::Tick(float DeltaTime)
111 {
112 Super::Tick(DeltaTime);
Explanation: Calls the parent Tick, then runs the custom logic.
113
114 // ========== 溶解动画(进入变身/退出变身的过渡效果)==========
115 if (bDissolveActive)
116 {
117 DissolveElapsed += DeltaTime;
Explanation: If the dissolve animation is active, accumulate the elapsed time.
118 float Progress = FMath::Clamp(DissolveElapsed / TransformFlashDelay, 0.0f, 1.0f);
Explanation: Computes the dissolve progress, clamped between 0 and 1.
119 float DissolveValue = FMath::Lerp(DissolveStart, DissolveTarget, Progress);
Explanation: Lerps between the start and target values to get the current dissolve value.
120
121 // 写入 MPC,驱动材质溶解
122 if (DissolveMPC)
123 {
124 UMaterialParameterCollectionInstance* MPCInstance = GetWorld()->GetParameterCollectionInstance(DissolveMPC);
Explanation: If the MPC is configured, get its world instance.
125 if (MPCInstance)
126 {
127 MPCInstance->SetScalarParameterValue(DissolveParamName, DissolveValue);
128 }
129 }
Explanation: Writes the dissolve value into the MPC's scalar parameter.
130
131 if (Progress >= 1.0f)
132 {
133 bDissolveActive = false;
134 }
135 }
Explanation: Dissolve complete; clear the flag.
136
137 // ========== 变身飞行系统 ==========
138 if (!bIsInTransformedForm)
139 {
140 return;
141 }
Explanation: If not in a transformed form, skip the flight logic.
142
143 // 获取移动组件
144 UCharacterMovementComponent* MoveComp = GetCharacterMovement();
145 if (!MoveComp)
146 {
147 return;
148 }
149
150 // 清除蓝图 WASD 移动输入(否则按 S 会同时触发"后退",与飞行方向控制冲突)
151 MoveComp->ConsumeInputVector();
Explanation: Retrieves the movement component and clears the existing input vectors to avoid conflicts with Blueprint input.
152
153 // 获取玩家视角方向
154 APlayerController* PC = Cast<APlayerController>(GetController());
155 if (!PC)
156 {
157 return;
158 }
Explanation: Retrieves the player controller and casts it to APlayerController, for reading the view direction and key state.
159 const FRotator ControlRot = PC->GetControlRotation();
160 const FVector ViewForward = FRotationMatrix(ControlRot).GetUnitAxis(EAxis::X);
161 const FVector ViewRight = FRotationMatrix(ControlRot).GetUnitAxis(EAxis::Y);
Explanation: Retrieves the controller rotation and computes the view forward vector and right vector.
162
163 const float AutoFlySpeed = bIsBoosting ? BoostFlySpeed : NormalFlySpeed;
164 const float SteerSpeed = bIsBoosting ? BoostSteerSpeed : NormalSteerSpeed;
Explanation: Selects the corresponding speed based on whether boosting is active.
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 }
Explanation: Prints the current speed and boost state to the screen.
171
172 // ========== 直接计算目标速度并设置 Velocity(绕过 MaxAcceleration 限制)==========
173 FVector DesiredVelocity = ViewForward * AutoFlySpeed;
Explanation: Computes the base target velocity from the view forward vector and the auto-flight speed.
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();
Explanation: If W is held, add an upward component to the target velocity; if S is held, prepare a ground check.
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);
Explanation: Retrieves the capsule half-height and computes the feet position and the ray end point.
190
191 FHitResult Hit;
192 FCollisionQueryParams Params;
193 Params.AddIgnoredActor(this);
194
195 bool bNearGround = GetWorld()->LineTraceSingleByChannel(Hit, FeetPos, TraceEnd, ECC_Visibility, Params);
Explanation: Ray-traces downward from the feet to determine whether the character is near the ground.
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 }
Explanation: Based on the A/D keys, adds left/right components to the target velocity.
214
215 // 平滑过渡到目标速度(避免瞬间突变,加速/减速都有手感)
216 const float SpeedLerpRate = 8.0f;
217 FVector NewVelocity = FMath::VInterpTo(MoveComp->Velocity, DesiredVelocity, DeltaTime, SpeedLerpRate);
218 MoveComp->Velocity = NewVelocity;
Explanation: Uses VInterpTo to smoothly transition the current velocity to the target at a rate of 8.0, then assigns it to the movement component.
219
220 // 提高上限,防止 CharacterMovement 把速度压回去
221 MoveComp->MaxFlySpeed = FMath::Max(MoveComp->MaxFlySpeed, AutoFlySpeed * 1.2f);
222 MoveComp->MaxAcceleration = FMath::Max(MoveComp->MaxAcceleration, 10000.0f);
223 }
Explanation: Ensures MaxFlySpeed and MaxAcceleration are large enough so CharacterMovement does not limit the speed.
224
225 // ========== 将角色世界速度传给 Niagara User 参数 ==========
226 if (SpawnedTransformVFX)
227 {
228 TArray<UNiagaraComponent*> NiagaraComps;
229 SpawnedTransformVFX->GetComponents<UNiagaraComponent>(NiagaraComps);
Explanation: If the transform-effect Actor exists, retrieve all of its Niagara components.
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 }
Explanation: Iterates over the Niagara components, passing the character's world velocity into the user parameter named User.FlyVelocity.
240
241 // ========== 形态切换 ==========
242
243 void TransformBase::OnTransformInput()
244 {
245 GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Cyan, TEXT("[Transform] FIRED!"));
Explanation: Defines the transform input handler function, which first prints the trigger info on screen.
246
247 if (bIsTransforming)
248 {
249 return;
250 }
Explanation: If in the middle of a transform flow (cooldown), return immediately.
251
252 bIsTransforming = true;
253
254 if (bIsInTransformedForm)
255 {
256 // 退出变身:立刻标记为非变身状态,停止 Tick 中的飞行逻辑
257 bIsInTransformedForm = false;
Explanation: If currently in a transformed form, run the exit-transform logic. First mark as non-transformed immediately to stop the flight 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 }
Explanation: Stops movement immediately: zeroes velocity, clears input, stops movement, switches to walking mode.
268
269 // 退出变身:先播闪光 + 销毁特效 + 显示角色网格 + 溶解出现
270 if (SpawnedTransformVFX)
271 {
272 SpawnedTransformVFX->Destroy();
273 SpawnedTransformVFX = nullptr;
274 }
Explanation: Destroys the transform-effect Actor.
275
276 // 显示网格(溶解会从 1→0 让角色逐渐出现)
277 USkeletalMeshComponent* BodyMesh = GetMesh();
278 if (BodyMesh)
279 {
280 BodyMesh->SetVisibility(true);
281 }
Explanation: Shows the character body mesh.
282
283 StartDissolveIn();
Explanation: Starts the reverse dissolve animation from 1 to 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 }
Explanation: If the flash effect is configured, spawn a one-shot flash effect at the character location.
298
299 // 延迟 0.4s 后正式退出变身
300 GetWorldTimerManager().SetTimer(
301 TransformFlashTimer,
302 this,
303 &TransformBase::ExecuteExitTransform,
304 TransformFlashDelay,
305 false
306 );
307 }
Explanation: Sets a 0.4 s delay timer; when it fires, call ExecuteExitTransform().
308 else
309 {
310 // 进入变身:先播闪光透特 + 溶解消失,延迟后才正式变身
311 GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Orange, TEXT("[Transform] Playing flash VFX..."));
Explanation: If not currently in a transformed form, run the enter-transform logic.
312
313 StartDissolveOut();
Explanation: Starts the dissolve-out animation from 0 to 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 }
Explanation: Spawns the flash effect at the character location. true, true means auto-destroy and auto-activate.
328
329 // 延迟 0.4s 后正式变身
330 GetWorldTimerManager().SetTimer(
331 TransformFlashTimer,
332 this,
333 &TransformBase::ExecuteEnterTransform,
334 TransformFlashDelay,
335 false
336 );
337 }
338 }
Explanation: Sets a 0.4 s delay timer; when it fires, call ExecuteEnterTransform().
339
340 void TransformBase::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")));
Explanation: Defines the right-mouse-button boost input handler function, which prints whether the character is currently in a transformed form.
345
346 // 只有变身状态下才能切换加速
347 if (!bIsInTransformedForm)
348 {
349 return;
350 }
Explanation: If not in a transformed form, ignore the right-mouse boost.
351
352 bIsBoosting = !bIsBoosting;
Explanation: Toggles the boost state.
353
354 // 同步修改 CharacterMovementComponent 的最大飞行速度上限
355 if (UCharacterMovementComponent* MoveComp = GetCharacterMovement())
356 {
357 MoveComp->MaxFlySpeed = bIsBoosting ? BoostFlySpeed : NormalFlySpeed;
358 }
Explanation: Adjusts MaxFlySpeed in sync with the boost state.
359
360 // 只在进入加速时播放一次特效,退出加速不播放
361 if (bIsBoosting)
362 {
363 if (BoostEffect)
364 {
365 FVector SpawnLoc = GetActorLocation() + FVector(0, 0, 50);
Explanation: If entering boost and a boost effect is configured, compute the spawn location (50 units above the character location).
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 }
Explanation: Spawns the boost effect and prints debug info; if the effect is empty, prints an error.
382
383 GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Yellow,
384 bIsBoosting ? TEXT("[Transform] Boost ON") : TEXT("[Transform] Boost OFF"));
385 }
Explanation: Prints the current boost state.
386
387 void TransformBase::ExecuteEnterTransform()
388 {
389 bIsInTransformedForm = true;
Explanation: Defines the formal enter-transform function (after the flash ends), marking as a transformed form.
390
391 // 隐藏角色网格
392 USkeletalMeshComponent* BodyMesh = GetMesh();
393 if (BodyMesh)
394 {
395 BodyMesh->SetVisibility(false);
396 }
Explanation: Hides the character body mesh.
397
398 // 生成变身特效蓝图 Actor
399 if (TransformVFXBlueprint && !SpawnedTransformVFX)
400 {
401 FActorSpawnParameters SpawnParams;
402 SpawnParams.Owner = this;
Explanation: If the transform-effect Blueprint is configured and not yet spawned, prepare the spawn parameters and set the Owner to the current character.
403 SpawnedTransformVFX = GetWorld()->SpawnActor<AActor>(TransformVFXBlueprint, FVector::ZeroVector, FRotator::ZeroRotator, SpawnParams);
404 if (SpawnedTransformVFX)
405 {
406 SpawnedTransformVFX->AttachToActor(this, FAttachmentTransformRules::KeepRelativeTransform);
Explanation: Spawns the effect Actor at the world origin and attaches it to the character to keep relative transforms.
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 }
Explanation: Sets the relative position to the origin and disables the projectile movement component on the effect 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 }
Explanation: Retrieves all Niagara components under the effect Actor and forces them active.
425
426 // 切换飞行模式
427 UCharacterMovementComponent* MoveComp = GetCharacterMovement();
428 if (MoveComp)
429 {
430 MoveComp->SetMovementMode(MOVE_Flying);
431 MoveComp->MaxFlySpeed = NormalFlySpeed;
432 }
Explanation: Switches movement mode to flying and initializes max flight speed to the normal speed.
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 }
Explanation: Sets the FOV to 120 via the player camera manager.
440
441 // 广播事件
442 OnFormChanged.Broadcast(FormIndex, 1);
Explanation: Broadcasts the form-change event, with parameters being the old form index and the new form index.
443
444 // 冷却
445 GetWorldTimerManager().SetTimer(
446 TransformCooldownTimer,
447 [this]() { bIsTransforming = false; },
448 TransformCooldown,
449 false
450 );
451 }
Explanation: Sets the cooldown timer; when it fires, resets bIsTransforming.
452
453 void TransformBase::ExecuteExitTransform()
454 {
455 bIsInTransformedForm = false;
Explanation: Defines the formal exit-transform function (after the flash ends), marking as non-transformed.
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 }
Explanation: Switches to walking mode and stops movement immediately.
466
467 // 退出变身时重置加速状态
468 bIsBoosting = false;
Explanation: Resets the boost state when exiting transform.
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 }
Explanation: Restores the FOV to 80.
476
477 // 广播事件
478 OnFormChanged.Broadcast(FormIndex, 0);
Explanation: Broadcasts the form-change event.
479
480 // 冷却
481 GetWorldTimerManager().SetTimer(
482 TransformCooldownTimer,
483 [this]() { bIsTransforming = false; },
484 TransformCooldown,
485 false
486 );
487 }
Explanation: Sets the cooldown timer.
488
489 void TransformBase::StartDissolveOut()
490 {
491 DissolveStart = 0.0f;
492 DissolveTarget = 1.0f;
493 DissolveElapsed = 0.0f;
494 bDissolveActive = true;
495 }
Explanation: Starts dissolve-out: transitions from 0 to 1, resets elapsed time, and activates the dissolve flag.
496
497 void TransformBase::StartDissolveIn()
498 {
499 DissolveStart = 1.0f;
500 DissolveTarget = 0.0f;
501 DissolveElapsed = 0.0f;
502 bDissolveActive = true;
503 }
Explanation: Starts reverse dissolve-in: transitions from 1 to 0.
504
505 void TransformBase::Server_ExecuteTransform_Implementation(int32 NewFormIndex)
506 {
507 }
Explanation: Server-side RPC implementation; currently empty.
508
509 void TransformBase::TransformToNextForm()
510 {
511 OnTransformInput();
512 }
513
514 void TransformBase::TransformToForm(int32 Index)
515 {
516 OnTransformInput();
517 }
Explanation: Form-switch function implementations; both currently call OnTransformInput() directly.
518
519 TSubclassOf<ACharacter> TransformBase::GetCurrentFormClass() const
520 {
521 if (Forms.IsValidIndex(FormIndex))
522 {
523 return Forms[FormIndex];
524 }
525 return nullptr;
526 }
Explanation: Returns the current form class, or empty if the index is invalid.
527
528 void TransformBase::ExecuteTransform(int32 NewFormIndex)
529 {
530 }
Explanation: Transform-logic execution function; currently empty.
531
532 void TransformBase::Multicast_PlayTransformEffect_Implementation()
533 {
534 PlayTransformEffect();
535 }
536
537 void TransformBase::PlayTransformEffect()
538 {
539 // 不再动态生成,使用常驻组件 TransformNiagaraComponent
540 // 激活/停用逻辑已在 OnTransformInput() 中处理
541 }
Explanation: The multicast RPC implementation calls PlayTransformEffect(), which is deprecated; the effect-spawn logic has been moved into OnTransformInput().
3. Deprecated Code Inventory and Migration Notes
Class A: Safe to delete (no references at all, zero risk of deletion)
| Code location | Deprecated notes |
|---|---|
TransformEffect / TransformEffectDuration (.h VFX section) |
Pure dead properties, no reads or writes at all |
CameraBlendTime (.h camera section) |
Camera blend time, never used |
PossessedBy() empty implementation (.cpp) |
Empty override with no logic; no impact after removal |
#include "Camera/CameraComponent.h" (.cpp) |
FOV is actually set via PlayerCameraManager; this header is unused |
#include "Kismet/GameplayStatics.h" (.cpp) |
Unused include |
Server_ExecuteTransform empty implementation (.cpp) |
Multi-player RPC, not enabled in this single-player project |
ExecuteTransform(int32) empty implementation (.cpp) |
Unused empty function |
Multicast_PlayTransformEffect / PlayTransformEffect (.cpp) |
Deprecated; the effect logic has moved to OnTransformInput() |
Class B: Delete with caution (Blueprint references must be handled too)
| Code location | Deprecated notes | Check before deleting |
|---|---|---|
FormMeshComponent (Sphere mesh, .h + constructor) |
Deprecated; Niagara is actually used. But the BP_ThirdPersonCharacter Blueprint component list still has a FormMesh instance |
After deleting the C++ member, the Blueprint must be recompiled, otherwise the Blueprint retains a stale reference to the component |
Forms / FormIndex / TransformToNextForm() / TransformToForm() / GetCurrentFormClass() |
Multi-form system; currently only single form is used | Check whether any Blueprint calls these BlueprintCallable functions |
OnFormChanged event delegate |
Broadcast inside ExecuteEnter/ExitTransform |
Check whether any Blueprint binds this event; if so, handle it in sync |
IA_Transform / DefaultMappingContext / MappingContextPriority |
Enhanced Input properties; traditional BindKey is actually used |
Check whether any reference is set in the Blueprint property panel; clear it before deleting |
Class C: Not deletable (current features depend on them)
bIsTransforming/TransformCooldownTimer/TransformCooldown: anti-spam cooldownbIsInTransformedForm/bIsBoosting: form and boost state flags- The four flight speed properties + the forced assignments in
BeginPlay()(prevent Blueprint CDO override) TransformFlashEffect/TransformFlashDelay: flash transitionDissolveMPC/DissolveParamNameand the dissolve-related private members: dissolve effectTransformVFXBlueprint/SpawnedTransformVFX: transform-effect ActorOnBoostInput()and the right-mouse binding: boost feature
Migration notes to the formal project (CultivatingImmortality_RPG)
-
Module API macro:
TEST_APImust be replaced with the formal project's module name macro (e.g.CULTIVATINGIMMORTALITY_RPG_API); class names are recommended to be renamed to the formal project style as well. -
Hard-coded asset paths: the
FObjectFinderpaths in the constructor (/Game/Test_02/...) are test-project paths. When migrating, either change them to the formal project paths or keep the UPROPERTY and configure it in Blueprints (the latter is recommended, more flexible). -
Module dependencies: the
.Build.csmust include the Niagara-related modules (Niagara,NiagaraCore,NiagaraShader, etc.); add them to the formal project's.Build.cswhen migrating. -
Transform-effect Actor:
BP_Projectile_Iceand its referenced Niagara assets (NS_StarSkill002, NS_Dash, NS_JiaSu, NS_SpriteSpawn, etc.) must be copied to the formal project together, keeping relative paths consistent. -
Input binding conflicts: the test project uses traditional
BindKey. If the formal project uses Enhanced Input and theTkey / right mouse button are already taken, adjust the key bindings or switch to Enhanced Input binding. -
Character materials: the dissolve effect requires adding a
CollectionParameter→OneMinus→OpacityMasknode chain in the formal project's character material, and creating the corresponding MPC asset. -
Blueprint CDO override pitfall: in the formal project, if you changed flight speed etc. in the Blueprint property panel, it overrides the C++ default values (this project once hit that issue: speed serialized to 0). Keeping the fallback assignment in
BeginPlay()avoids it. -
FOV and speeds are test values: the 120/80 FOV and the 1200/800/5000/4000 speeds are tuning results from the test project; re-tune to feel in the formal project.
-
Compile mode: modifying
.h(adding/removing UPROPERTY, UFUNCTION) requires a full recompile with the editor closed; changing only the.cppfunction body can use Live Coding.