Pressing any WASD direction key + LeftShift triggers the aerial dash: the character teleports 5000 units along the camera direction, with a dissolve effect, a radial blur screen effect, and four-directional dodge montages. The Blueprint version lives in the CharacterBlueprint InputEventGraph; the C++ version lives in CharacterBase::DoAirDash.
— System summary
1. Execution Flow: Blueprint vs C++ Comparison
The two versions follow the same logic chain; the C++ version replaces the Blueprint Timeline-driven flow with in-Tick per-frame driving.
[输入] WASD + LeftShift(空中态)
│
├─ [守卫] 闪现中不重复触发(AirDashRemaining > 0)
├─ [守卫] 打断当前普攻(IsAttacking → InterruptAttack / ResetAttackM)
│
├─ [方向] W→前 / S→后 / A→左 / D→右
│ GetDashDirection() → Controller.GetControlRotation().Vector()(含 Pitch,不压平 Z)
│ GetDashRightVector() → Controller.GetControlRotation() + Yaw+90°
│
├─ [安全落点] CalculateSafeDashLocation(Dir, DashLerpDistance=5000)
│ 胶囊体 Sweep:碰到 StaticMesh/Landscape 墙 → 回退到最近阻挡点
│ 植被放行:IsVegetationHit 判断关键词,树/草不挡
│
├─ [特效 1] SpawnSystemAttached(DashDissolveFX) → 角色根组件
├─ [特效 2] ApplyAirDashScreenEffect() → EF_PP_RadialBlur_Dodge 挂相机 WeightedBlendables
├─ [特效 3] FlashCamLag_Disable() → SpringArm CameraLagSpeed = 9999
├─ [特效 4] WriteRadialBlurMPC(0.8f) → MPC_RadialBlur BlurIntensity 峰值
├─ [溶解] StartDissolveOut() → 角色溶解消失
│
├─ [位移] VLerp(DashLerpStart, DashLerpTarget, α) 0.3s 插值
│ α = curTime / DashLerpDuration(0.3)
│ SetActorLocation(Sweep=true)
│
└─ [动画] PlayMontage(PickDashMontageByInput(), 1.0f)
角色_Dodge_Montage / Dodge_Back / Dodge_Left / Dodge_Right
2. Clip-Through Fixes: 5 Iterations from V1 to V5
Clip-through on dash is a critical issue for this feature, fixed over 5 revisions. The root causes are concentrated in CalculateSafeDashLocation: collision channel selection, Sweep result ordering, and Z-axis handling.
V1
Blueprint-only
Implemented entirely in Blueprint, no C++ safety calculation — direct teleport, frequently clipping walls and trees
Initial version · severe clipping
V2
ECC_Visibility channel
C++ LineTraceMultiByChannel(ECC_Visibility) → missed StaticMeshActor walls
ECC_Visibility does not respond to many objects
V3
ECC_WorldStatic + vegetation pass-through
Switched the channel to WorldStatic and added keyword matching in IsVegetationHit ("Vegetation"/"Grass"/"Tree")
Walls are now blocked, but Z-axis handling is broken
V4
Landscape recognition fix
Class-name check "Landscape" → "Landscape" (Contains did not match, so the ground was not recognized)
Fix the class-name match condition
V5
bypass Sweep + Z clamp
Blueprint-side bSweep=false clips trees; C++ changes only the Z axis to the ground position, keeping XY consistent with the direction
Final decision
Key fix: C++ CalculateSafeDashLocation function body (V5 final)
// TransformCharacter.cpp · CalculateSafeDashLocation
FVector TransformBase::CalculateSafeDashLocation(const FVector& Dir, float MaxDist)
{
FVector Start = GetActorLocation();
FVector End = Start + Dir.GetSafeNormal() * MaxDist;
// 路径 Sweep(胶囊体)
FHitResult Hit;
FCollisionQueryParams Params(SCENE_QUERY_STAT(DashSafe), false, this);
Params.AddIgnoredActor(this);
GetWorld()->SweepSingleByChannel(Hit, Start, End, FQuat::Identity,
ECC_WorldStatic, FCollisionShape::MakeCapsule(CapsuleRadius, CapsuleHalfHeight), Params);
FVector SafeTarget = Hit.bBlockingHit ? Hit.Location : End;
// Z 轴单独钳位到地面(Landscape + WorldStatic)
FHitResult GroundHit;
GetWorld()->LineTraceSingleByChannel(GroundHit,
SafeTarget + FVector(0, 0, 100), SafeTarget + FVector(0, 0, -500),
ECC_WorldStatic, Params);
if (GroundHit.bBlockingHit) {
// 只改 Z,XY 保持原方向(修复"闪现方向与镜头不对齐")
SafeTarget.Z = GroundHit.Location.Z + CapsuleHalfHeight;
}
return SafeTarget;
}
Key fix: Blueprint-side bSweep set to false
Blueprint CF51 (SetActorLocation node) → bSweep = false, so Blueprint movement also clips trees (trees are blocked only at the C++ Sweep layer for walls, not for vegetation).
3. VFX List (Down to Asset Path + Attachment Point)
| Type | Asset | Path | Attachment/Injection Point | Timing |
|---|---|---|---|---|
| Niagara particles | NS_Dodge角色 | /Game/Test_02/VFX/FlashVFX/ | Character root component, SnapToTarget | Spawn immediately at dash start |
| Niagara trail | NS_FlyTrail | /Game/Effect/ | Blueprint Niagara component FlyParticle | Activate in hover flight state |
| PP radial blur | EF_PP_RadialBlur_Dodge | /Game/Effect/MaterialInstance/PostProcess/ | Camera WeightedBlendables weight 1.0 | Weight → 0 after 0.15 s, removed after 0.4 s |
| MPC radial | MPC_RadialBlur | Global MPC | BlurIntensity=0.8, WarpFlowIntensity=0.8 | Peak → decay following the Timeline |
| SpringArm | CameraLag | Built into the camera component | CameraLagSpeed=9999, RotationLagSpeed=9999 | Lag disabled throughout the dash, restored at the end |
4. Key Variable Quick Reference
| Variable | Type | Default | Notes |
|---|---|---|---|
| DashLerpDistance | float | 5000 | Maximum dash distance (units) |
| DashLerpDuration | float | 0.3 | Dash interpolation duration (s) |
| DashLerpStart | FVector | — | Dash start position |
| DashLerpTarget | FVector | — | Safe landing position |
| AirDashRemaining | float | 0 | Dash remaining time (>0 means in dash) |
| bIsAttacking | bool | false | Basic attack in progress (dash must interrupt it first) |
| DashDissolveFX | TObjectPtr<UNiagaraSystem> | Soft-path loaded | Dash dissolve effect asset |
| DashMontageForward/Back/Left/Right | UPROPERTY FSoftObjectPath | — | Four-directional dodge montages |
5. Issues and Fixes Quick Reference
- OnDashInput execute pin not wired: the then pin of the Blueprint Custom Event node was not wired, so CalculateSafeDashLocation never ran and SafeTarget stayed at the origin. Fix: wire the then pin to the TimelineDodge node.
- Blueprint SetActorLocation → bSweep: the Blueprint side defaulted bSweep=true, duplicating the C++ Sweep check and causing tree clipping to fail. Fix: set bSweep=false on the Blueprint side so the C++ layer centrally owns collision checks.
- StartDissolveOut timing: the dissolve-effect Niagara is attached to the root component; if the character is not yet in place and the Niagara Deactivates early, the character becomes visible early. Fix: call StartDissolveOut at the start of DoAirDash, and trigger StartDissolveIn at the end of Tick when DashLerpCurTime > 0.8 * Duration.
- EXCEPTION_ACCESS_VIOLATION: during exit, the Niagara components were destroyed before being Activated, causing a null pointer crash. Fix: run IsValid() checks before Destroy.
- Direction misaligned with the camera: in VLerp(Start, SafeTarget, α), if Sweep corrected the XY of SafeTarget, the dash direction drifted from the camera. Fix: use Sweep only for the Z-axis clamp, keeping XY along Dir.