claude-code-arcane 2.2.0 → 2.4.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.
- package/CHANGELOG.md +37 -0
- package/README.md +104 -55
- package/agents/career/career-strategist.md +88 -0
- package/agents/career/cv-reviewer.md +81 -0
- package/agents/career/hiring-manager.md +68 -0
- package/agents/career/mock-interviewer.md +76 -0
- package/agents/freelance/client-screener.md +84 -0
- package/agents/freelance/discovery-call.md +65 -0
- package/agents/freelance/pipeline-strategist.md +99 -0
- package/agents/freelance/proposal-reviewer.md +82 -0
- package/agents/game/ue-blueprint-specialist.md +125 -0
- package/agents/game/ue-gas-specialist.md +133 -0
- package/agents/game/ue-umg-specialist.md +131 -0
- package/dist/cli.js +57 -13
- package/docs/RELEASE-SETUP.md +1 -1
- package/docs/SKILLS-CATALOG.md +67 -4
- package/docs/USER-GUIDE.md +77 -72
- package/docs/agent-hierarchy.md +21 -1
- package/docs/presentations/build_arcane_deck.py +2 -2
- package/docs/presentations/build_arcane_deck_12.py +2 -2
- package/hooks/detect-unreal.sh +90 -0
- package/package.json +2 -2
- package/profiles/freelance.yaml +43 -0
- package/profiles/job-hunt.yaml +5 -2
- package/profiles/unreal-dev.yaml +97 -0
- package/rules/drafter-reviewer.md +16 -3
- package/rules/freelance-guardrails.md +53 -0
- package/rules/gamedev/unreal-code.md +125 -0
- package/skills/career-registry/SKILL.md +95 -0
- package/skills/career-registry/scripts/career_registry.py +488 -0
- package/skills/client-screen/SKILL.md +83 -0
- package/skills/client-screen/references/red-flags.md +95 -0
- package/skills/cover-letter/SKILL.md +4 -3
- package/skills/cv-tailor/SKILL.md +11 -2
- package/skills/freelance-deliver/SKILL.md +90 -0
- package/skills/freelance-hunt/SKILL.md +126 -0
- package/skills/freelance-hunt/references/templates/Connects.md +39 -0
- package/skills/freelance-hunt/references/templates/Contrato.md +60 -0
- package/skills/freelance-hunt/references/templates/Propuesta.md +66 -0
- package/skills/freelance-kickoff/SKILL.md +94 -0
- package/skills/freelance-pipeline/SKILL.md +109 -0
- package/skills/freelance-pipeline/scripts/import_history.py +425 -0
- package/skills/freelance-profile/SKILL.md +93 -0
- package/skills/freelance-proposal/SKILL.md +112 -0
- package/skills/freelance-scan/SKILL.md +196 -0
- package/skills/freelance-scan/references/dedup.md +35 -0
- package/skills/freelance-scan/references/platforms.md +167 -0
- package/skills/freelance-scan/scripts/freelance_search.py +1077 -0
- package/skills/install-mcp/SKILL.md +135 -46
- package/skills/install-mcp/references/unreal-operations.md +55 -0
- package/skills/interview-prep/SKILL.md +11 -3
- package/skills/job-aplicar/SKILL.md +5 -1
- package/skills/job-hunt/SKILL.md +17 -0
- package/skills/job-outcome/SKILL.md +15 -1
- package/skills/job-upskill/SKILL.md +10 -2
- package/skills/ue-actor-component-architecture/SKILL.md +509 -0
- package/skills/ue-actor-component-architecture/references/actor-lifecycle.md +301 -0
- package/skills/ue-actor-component-architecture/references/component-types.md +592 -0
- package/skills/ue-ai-navigation/SKILL.md +526 -0
- package/skills/ue-ai-navigation/references/behavior-tree-patterns.md +392 -0
- package/skills/ue-ai-navigation/references/behavior-tree-workflow.md +107 -0
- package/skills/ue-ai-navigation/references/custom-bttask.md +104 -0
- package/skills/ue-ai-navigation/references/eqs-reference.md +423 -0
- package/skills/ue-animation-system/SKILL.md +521 -0
- package/skills/ue-animation-system/references/anim-notify-reference.md +486 -0
- package/skills/ue-animation-system/references/locomotion-setup.md +550 -0
- package/skills/ue-async-threading/SKILL.md +433 -0
- package/skills/ue-async-threading/references/thread-safety-guide.md +213 -0
- package/skills/ue-async-threading/references/threading-patterns.md +345 -0
- package/skills/ue-audio-system/SKILL.md +527 -0
- package/skills/ue-audio-system/references/audio-setup-patterns.md +700 -0
- package/skills/ue-blueprints/SKILL.md +134 -0
- package/skills/ue-blueprints/references/communication.md +82 -0
- package/skills/ue-character-movement/SKILL.md +475 -0
- package/skills/ue-character-movement/references/cmc-extension-patterns.md +335 -0
- package/skills/ue-character-movement/references/movement-pipeline.md +189 -0
- package/skills/ue-cpp-foundations/SKILL.md +533 -0
- package/skills/ue-cpp-foundations/references/components-and-gc.md +107 -0
- package/skills/ue-cpp-foundations/references/container-patterns.md +544 -0
- package/skills/ue-cpp-foundations/references/delegate-patterns.md +497 -0
- package/skills/ue-cpp-foundations/references/gameplay-classes-cpp.md +136 -0
- package/skills/ue-cpp-foundations/references/property-specifiers.md +306 -0
- package/skills/ue-data-assets-tables/SKILL.md +527 -0
- package/skills/ue-data-assets-tables/references/asset-loading-patterns.md +306 -0
- package/skills/ue-data-assets-tables/references/data-driven-design.md +378 -0
- package/skills/ue-editor-tools/SKILL.md +523 -0
- package/skills/ue-editor-tools/references/detail-customization-patterns.md +395 -0
- package/skills/ue-editor-tools/references/editor-module-setup.md +480 -0
- package/skills/ue-game-features/SKILL.md +477 -0
- package/skills/ue-game-features/references/experience-system.md +222 -0
- package/skills/ue-game-features/references/game-feature-patterns.md +308 -0
- package/skills/ue-gameplay-abilities/SKILL.md +518 -0
- package/skills/ue-gameplay-abilities/references/ability-task-reference.md +391 -0
- package/skills/ue-gameplay-abilities/references/gameplay-effect-reference.md +397 -0
- package/skills/ue-gameplay-abilities/references/gas-setup-patterns.md +369 -0
- package/skills/ue-gameplay-framework/SKILL.md +466 -0
- package/skills/ue-gameplay-framework/references/framework-class-map.md +243 -0
- package/skills/ue-input-system/SKILL.md +394 -0
- package/skills/ue-input-system/references/cpp-setup.md +131 -0
- package/skills/ue-input-system/references/enhanced-input-workflow.md +117 -0
- package/skills/ue-input-system/references/input-action-reference.md +731 -0
- package/skills/ue-mass-entity/SKILL.md +476 -0
- package/skills/ue-mass-entity/references/mass-entity-patterns.md +319 -0
- package/skills/ue-mass-entity/references/mass-fragment-reference.md +119 -0
- package/skills/ue-materials-rendering/SKILL.md +526 -0
- package/skills/ue-materials-rendering/references/material-parameter-reference.md +216 -0
- package/skills/ue-materials-rendering/references/post-process-settings.md +383 -0
- package/skills/ue-module-build-system/SKILL.md +487 -0
- package/skills/ue-module-build-system/references/build-cs-reference.md +418 -0
- package/skills/ue-module-build-system/references/common-build-errors.md +381 -0
- package/skills/ue-naming-conventions/SKILL.md +142 -0
- package/skills/ue-naming-conventions/references/asset-name-modifiers.md +233 -0
- package/skills/ue-naming-conventions/references/content-structure.md +83 -0
- package/skills/ue-networking-replication/SKILL.md +452 -0
- package/skills/ue-networking-replication/references/replication-patterns.md +526 -0
- package/skills/ue-networking-replication/references/rpc-decision-guide.md +393 -0
- package/skills/ue-niagara-effects/SKILL.md +531 -0
- package/skills/ue-niagara-effects/references/niagara-data-interfaces.md +491 -0
- package/skills/ue-niagara-effects/references/niagara-parameter-types.md +153 -0
- package/skills/ue-niagara-effects/references/niagara-workflow.md +115 -0
- package/skills/ue-packaging/SKILL.md +116 -0
- package/skills/ue-physics-collision/SKILL.md +423 -0
- package/skills/ue-physics-collision/references/collision-channel-setup.md +218 -0
- package/skills/ue-physics-collision/references/trace-patterns.md +499 -0
- package/skills/ue-procedural-generation/SKILL.md +505 -0
- package/skills/ue-procedural-generation/references/pcg-node-reference.md +381 -0
- package/skills/ue-procedural-generation/references/procedural-mesh-patterns.md +940 -0
- package/skills/ue-project-context/SKILL.md +305 -0
- package/skills/ue-sequencer-cinematics/SKILL.md +506 -0
- package/skills/ue-sequencer-cinematics/references/sequencer-patterns.md +629 -0
- package/skills/ue-serialization-savegames/SKILL.md +524 -0
- package/skills/ue-serialization-savegames/references/save-system-architecture.md +705 -0
- package/skills/ue-state-trees/SKILL.md +489 -0
- package/skills/ue-state-trees/references/state-tree-mass-integration.md +184 -0
- package/skills/ue-state-trees/references/state-tree-patterns.md +321 -0
- package/skills/ue-testing-debugging/SKILL.md +520 -0
- package/skills/ue-testing-debugging/references/automation-test-patterns.md +387 -0
- package/skills/ue-testing-debugging/references/profiling-commands.md +357 -0
- package/skills/ue-ui-umg-slate/SKILL.md +431 -0
- package/skills/ue-ui-umg-slate/references/common-ui-setup.md +484 -0
- package/skills/ue-ui-umg-slate/references/widget-types.md +487 -0
- package/skills/ue-world-level-streaming/SKILL.md +518 -0
- package/skills/ue-world-level-streaming/references/streaming-patterns.md +414 -0
- 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
|