claude-code-arcane 2.3.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +131 -41
  3. package/agents/game/ue-blueprint-specialist.md +125 -0
  4. package/agents/game/ue-gas-specialist.md +133 -0
  5. package/agents/game/ue-umg-specialist.md +131 -0
  6. package/dist/cli.js +642 -478
  7. package/docs/SKILLS-CATALOG.md +42 -3
  8. package/docs/USER-GUIDE.md +28 -54
  9. package/hooks/detect-unreal.sh +90 -0
  10. package/package.json +2 -1
  11. package/profiles/agile.yaml +1 -1
  12. package/profiles/ai.yaml +1 -1
  13. package/profiles/android-native.yaml +1 -1
  14. package/profiles/audio.yaml +1 -1
  15. package/profiles/backend-dotnet.yaml +1 -1
  16. package/profiles/backend-go.yaml +1 -1
  17. package/profiles/backend-nestjs.yaml +1 -1
  18. package/profiles/backend-nextjs.yaml +1 -1
  19. package/profiles/backend-ts.yaml +1 -1
  20. package/profiles/business.yaml +1 -1
  21. package/profiles/clevel.yaml +1 -1
  22. package/profiles/clickup.yaml +1 -1
  23. package/profiles/core.yaml +1 -1
  24. package/profiles/database.yaml +1 -1
  25. package/profiles/design.yaml +1 -1
  26. package/profiles/docs.yaml +1 -1
  27. package/profiles/ecommerce.yaml +1 -1
  28. package/profiles/finance.yaml +1 -1
  29. package/profiles/flutter.yaml +1 -1
  30. package/profiles/freelance.yaml +1 -1
  31. package/profiles/frontend.yaml +1 -1
  32. package/profiles/infra.yaml +1 -1
  33. package/profiles/integrations.yaml +1 -1
  34. package/profiles/ios-native.yaml +1 -1
  35. package/profiles/jira.yaml +1 -1
  36. package/profiles/job-hunt.yaml +1 -1
  37. package/profiles/marketing.yaml +1 -1
  38. package/profiles/mobile.yaml +1 -1
  39. package/profiles/regulatory.yaml +1 -1
  40. package/profiles/second-brain.yaml +1 -1
  41. package/profiles/security.yaml +1 -1
  42. package/profiles/self-improving.yaml +1 -1
  43. package/profiles/statusline.yaml +1 -1
  44. package/profiles/testing.yaml +1 -1
  45. package/profiles/unity-design.yaml +1 -1
  46. package/profiles/unity-dev.yaml +1 -1
  47. package/profiles/unreal-dev.yaml +97 -0
  48. package/profiles/visual-novel.yaml +1 -1
  49. package/rules/gamedev/unreal-code.md +125 -0
  50. package/skills/arcane-list/SKILL.md +12 -7
  51. package/skills/install-mcp/SKILL.md +135 -46
  52. package/skills/install-mcp/references/unreal-operations.md +55 -0
  53. package/skills/ue-actor-component-architecture/SKILL.md +509 -0
  54. package/skills/ue-actor-component-architecture/references/actor-lifecycle.md +301 -0
  55. package/skills/ue-actor-component-architecture/references/component-types.md +592 -0
  56. package/skills/ue-ai-navigation/SKILL.md +526 -0
  57. package/skills/ue-ai-navigation/references/behavior-tree-patterns.md +392 -0
  58. package/skills/ue-ai-navigation/references/behavior-tree-workflow.md +107 -0
  59. package/skills/ue-ai-navigation/references/custom-bttask.md +104 -0
  60. package/skills/ue-ai-navigation/references/eqs-reference.md +423 -0
  61. package/skills/ue-animation-system/SKILL.md +521 -0
  62. package/skills/ue-animation-system/references/anim-notify-reference.md +486 -0
  63. package/skills/ue-animation-system/references/locomotion-setup.md +550 -0
  64. package/skills/ue-async-threading/SKILL.md +433 -0
  65. package/skills/ue-async-threading/references/thread-safety-guide.md +213 -0
  66. package/skills/ue-async-threading/references/threading-patterns.md +345 -0
  67. package/skills/ue-audio-system/SKILL.md +527 -0
  68. package/skills/ue-audio-system/references/audio-setup-patterns.md +700 -0
  69. package/skills/ue-blueprints/SKILL.md +134 -0
  70. package/skills/ue-blueprints/references/communication.md +82 -0
  71. package/skills/ue-character-movement/SKILL.md +475 -0
  72. package/skills/ue-character-movement/references/cmc-extension-patterns.md +335 -0
  73. package/skills/ue-character-movement/references/movement-pipeline.md +189 -0
  74. package/skills/ue-cpp-foundations/SKILL.md +533 -0
  75. package/skills/ue-cpp-foundations/references/components-and-gc.md +107 -0
  76. package/skills/ue-cpp-foundations/references/container-patterns.md +544 -0
  77. package/skills/ue-cpp-foundations/references/delegate-patterns.md +497 -0
  78. package/skills/ue-cpp-foundations/references/gameplay-classes-cpp.md +136 -0
  79. package/skills/ue-cpp-foundations/references/property-specifiers.md +306 -0
  80. package/skills/ue-data-assets-tables/SKILL.md +527 -0
  81. package/skills/ue-data-assets-tables/references/asset-loading-patterns.md +306 -0
  82. package/skills/ue-data-assets-tables/references/data-driven-design.md +378 -0
  83. package/skills/ue-editor-tools/SKILL.md +523 -0
  84. package/skills/ue-editor-tools/references/detail-customization-patterns.md +395 -0
  85. package/skills/ue-editor-tools/references/editor-module-setup.md +480 -0
  86. package/skills/ue-game-features/SKILL.md +477 -0
  87. package/skills/ue-game-features/references/experience-system.md +222 -0
  88. package/skills/ue-game-features/references/game-feature-patterns.md +308 -0
  89. package/skills/ue-gameplay-abilities/SKILL.md +518 -0
  90. package/skills/ue-gameplay-abilities/references/ability-task-reference.md +391 -0
  91. package/skills/ue-gameplay-abilities/references/gameplay-effect-reference.md +397 -0
  92. package/skills/ue-gameplay-abilities/references/gas-setup-patterns.md +369 -0
  93. package/skills/ue-gameplay-framework/SKILL.md +466 -0
  94. package/skills/ue-gameplay-framework/references/framework-class-map.md +243 -0
  95. package/skills/ue-input-system/SKILL.md +394 -0
  96. package/skills/ue-input-system/references/cpp-setup.md +131 -0
  97. package/skills/ue-input-system/references/enhanced-input-workflow.md +117 -0
  98. package/skills/ue-input-system/references/input-action-reference.md +731 -0
  99. package/skills/ue-mass-entity/SKILL.md +476 -0
  100. package/skills/ue-mass-entity/references/mass-entity-patterns.md +319 -0
  101. package/skills/ue-mass-entity/references/mass-fragment-reference.md +119 -0
  102. package/skills/ue-materials-rendering/SKILL.md +526 -0
  103. package/skills/ue-materials-rendering/references/material-parameter-reference.md +216 -0
  104. package/skills/ue-materials-rendering/references/post-process-settings.md +383 -0
  105. package/skills/ue-module-build-system/SKILL.md +487 -0
  106. package/skills/ue-module-build-system/references/build-cs-reference.md +418 -0
  107. package/skills/ue-module-build-system/references/common-build-errors.md +381 -0
  108. package/skills/ue-naming-conventions/SKILL.md +142 -0
  109. package/skills/ue-naming-conventions/references/asset-name-modifiers.md +233 -0
  110. package/skills/ue-naming-conventions/references/content-structure.md +83 -0
  111. package/skills/ue-networking-replication/SKILL.md +452 -0
  112. package/skills/ue-networking-replication/references/replication-patterns.md +526 -0
  113. package/skills/ue-networking-replication/references/rpc-decision-guide.md +393 -0
  114. package/skills/ue-niagara-effects/SKILL.md +531 -0
  115. package/skills/ue-niagara-effects/references/niagara-data-interfaces.md +491 -0
  116. package/skills/ue-niagara-effects/references/niagara-parameter-types.md +153 -0
  117. package/skills/ue-niagara-effects/references/niagara-workflow.md +115 -0
  118. package/skills/ue-packaging/SKILL.md +116 -0
  119. package/skills/ue-physics-collision/SKILL.md +423 -0
  120. package/skills/ue-physics-collision/references/collision-channel-setup.md +218 -0
  121. package/skills/ue-physics-collision/references/trace-patterns.md +499 -0
  122. package/skills/ue-procedural-generation/SKILL.md +505 -0
  123. package/skills/ue-procedural-generation/references/pcg-node-reference.md +381 -0
  124. package/skills/ue-procedural-generation/references/procedural-mesh-patterns.md +940 -0
  125. package/skills/ue-project-context/SKILL.md +305 -0
  126. package/skills/ue-sequencer-cinematics/SKILL.md +506 -0
  127. package/skills/ue-sequencer-cinematics/references/sequencer-patterns.md +629 -0
  128. package/skills/ue-serialization-savegames/SKILL.md +524 -0
  129. package/skills/ue-serialization-savegames/references/save-system-architecture.md +705 -0
  130. package/skills/ue-state-trees/SKILL.md +489 -0
  131. package/skills/ue-state-trees/references/state-tree-mass-integration.md +184 -0
  132. package/skills/ue-state-trees/references/state-tree-patterns.md +321 -0
  133. package/skills/ue-testing-debugging/SKILL.md +520 -0
  134. package/skills/ue-testing-debugging/references/automation-test-patterns.md +387 -0
  135. package/skills/ue-testing-debugging/references/profiling-commands.md +357 -0
  136. package/skills/ue-ui-umg-slate/SKILL.md +431 -0
  137. package/skills/ue-ui-umg-slate/references/common-ui-setup.md +484 -0
  138. package/skills/ue-ui-umg-slate/references/widget-types.md +487 -0
  139. package/skills/ue-world-level-streaming/SKILL.md +518 -0
  140. package/skills/ue-world-level-streaming/references/streaming-patterns.md +414 -0
  141. package/templates/gamedev/unreal-project-context.md +107 -0
@@ -0,0 +1,335 @@
1
+ # CMC Extension Patterns
2
+
3
+ Complete code templates for extending `UCharacterMovementComponent` with custom movement modes, networked prediction, and custom move data.
4
+
5
+ ---
6
+
7
+ ## Custom CMC Subclass Header
8
+
9
+ ```cpp
10
+ // MyCMC.h
11
+ #pragma once
12
+ #include "GameFramework/CharacterMovementComponent.h"
13
+ #include "MyCMC.generated.h"
14
+
15
+ UENUM(BlueprintType)
16
+ enum class ECustomMovementMode : uint8
17
+ {
18
+ None = 0,
19
+ WallRun = 1,
20
+ Climb = 2,
21
+ Dash = 3
22
+ };
23
+
24
+ UCLASS()
25
+ class MYGAME_API UMyCMC : public UCharacterMovementComponent
26
+ {
27
+ GENERATED_BODY()
28
+
29
+ public:
30
+ UMyCMC();
31
+
32
+ // --- Custom movement state ---
33
+ UPROPERTY(Transient)
34
+ uint8 bWantsToWallRun : 1;
35
+
36
+ UPROPERTY(Transient)
37
+ FVector WallRunNormal;
38
+
39
+ // --- Overrides ---
40
+ virtual void PhysCustom(float deltaTime, int32 Iterations) override;
41
+ virtual void OnMovementModeChanged(EMovementMode PrevMode, uint8 PrevCustomMode) override;
42
+ virtual FNetworkPredictionData_Client* GetPredictionData_Client() const override;
43
+
44
+ protected:
45
+ void PhysWallRun(float deltaTime, int32 Iterations);
46
+ void PhysClimb(float deltaTime, int32 Iterations);
47
+ void PhysDash(float deltaTime, int32 Iterations);
48
+
49
+ bool CanWallRun() const;
50
+ void EnterWallRun(const FHitResult& WallHit);
51
+ void ExitWallRun();
52
+ };
53
+ ```
54
+
55
+ ---
56
+
57
+ ## FSavedMove_Character Subclass
58
+
59
+ ```cpp
60
+ // MySavedMove.h (can live in MyCMC.h)
61
+ class FMySavedMove : public FSavedMove_Character
62
+ {
63
+ public:
64
+ typedef FSavedMove_Character Super;
65
+
66
+ // Custom saved state
67
+ uint8 bSavedWantsToWallRun : 1;
68
+ FVector SavedWallRunNormal;
69
+
70
+ virtual void Clear() override
71
+ {
72
+ Super::Clear();
73
+ bSavedWantsToWallRun = 0;
74
+ SavedWallRunNormal = FVector::ZeroVector;
75
+ }
76
+
77
+ virtual void SetMoveFor(
78
+ ACharacter* C,
79
+ float InDeltaTime,
80
+ FVector const& NewAccel,
81
+ FNetworkPredictionData_Client_Character& ClientData) override
82
+ {
83
+ Super::SetMoveFor(C, InDeltaTime, NewAccel, ClientData);
84
+
85
+ // Capture custom state from CMC before the move executes
86
+ UMyCMC* CMC = Cast<UMyCMC>(C->GetCharacterMovement());
87
+ if (CMC)
88
+ {
89
+ bSavedWantsToWallRun = CMC->bWantsToWallRun;
90
+ SavedWallRunNormal = CMC->WallRunNormal;
91
+ }
92
+ }
93
+
94
+ virtual void PrepMoveFor(ACharacter* C) override
95
+ {
96
+ Super::PrepMoveFor(C);
97
+
98
+ // Restore custom state before replaying this move
99
+ UMyCMC* CMC = Cast<UMyCMC>(C->GetCharacterMovement());
100
+ if (CMC)
101
+ {
102
+ CMC->bWantsToWallRun = bSavedWantsToWallRun;
103
+ CMC->WallRunNormal = SavedWallRunNormal;
104
+ }
105
+ }
106
+
107
+ virtual uint8 GetCompressedFlags() const override
108
+ {
109
+ uint8 Result = Super::GetCompressedFlags();
110
+
111
+ if (bSavedWantsToWallRun)
112
+ {
113
+ Result |= FLAG_Custom_0; // 0x10
114
+ }
115
+
116
+ return Result;
117
+ }
118
+
119
+ virtual bool CanCombineWith(
120
+ const FSavedMovePtr& NewMove,
121
+ ACharacter* InCharacter,
122
+ float MaxDelta) const override
123
+ {
124
+ const FMySavedMove* Other = static_cast<const FMySavedMove*>(NewMove.Get());
125
+
126
+ if (bSavedWantsToWallRun != Other->bSavedWantsToWallRun)
127
+ {
128
+ return false;
129
+ }
130
+
131
+ if (bSavedWantsToWallRun && !SavedWallRunNormal.Equals(Other->SavedWallRunNormal, 0.1f))
132
+ {
133
+ return false;
134
+ }
135
+
136
+ return Super::CanCombineWith(NewMove, InCharacter, MaxDelta);
137
+ }
138
+ };
139
+ ```
140
+
141
+ ---
142
+
143
+ ## FNetworkPredictionData_Client_Character Subclass
144
+
145
+ ```cpp
146
+ class FMyNetworkPredictionData : public FNetworkPredictionData_Client_Character
147
+ {
148
+ public:
149
+ typedef FNetworkPredictionData_Client_Character Super;
150
+
151
+ FMyNetworkPredictionData(const UCharacterMovementComponent& ClientMovement)
152
+ : Super(ClientMovement) {}
153
+
154
+ virtual FSavedMovePtr AllocateNewMove() override
155
+ {
156
+ return FSavedMovePtr(new FMySavedMove());
157
+ }
158
+ };
159
+ ```
160
+
161
+ ---
162
+
163
+ ## GetPredictionData_Client Override (Lazy-Init Pattern)
164
+
165
+ Override on your CMC subclass. Uses `const_cast` because the base method is `const` but the prediction data is mutable state:
166
+
167
+ ```cpp
168
+ FNetworkPredictionData_Client* UMyCMC::GetPredictionData_Client() const
169
+ {
170
+ if (ClientPredictionData == nullptr)
171
+ {
172
+ UMyCMC* MutableThis = const_cast<UMyCMC*>(this);
173
+ MutableThis->ClientPredictionData = new FMyNetworkPredictionData(*this);
174
+ }
175
+
176
+ return ClientPredictionData;
177
+ }
178
+ ```
179
+
180
+ ---
181
+
182
+ ## Wall-Run PhysCustom Implementation
183
+
184
+ ```cpp
185
+ void UMyCMC::PhysCustom(float deltaTime, int32 Iterations)
186
+ {
187
+ Super::PhysCustom(deltaTime, Iterations);
188
+
189
+ switch (static_cast<ECustomMovementMode>(CustomMovementMode))
190
+ {
191
+ case ECustomMovementMode::WallRun:
192
+ PhysWallRun(deltaTime, Iterations);
193
+ break;
194
+ case ECustomMovementMode::Climb:
195
+ PhysClimb(deltaTime, Iterations);
196
+ break;
197
+ case ECustomMovementMode::Dash:
198
+ PhysDash(deltaTime, Iterations);
199
+ break;
200
+ default:
201
+ UE_LOG(LogTemp, Warning, TEXT("Unknown custom movement mode: %d"), CustomMovementMode);
202
+ SetMovementMode(MOVE_Falling);
203
+ break;
204
+ }
205
+ }
206
+
207
+ void UMyCMC::PhysWallRun(float deltaTime, int32 Iterations)
208
+ {
209
+ if (deltaTime < MIN_TICK_TIME) { return; }
210
+ if (!bWantsToWallRun || !CanWallRun())
211
+ {
212
+ ExitWallRun();
213
+ return;
214
+ }
215
+
216
+ // Wall-run direction: cross gravity up with wall normal
217
+ FVector RunDir = FVector::CrossProduct(WallRunNormal, FVector::UpVector);
218
+
219
+ // Match character's forward to determine left vs right wall
220
+ if (FVector::DotProduct(RunDir, UpdatedComponent->GetForwardVector()) < 0.f)
221
+ {
222
+ RunDir *= -1.f;
223
+ }
224
+
225
+ // Accelerate along wall
226
+ Velocity = RunDir * MaxCustomMovementSpeed;
227
+
228
+ // Apply slight gravity pull toward wall to maintain contact
229
+ Velocity += -WallRunNormal * 50.f;
230
+
231
+ // Move
232
+ FHitResult Hit;
233
+ SafeMoveUpdatedComponent(Velocity * deltaTime, UpdatedComponent->GetComponentQuat(),
234
+ true, Hit);
235
+
236
+ // Verify wall is still there
237
+ if (Hit.bBlockingHit)
238
+ {
239
+ WallRunNormal = Hit.Normal;
240
+ }
241
+ else
242
+ {
243
+ // Lost wall contact — check with a trace
244
+ FHitResult WallCheck;
245
+ FVector TraceEnd = UpdatedComponent->GetComponentLocation()
246
+ - WallRunNormal * 100.f;
247
+ bool bStillOnWall = GetWorld()->LineTraceSingleByChannel(
248
+ WallCheck,
249
+ UpdatedComponent->GetComponentLocation(),
250
+ TraceEnd,
251
+ ECC_Visibility);
252
+
253
+ if (!bStillOnWall)
254
+ {
255
+ ExitWallRun();
256
+ }
257
+ }
258
+ }
259
+
260
+ void UMyCMC::EnterWallRun(const FHitResult& WallHit)
261
+ {
262
+ WallRunNormal = WallHit.Normal;
263
+ bWantsToWallRun = true;
264
+ SetMovementMode(MOVE_Custom, static_cast<uint8>(ECustomMovementMode::WallRun));
265
+ }
266
+
267
+ void UMyCMC::ExitWallRun()
268
+ {
269
+ bWantsToWallRun = false;
270
+ WallRunNormal = FVector::ZeroVector;
271
+ SetMovementMode(MOVE_Falling);
272
+ }
273
+ ```
274
+
275
+ ---
276
+
277
+ ## Custom Network Move Data
278
+
279
+ For custom data that exceeds the four CompressedFlags bits, derive `FCharacterNetworkMoveData`:
280
+
281
+ ```cpp
282
+ struct FMyNetworkMoveData : public FCharacterNetworkMoveData
283
+ {
284
+ FVector CustomWallNormal = FVector::ZeroVector;
285
+ uint8 CustomMoveMode = 0;
286
+
287
+ virtual void ClientFillNetworkMoveData(
288
+ const FSavedMove_Character& ClientMove,
289
+ ENetworkMoveType MoveType) override
290
+ {
291
+ Super::ClientFillNetworkMoveData(ClientMove, MoveType);
292
+
293
+ const FMySavedMove& MyMove = static_cast<const FMySavedMove&>(ClientMove);
294
+ CustomWallNormal = MyMove.SavedWallRunNormal;
295
+ }
296
+
297
+ virtual bool Serialize(
298
+ UCharacterMovementComponent& CharacterMovement,
299
+ FArchive& Ar,
300
+ UPackageMap* PackageMap,
301
+ ENetworkMoveType MoveType) override
302
+ {
303
+ Super::Serialize(CharacterMovement, Ar, PackageMap, MoveType);
304
+
305
+ Ar << CustomWallNormal;
306
+ Ar << CustomMoveMode;
307
+
308
+ return !Ar.IsError();
309
+ }
310
+ };
311
+
312
+ struct FMyNetworkMoveDataContainer : public FCharacterNetworkMoveDataContainer
313
+ {
314
+ FMyNetworkMoveData CustomDefaultMoveData[3];
315
+
316
+ FMyNetworkMoveDataContainer()
317
+ {
318
+ NewMoveData = &CustomDefaultMoveData[0];
319
+ PendingMoveData = &CustomDefaultMoveData[1];
320
+ OldMoveData = &CustomDefaultMoveData[2];
321
+ }
322
+ };
323
+ ```
324
+
325
+ Register the container in the CMC constructor:
326
+
327
+ ```cpp
328
+ UMyCMC::UMyCMC()
329
+ {
330
+ static FMyNetworkMoveDataContainer MoveDataContainer;
331
+ SetNetworkMoveDataContainer(MoveDataContainer);
332
+ }
333
+ ```
334
+
335
+ **Important:** The container must be `static` or otherwise outlive the CMC. `SetNetworkMoveDataContainer` stores a pointer, not a copy.
@@ -0,0 +1,189 @@
1
+ # Movement Pipeline
2
+
3
+ Detailed breakdown of CMC's internal movement execution, floor detection, and root motion replication.
4
+
5
+ ---
6
+
7
+ ## Phys* Function Flow
8
+
9
+ ```
10
+ TickComponent(DeltaTime)
11
+ |
12
+ v
13
+ PerformMovement(DeltaTime) [main entry point, protected]
14
+ |
15
+ +-- ControlledCharacterMove() [applies input acceleration]
16
+ |
17
+ v
18
+ StartNewPhysics(DeltaTime, Iterations)
19
+ |
20
+ +-- switch (MovementMode)
21
+ | |
22
+ | +-- MOVE_Walking --> PhysWalking(dt, iter)
23
+ | +-- MOVE_NavWalking --> PhysNavWalking(dt, iter)
24
+ | +-- MOVE_Falling --> PhysFalling(dt, iter)
25
+ | +-- MOVE_Swimming --> PhysSwimming(dt, iter)
26
+ | +-- MOVE_Flying --> PhysFlying(dt, iter)
27
+ | +-- MOVE_Custom --> PhysCustom(dt, iter)
28
+ |
29
+ v
30
+ [Inside each Phys* function:]
31
+ CalcVelocity(dt, Friction, bFluid, BrakingDecel)
32
+ |
33
+ v
34
+ SafeMoveUpdatedComponent(Delta, Rotation, bSweep, Hit)
35
+ |
36
+ +-- MoveUpdatedComponent() [actual primitive move]
37
+ +-- ResolvePenetration() [if overlapping after move]
38
+ ```
39
+
40
+ Each `Phys*` function may iterate multiple times within a single tick (up to `MaxSimulationIterations`) when sub-stepping is required, for example when a sweep hit blocks movement partway through the delta.
41
+
42
+ ---
43
+
44
+ ## Floor Detection Chain
45
+
46
+ ```
47
+ FindFloor(CapsuleLocation, OutFloorResult, bCanUseCached, DownwardSweepResult)
48
+ |
49
+ v
50
+ ComputeFloorDist(CapsuleLocation, LineDistance, SweepDistance, OutResult, SweepRadius, DownwardSweepResult)
51
+ |
52
+ +-- 1. Capsule sweep downward (SweepDistance)
53
+ | - Uses capsule shape slightly smaller than actual (SweepRadius)
54
+ | - Tests bBlockingHit and surface angle vs WalkableFloorZ
55
+ |
56
+ +-- 2. Line trace downward (LineDistance)
57
+ | - From capsule center to below capsule bottom
58
+ | - Validates the sweep result at the exact contact point
59
+ | - Catches edges where the sweep capsule might miss
60
+ |
61
+ v
62
+ FFindFloorResult populated:
63
+ bBlockingHit = sweep found something
64
+ bWalkableFloor = surface normal Z >= WalkableFloorZ
65
+ bLineTrace = result came from line trace fallback
66
+ FloorDist = distance from capsule bottom to floor (sweep)
67
+ LineDist = distance from line trace
68
+ HitResult = full FHitResult
69
+ ```
70
+
71
+ `IsWalkableFloor()` returns `true` only when both `bBlockingHit` and `bWalkableFloor` are set. A blocking hit on a steep surface (above `WalkableFloorAngle`) returns `bBlockingHit = true` but `bWalkableFloor = false`.
72
+
73
+ ---
74
+
75
+ ## Step-Up Logic
76
+
77
+ When `PhysWalking` encounters a blocking hit during `SafeMoveUpdatedComponent`, it attempts a step-up if the obstacle height is within `MaxStepHeight`:
78
+
79
+ ```
80
+ 1. Hit blocking surface during horizontal move
81
+ 2. Check obstacle height <= MaxStepHeight
82
+ 3. Move capsule UP by MaxStepHeight
83
+ 4. Move capsule FORWARD by remaining Delta
84
+ 5. Move capsule DOWN to find new floor
85
+ 6. If new floor is walkable:
86
+ - Accept the step-up position
87
+ - Continue walking
88
+ 7. If no walkable floor found or obstacle too tall:
89
+ - Revert to pre-step-up position
90
+ - Slide along the wall or transition to falling
91
+ ```
92
+
93
+ Step-up only triggers during walking mode. Characters in `MOVE_Falling` do not step up -- they slide along surfaces.
94
+
95
+ ---
96
+
97
+ ## PhysWalking Flow
98
+
99
+ ```
100
+ PhysWalking(deltaTime, Iterations)
101
+ |
102
+ +-- CalcVelocity(dt, GroundFriction, false, BrakingDecelerationWalking)
103
+ |
104
+ +-- MoveAlongFloor(Velocity * dt, Hit)
105
+ | |
106
+ | +-- SafeMoveUpdatedComponent(Delta, Rotation, true, Hit)
107
+ | +-- If blocked: attempt SlideAlongSurface or StepUp
108
+ |
109
+ +-- FindFloor(NewLocation, FloorResult, false)
110
+ | |
111
+ | +-- If walkable floor found: maintain MOVE_Walking
112
+ | +-- If floor lost: SetMovementMode(MOVE_Falling)
113
+ | +-- If floor too steep: slide down or fall
114
+ |
115
+ +-- AdjustFloorHeight()
116
+ |
117
+ +-- Snap capsule to floor within tolerance
118
+ +-- Prevents floating or sinking between frames
119
+ ```
120
+
121
+ The `Iterations` parameter limits how many sub-steps occur if the character hits multiple surfaces in one frame. Each slide or step-up consumes an iteration.
122
+
123
+ ---
124
+
125
+ ## PhysFalling Flow
126
+
127
+ ```
128
+ PhysFalling(deltaTime, Iterations)
129
+ |
130
+ +-- GetFallingLateralAcceleration()
131
+ | +-- Applies AirControl to lateral input
132
+ | +-- Scaled by GetAirControl() (0-1 range)
133
+ |
134
+ +-- CalcVelocity(dt, FallingLateralFriction, false, BrakingDecelerationFalling)
135
+ |
136
+ +-- Apply gravity: Velocity.Z += GetGravityZ() * dt
137
+ |
138
+ +-- SafeMoveUpdatedComponent(Velocity * dt, Rotation, true, Hit)
139
+ | |
140
+ | +-- If hit floor:
141
+ | | +-- ProcessLanded(Hit)
142
+ | | +-- FindFloor check
143
+ | | +-- If walkable: SetMovementMode(MOVE_Walking)
144
+ | | +-- ACharacter::Landed(Hit) called
145
+ | |
146
+ | +-- If hit wall:
147
+ | +-- HandleImpact(Hit)
148
+ | +-- SlideAlongSurface(Delta, 1-Hit.Time, Hit.Normal, Hit)
149
+ |
150
+ +-- If no hit: continue falling next frame
151
+ ```
152
+
153
+ Air control: `AirControl` (0-1) scales the player's lateral input while falling. At 0, the character follows a pure ballistic arc. At 1, the character has full lateral control in air (feels unrealistic but common in platformers).
154
+
155
+ ---
156
+
157
+ ## Root Motion Replication
158
+
159
+ Root motion from `FRootMotionSource` replicates through the CMC's network prediction system.
160
+
161
+ ```
162
+ [Server]
163
+ ApplyRootMotionSource(Source)
164
+ |
165
+ +-- Source added to CurrentRootMotion.RootMotionSources
166
+ +-- Each tick: accumulated into FRootMotionMovementParams
167
+ +-- Applied during PerformMovement before physics
168
+ +-- Included in server move response
169
+
170
+ [Client - Autonomous Proxy]
171
+ Receives source via replication
172
+ |
173
+ +-- Applies locally for prediction
174
+ +-- Corrections handled through normal FSavedMove replay
175
+ +-- Source duration tracked independently
176
+
177
+ [Client - Simulated Proxy]
178
+ Receives replicated movement result
179
+ |
180
+ +-- Smoothing applied via NetworkSmoothingMode
181
+ +-- Root motion baked into final position/velocity
182
+ ```
183
+
184
+ Key points:
185
+ - Root motion sources with `Duration < 0` run infinitely until explicitly removed with `RemoveRootMotionSource(InstanceName)`
186
+ - `AccumulateMode::Override` replaces velocity from other sources at the same or lower priority
187
+ - `AccumulateMode::Additive` stacks on top of existing velocity
188
+ - Animation root motion (`FRootMotionMovementParams` from `UAnimMontage`) takes a separate path: it is extracted from `UAnimInstance` each tick in `TickComponent`, accumulated in `RootMotionParams`, then applied in `PerformMovement`. This path is distinct from `FRootMotionSource` and is handled by the animation system, not the source list.
189
+ - Both paths merge in `PerformMovement` where the final delta is applied