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.
- package/CHANGELOG.md +23 -0
- package/README.md +131 -41
- 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 +642 -478
- package/docs/SKILLS-CATALOG.md +42 -3
- package/docs/USER-GUIDE.md +28 -54
- package/hooks/detect-unreal.sh +90 -0
- package/package.json +2 -1
- package/profiles/agile.yaml +1 -1
- package/profiles/ai.yaml +1 -1
- package/profiles/android-native.yaml +1 -1
- package/profiles/audio.yaml +1 -1
- package/profiles/backend-dotnet.yaml +1 -1
- package/profiles/backend-go.yaml +1 -1
- package/profiles/backend-nestjs.yaml +1 -1
- package/profiles/backend-nextjs.yaml +1 -1
- package/profiles/backend-ts.yaml +1 -1
- package/profiles/business.yaml +1 -1
- package/profiles/clevel.yaml +1 -1
- package/profiles/clickup.yaml +1 -1
- package/profiles/core.yaml +1 -1
- package/profiles/database.yaml +1 -1
- package/profiles/design.yaml +1 -1
- package/profiles/docs.yaml +1 -1
- package/profiles/ecommerce.yaml +1 -1
- package/profiles/finance.yaml +1 -1
- package/profiles/flutter.yaml +1 -1
- package/profiles/freelance.yaml +1 -1
- package/profiles/frontend.yaml +1 -1
- package/profiles/infra.yaml +1 -1
- package/profiles/integrations.yaml +1 -1
- package/profiles/ios-native.yaml +1 -1
- package/profiles/jira.yaml +1 -1
- package/profiles/job-hunt.yaml +1 -1
- package/profiles/marketing.yaml +1 -1
- package/profiles/mobile.yaml +1 -1
- package/profiles/regulatory.yaml +1 -1
- package/profiles/second-brain.yaml +1 -1
- package/profiles/security.yaml +1 -1
- package/profiles/self-improving.yaml +1 -1
- package/profiles/statusline.yaml +1 -1
- package/profiles/testing.yaml +1 -1
- package/profiles/unity-design.yaml +1 -1
- package/profiles/unity-dev.yaml +1 -1
- package/profiles/unreal-dev.yaml +97 -0
- package/profiles/visual-novel.yaml +1 -1
- package/rules/gamedev/unreal-code.md +125 -0
- package/skills/arcane-list/SKILL.md +12 -7
- package/skills/install-mcp/SKILL.md +135 -46
- package/skills/install-mcp/references/unreal-operations.md +55 -0
- 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,550 @@
|
|
|
1
|
+
# Locomotion Setup Reference
|
|
2
|
+
|
|
3
|
+
This guide covers the end-to-end setup for character locomotion using UE5's
|
|
4
|
+
animation system: AnimInstance properties, blend spaces, state machines, and
|
|
5
|
+
layered blending.
|
|
6
|
+
|
|
7
|
+
Source headers referenced:
|
|
8
|
+
- `Engine/Source/Runtime/Engine/Classes/Animation/AnimInstance.h`
|
|
9
|
+
- `Engine/Source/Runtime/Engine/Classes/Animation/BlendSpace.h`
|
|
10
|
+
- `Engine/Source/Runtime/Engine/Classes/Animation/BlendSpace1D.h`
|
|
11
|
+
- `Engine/Source/Runtime/Engine/Classes/Animation/AimOffsetBlendSpace.h`
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## AnimInstance: Locomotion Properties
|
|
16
|
+
|
|
17
|
+
```cpp
|
|
18
|
+
// LocomotionAnimInstance.h
|
|
19
|
+
#pragma once
|
|
20
|
+
#include "Animation/AnimInstance.h"
|
|
21
|
+
#include "LocomotionAnimInstance.generated.h"
|
|
22
|
+
|
|
23
|
+
UCLASS()
|
|
24
|
+
class MYGAME_API ULocomotionAnimInstance : public UAnimInstance
|
|
25
|
+
{
|
|
26
|
+
GENERATED_BODY()
|
|
27
|
+
|
|
28
|
+
public:
|
|
29
|
+
virtual void NativeInitializeAnimation() override;
|
|
30
|
+
virtual void NativeUpdateAnimation(float DeltaSeconds) override;
|
|
31
|
+
|
|
32
|
+
protected:
|
|
33
|
+
// Cached references (game thread only)
|
|
34
|
+
UPROPERTY(Transient)
|
|
35
|
+
TObjectPtr<ACharacter> OwningCharacter;
|
|
36
|
+
|
|
37
|
+
UPROPERTY(Transient)
|
|
38
|
+
TObjectPtr<UCharacterMovementComponent> MovementComp;
|
|
39
|
+
|
|
40
|
+
// --- Properties read by the AnimGraph ---
|
|
41
|
+
|
|
42
|
+
// Ground speed (XY plane), drives blend space Y axis
|
|
43
|
+
UPROPERTY(Transient, BlueprintReadOnly, Category="Locomotion")
|
|
44
|
+
float GroundSpeed = 0.f;
|
|
45
|
+
|
|
46
|
+
// Movement direction relative to actor facing (-180 to 180), drives X axis
|
|
47
|
+
UPROPERTY(Transient, BlueprintReadOnly, Category="Locomotion")
|
|
48
|
+
float Direction = 0.f;
|
|
49
|
+
|
|
50
|
+
// True when CharacterMovement reports IsFalling()
|
|
51
|
+
UPROPERTY(Transient, BlueprintReadOnly, Category="Locomotion")
|
|
52
|
+
bool bIsInAir = false;
|
|
53
|
+
|
|
54
|
+
// True when velocity is near zero
|
|
55
|
+
UPROPERTY(Transient, BlueprintReadOnly, Category="Locomotion")
|
|
56
|
+
bool bIsIdle = true;
|
|
57
|
+
|
|
58
|
+
// True when crouching
|
|
59
|
+
UPROPERTY(Transient, BlueprintReadOnly, Category="Locomotion")
|
|
60
|
+
bool bIsCrouching = false;
|
|
61
|
+
|
|
62
|
+
// Acceleration magnitude, for start/stop transitions
|
|
63
|
+
UPROPERTY(Transient, BlueprintReadOnly, Category="Locomotion")
|
|
64
|
+
float Acceleration = 0.f;
|
|
65
|
+
|
|
66
|
+
// For aim offset
|
|
67
|
+
UPROPERTY(Transient, BlueprintReadOnly, Category="AimOffset")
|
|
68
|
+
float AimYaw = 0.f;
|
|
69
|
+
|
|
70
|
+
UPROPERTY(Transient, BlueprintReadOnly, Category="AimOffset")
|
|
71
|
+
float AimPitch = 0.f;
|
|
72
|
+
|
|
73
|
+
// Lean amount for banking in turns
|
|
74
|
+
UPROPERTY(Transient, BlueprintReadOnly, Category="Locomotion")
|
|
75
|
+
float LeanAmount = 0.f;
|
|
76
|
+
|
|
77
|
+
private:
|
|
78
|
+
// Previous yaw for lean calculation
|
|
79
|
+
float PreviousActorYaw = 0.f;
|
|
80
|
+
float LeanVelocity = 0.f;
|
|
81
|
+
};
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
```cpp
|
|
85
|
+
// LocomotionAnimInstance.cpp
|
|
86
|
+
#include "LocomotionAnimInstance.h"
|
|
87
|
+
#include "GameFramework/Character.h"
|
|
88
|
+
#include "GameFramework/CharacterMovementComponent.h"
|
|
89
|
+
#include "Kismet/KismetMathLibrary.h"
|
|
90
|
+
|
|
91
|
+
static constexpr float IdleSpeedThreshold = 10.f;
|
|
92
|
+
static constexpr float LeanInterpSpeed = 4.f;
|
|
93
|
+
static constexpr float LeanMaxAngle = 30.f;
|
|
94
|
+
|
|
95
|
+
void ULocomotionAnimInstance::NativeInitializeAnimation()
|
|
96
|
+
{
|
|
97
|
+
Super::NativeInitializeAnimation();
|
|
98
|
+
|
|
99
|
+
OwningCharacter = Cast<ACharacter>(TryGetPawnOwner());
|
|
100
|
+
if (OwningCharacter)
|
|
101
|
+
{
|
|
102
|
+
MovementComp = OwningCharacter->GetCharacterMovement();
|
|
103
|
+
PreviousActorYaw = OwningCharacter->GetActorRotation().Yaw;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
void ULocomotionAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
|
|
108
|
+
{
|
|
109
|
+
Super::NativeUpdateAnimation(DeltaSeconds);
|
|
110
|
+
|
|
111
|
+
if (!OwningCharacter || !MovementComp || DeltaSeconds <= 0.f) return;
|
|
112
|
+
|
|
113
|
+
const FVector Velocity = MovementComp->Velocity;
|
|
114
|
+
GroundSpeed = Velocity.Size2D();
|
|
115
|
+
bIsInAir = MovementComp->IsFalling();
|
|
116
|
+
bIsCrouching = MovementComp->IsCrouching();
|
|
117
|
+
bIsIdle = GroundSpeed < IdleSpeedThreshold && !bIsInAir;
|
|
118
|
+
Acceleration = MovementComp->GetCurrentAcceleration().Size2D();
|
|
119
|
+
|
|
120
|
+
// Direction: angle between velocity and actor facing
|
|
121
|
+
if (GroundSpeed > IdleSpeedThreshold)
|
|
122
|
+
{
|
|
123
|
+
const FRotator ActorRot = OwningCharacter->GetActorRotation();
|
|
124
|
+
const FRotator VelocityRot = Velocity.ToOrientationRotator();
|
|
125
|
+
Direction = UKismetMathLibrary::NormalizedDeltaRotator(
|
|
126
|
+
VelocityRot, ActorRot).Yaw;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Aim offset (yaw/pitch delta from actor to control rotation)
|
|
130
|
+
const FRotator AimRot = OwningCharacter->GetBaseAimRotation();
|
|
131
|
+
const FRotator ActorRot = OwningCharacter->GetActorRotation();
|
|
132
|
+
const FRotator AimDelta = UKismetMathLibrary::NormalizedDeltaRotator(
|
|
133
|
+
AimRot, ActorRot);
|
|
134
|
+
AimYaw = FMath::Clamp(AimDelta.Yaw, -90.f, 90.f);
|
|
135
|
+
AimPitch = FMath::Clamp(AimDelta.Pitch, -90.f, 90.f);
|
|
136
|
+
|
|
137
|
+
// Lean: angular velocity of actor yaw, smoothed with interp
|
|
138
|
+
const float CurrentYaw = OwningCharacter->GetActorRotation().Yaw;
|
|
139
|
+
const float YawDeltaRate = UKismetMathLibrary::NormalizedDeltaRotator(
|
|
140
|
+
FRotator(0.f, CurrentYaw, 0.f),
|
|
141
|
+
FRotator(0.f, PreviousActorYaw, 0.f)).Yaw / DeltaSeconds;
|
|
142
|
+
PreviousActorYaw = CurrentYaw;
|
|
143
|
+
|
|
144
|
+
const float TargetLean = FMath::Clamp(YawDeltaRate / LeanMaxAngle, -1.f, 1.f);
|
|
145
|
+
LeanAmount = FMath::FInterpTo(LeanAmount, TargetLean,
|
|
146
|
+
DeltaSeconds, LeanInterpSpeed);
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Blend Space Setup
|
|
153
|
+
|
|
154
|
+
### 1D Blend Space — Walk/Run
|
|
155
|
+
|
|
156
|
+
**Asset**: `BS_GroundLocomotion1D` (type: `BlendSpace1D`)
|
|
157
|
+
|
|
158
|
+
| Axis | Name | Min | Max | Grid Points |
|
|
159
|
+
|-------|---------|-----|-----|-------------|
|
|
160
|
+
| X | Speed | 0 | 600 | 3 |
|
|
161
|
+
|
|
162
|
+
Samples:
|
|
163
|
+
| Speed | Animation |
|
|
164
|
+
|-------|-----------------|
|
|
165
|
+
| 0 | Idle |
|
|
166
|
+
| 200 | Walk_Fwd |
|
|
167
|
+
| 600 | Run_Fwd |
|
|
168
|
+
|
|
169
|
+
Interpolation (from `FInterpolationParameter`):
|
|
170
|
+
```
|
|
171
|
+
InterpolationTime = 0.15
|
|
172
|
+
InterpolationType = SpringDamper
|
|
173
|
+
DampingRatio = 1.0
|
|
174
|
+
MaxSpeed = 0.0 (unclamped)
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
In the AnimGraph, a **Blend Space 1D Player** node:
|
|
178
|
+
- Asset: `BS_GroundLocomotion1D`
|
|
179
|
+
- X pin: bound to `GroundSpeed` from the AnimInstance
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
### 2D Blend Space — Directional Locomotion
|
|
184
|
+
|
|
185
|
+
**Asset**: `BS_GroundLocomotion2D` (type: `BlendSpace`)
|
|
186
|
+
|
|
187
|
+
| Axis | Name | Min | Max | Grid Points |
|
|
188
|
+
|------|-----------|------|-----|-------------|
|
|
189
|
+
| X | Direction | -180 | 180 | 5 |
|
|
190
|
+
| Y | Speed | 0 | 600 | 3 |
|
|
191
|
+
|
|
192
|
+
Sample layout (Direction x Speed):
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
Direction\Speed | 0 (Idle) | 200 (Walk) | 600 (Run)
|
|
196
|
+
----------------+-----------+------------+-----------
|
|
197
|
+
0 (Fwd) | Idle | Walk_Fwd | Run_Fwd
|
|
198
|
+
90 (Right) | Idle | Walk_Right | Run_Right
|
|
199
|
+
-90 (Left) | Idle | Walk_Left | Run_Left
|
|
200
|
+
180 (Bwd) | Idle | Walk_Bwd | Run_Bwd
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Idle is the same animation at all direction values and Speed=0; it is defined
|
|
204
|
+
at four direction points so the blend falls back to it cleanly.
|
|
205
|
+
|
|
206
|
+
Interpolation per axis:
|
|
207
|
+
```
|
|
208
|
+
X (Direction): InterpolationTime=0.10, InterpolationType=SpringDamper
|
|
209
|
+
Y (Speed): InterpolationTime=0.15, InterpolationType=SpringDamper
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
In the AnimGraph, a **Blend Space Player** node:
|
|
213
|
+
- Asset: `BS_GroundLocomotion2D`
|
|
214
|
+
- X pin: `Direction`
|
|
215
|
+
- Y pin: `GroundSpeed`
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
### Aim Offset — Upper Body Aiming
|
|
220
|
+
|
|
221
|
+
**Asset**: `AO_Rifle` (type: `AimOffset`)
|
|
222
|
+
|
|
223
|
+
| Axis | Name | Min | Max |
|
|
224
|
+
|-------|-------|-----|-----|
|
|
225
|
+
| X | Yaw | -90 | 90 |
|
|
226
|
+
| Y | Pitch | -90 | 90 |
|
|
227
|
+
|
|
228
|
+
Samples (5 poses, all additive relative to reference pose):
|
|
229
|
+
|
|
230
|
+
| Yaw | Pitch | Animation |
|
|
231
|
+
|-----|-------|-----------------------|
|
|
232
|
+
| 0 | 0 | AO_Rifle_Center |
|
|
233
|
+
| 90 | 0 | AO_Rifle_Right |
|
|
234
|
+
| -90 | 0 | AO_Rifle_Left |
|
|
235
|
+
| 0 | 90 | AO_Rifle_Up |
|
|
236
|
+
| 0 | -90 | AO_Rifle_Down |
|
|
237
|
+
|
|
238
|
+
In the AnimGraph:
|
|
239
|
+
- Place **Aim Offset** node after the base locomotion result
|
|
240
|
+
- X pin: `AimYaw`, Y pin: `AimPitch`
|
|
241
|
+
- This node applies an additive pose on top of the base pose
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## State Machine Setup — Locomotion State Machine
|
|
246
|
+
|
|
247
|
+
**State Machine Name**: `LocomotionSM`
|
|
248
|
+
|
|
249
|
+
### States
|
|
250
|
+
|
|
251
|
+
```
|
|
252
|
+
[Entry] --> [Idle] --> [Walk/Run] --> [Idle]
|
|
253
|
+
\--> [Jump Start] --> [In Air] --> [Land] --> [Idle]
|
|
254
|
+
\--> [Crouch Idle] --> [Crouch Walk] --> [Crouch Idle]
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### State Definitions
|
|
258
|
+
|
|
259
|
+
**Idle**
|
|
260
|
+
- Animation: Idle loop sequence or the 1D/2D blend space at Speed=0
|
|
261
|
+
- Transition to Walk/Run: `GroundSpeed > 10 && !bIsInAir`
|
|
262
|
+
- Transition to Jump Start: `bIsInAir`
|
|
263
|
+
- Transition to Crouch Idle: `bIsCrouching`
|
|
264
|
+
|
|
265
|
+
**Walk/Run**
|
|
266
|
+
- Animation: `BS_GroundLocomotion1D` or `BS_GroundLocomotion2D`
|
|
267
|
+
- Transition to Idle: `GroundSpeed <= 10 && !bIsInAir`
|
|
268
|
+
- Transition to Jump Start: `bIsInAir`
|
|
269
|
+
|
|
270
|
+
**Jump Start**
|
|
271
|
+
- Animation: `JumpStart` (plays once)
|
|
272
|
+
- Automatic transition to In Air when animation finishes:
|
|
273
|
+
`TimeRemaining(ratio) < 0.1`
|
|
274
|
+
|
|
275
|
+
**In Air**
|
|
276
|
+
- Animation: `InAir` loop (often a blend between apex and falling poses)
|
|
277
|
+
- Transition to Land: `!bIsInAir`
|
|
278
|
+
|
|
279
|
+
**Land**
|
|
280
|
+
- Animation: `Land` (plays once)
|
|
281
|
+
- Automatic transition to Idle or Walk/Run when complete
|
|
282
|
+
|
|
283
|
+
**Crouch Idle**
|
|
284
|
+
- Animation: `CrouchIdle` loop
|
|
285
|
+
- Transition to Crouch Walk: `GroundSpeed > 3`
|
|
286
|
+
- Transition to Idle (stand): `!bIsCrouching`
|
|
287
|
+
|
|
288
|
+
**Crouch Walk**
|
|
289
|
+
- Animation: `BS_CrouchLocomotion1D` (separate blend space for crouch walk)
|
|
290
|
+
- Transition to Crouch Idle: `GroundSpeed <= 3`
|
|
291
|
+
- Transition to Idle: `!bIsCrouching`
|
|
292
|
+
|
|
293
|
+
### Transition Cross-Fade Settings
|
|
294
|
+
|
|
295
|
+
| From | To | Duration | Blend Curve |
|
|
296
|
+
|-------------|-------------|----------|--------------|
|
|
297
|
+
| Idle | Walk/Run | 0.15s | Cubic In-Out |
|
|
298
|
+
| Walk/Run | Idle | 0.20s | Cubic In-Out |
|
|
299
|
+
| Walk/Run | Jump Start | 0.10s | Linear |
|
|
300
|
+
| In Air | Land | 0.05s | Linear |
|
|
301
|
+
| Land | Idle | 0.20s | Cubic In-Out |
|
|
302
|
+
| Idle | Crouch Idle | 0.15s | Linear |
|
|
303
|
+
| Crouch Idle | Idle | 0.15s | Linear |
|
|
304
|
+
|
|
305
|
+
Use **Automatic Rule Based on Sequence Player** for states with a single
|
|
306
|
+
play-once animation (Jump Start, Land) to auto-transition when the animation
|
|
307
|
+
nears completion.
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## AnimGraph Structure
|
|
312
|
+
|
|
313
|
+
```
|
|
314
|
+
[Output Pose]
|
|
315
|
+
|
|
|
316
|
+
[Layered Blend Per Bone] <-- upper body slot for montages
|
|
317
|
+
| |
|
|
318
|
+
[LocomotionSM] [Slot 'UpperBody']
|
|
319
|
+
|
|
|
320
|
+
[Aim Offset] <-- aim yaw/pitch additive
|
|
321
|
+
|
|
|
322
|
+
[Locomotion Blend Space Player]
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### Full AnimGraph Node Order (Bottom to Top)
|
|
326
|
+
|
|
327
|
+
1. **Blend Space Player** (`BS_GroundLocomotion2D`) — base movement pose
|
|
328
|
+
2. **Aim Offset** (`AO_Rifle`) — additive aim layer on top of base
|
|
329
|
+
3. **State Machine** (`LocomotionSM`) — wraps steps 1 & 2 inside states
|
|
330
|
+
4. **Slot 'UpperBody'** — montage poses blend into upper body here
|
|
331
|
+
5. **Layered Blend Per Bone** — blends upper body slot over the state machine
|
|
332
|
+
- Blend Poses 0: State Machine output (lower body + full)
|
|
333
|
+
- Blend Poses 1: UpperBody slot
|
|
334
|
+
- Layer Setup: `spine_01`, Depth=0, BlendDepth=1, MeshPoseBlendFactor=1
|
|
335
|
+
6. **Output Pose**
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## Layered Blend Per Bone — Upper/Lower Split
|
|
340
|
+
|
|
341
|
+
To play attack montages on the upper body while keeping lower body locomotion:
|
|
342
|
+
|
|
343
|
+
In the AnimGraph:
|
|
344
|
+
1. Add a **Slot** node named `UpperBody` (create the slot in Skeleton settings).
|
|
345
|
+
2. Add a **Layered Blend Per Bone** node.
|
|
346
|
+
3. Connect the State Machine output to **Base Pose**.
|
|
347
|
+
4. Connect the `UpperBody` slot output to **Blend Poses 0**.
|
|
348
|
+
5. In the **Layer Setup** array, add entry:
|
|
349
|
+
- Bone Name: `spine_01`
|
|
350
|
+
- Blend Depth: `-1` (blend from the specified bone downward to root is
|
|
351
|
+
unaffected; upward is fully blended)
|
|
352
|
+
- Mesh Pose Blend Factor: 1.0
|
|
353
|
+
- Inverse: unchecked (positive bones = upper body = get slot blend)
|
|
354
|
+
|
|
355
|
+
The montage must target the `UpperBody` slot. In C++:
|
|
356
|
+
|
|
357
|
+
```cpp
|
|
358
|
+
// Play on UpperBody slot only
|
|
359
|
+
AnimInst->Montage_Play(AttackMontage);
|
|
360
|
+
// AttackMontage must have its anim tracks assigned to the 'UpperBody' slot
|
|
361
|
+
// in the montage editor (SlotAnimTracks[0].SlotName = "UpperBody")
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
## Native State Machine Bindings
|
|
367
|
+
|
|
368
|
+
Bind C++ logic to state transitions without Blueprint:
|
|
369
|
+
|
|
370
|
+
```cpp
|
|
371
|
+
// In ULocomotionAnimInstance::NativeInitializeAnimation()
|
|
372
|
+
|
|
373
|
+
// Transition rule
|
|
374
|
+
AddNativeTransitionBinding(
|
|
375
|
+
FName("LocomotionSM"),
|
|
376
|
+
FName("Idle"),
|
|
377
|
+
FName("Walk/Run"),
|
|
378
|
+
FCanTakeTransition::CreateUObject(
|
|
379
|
+
this, &ULocomotionAnimInstance::CanStartMoving),
|
|
380
|
+
FName("IdleToMoving")
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
// State entry notification
|
|
384
|
+
AddNativeStateEntryBinding(
|
|
385
|
+
FName("LocomotionSM"),
|
|
386
|
+
FName("Land"),
|
|
387
|
+
FOnGraphStateChanged::CreateUObject(
|
|
388
|
+
this, &ULocomotionAnimInstance::OnLandStateEntered)
|
|
389
|
+
);
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
```cpp
|
|
393
|
+
bool ULocomotionAnimInstance::CanStartMoving() const
|
|
394
|
+
{
|
|
395
|
+
return GroundSpeed > IdleSpeedThreshold && !bIsInAir;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
void ULocomotionAnimInstance::OnLandStateEntered(
|
|
399
|
+
const FAnimNode_StateMachine& SM,
|
|
400
|
+
int32 PrevState,
|
|
401
|
+
int32 NextState)
|
|
402
|
+
{
|
|
403
|
+
// Play landing camera shake, rumble, etc.
|
|
404
|
+
if (OwningCharacter)
|
|
405
|
+
{
|
|
406
|
+
// UGameplayStatics::PlayWorldCameraShake(...);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
## Modular Locomotion via Linked Anim Layers
|
|
414
|
+
|
|
415
|
+
For projects with multiple locomotion modes (ground, swimming, climbing,
|
|
416
|
+
zero-gravity), use the linked anim layer pattern instead of a monolithic state
|
|
417
|
+
machine.
|
|
418
|
+
|
|
419
|
+
### Interface Definition
|
|
420
|
+
|
|
421
|
+
Create `ULocomotionLayerInterface` (AnimLayerInterface Blueprint asset)
|
|
422
|
+
with a single layer function: `LocomotionLayer`.
|
|
423
|
+
|
|
424
|
+
### Main AnimInstance
|
|
425
|
+
|
|
426
|
+
The main `ABP_Character` has a **Linked Anim Layer** node targeting
|
|
427
|
+
`ULocomotionLayerInterface::LocomotionLayer`. Its output feeds directly into
|
|
428
|
+
the **Output Pose** (after any additive overlays).
|
|
429
|
+
|
|
430
|
+
### Layer Implementations
|
|
431
|
+
|
|
432
|
+
```cpp
|
|
433
|
+
// GroundLocomotionLayer.h — implements LocomotionLayerInterface
|
|
434
|
+
UCLASS()
|
|
435
|
+
class MYGAME_API UGroundLocomotionLayer : public UAnimInstance
|
|
436
|
+
{
|
|
437
|
+
GENERATED_BODY()
|
|
438
|
+
// ...same locomotion properties as above...
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
// ClimbingLocomotionLayer.h
|
|
442
|
+
UCLASS()
|
|
443
|
+
class MYGAME_API UClimbingLocomotionLayer : public UAnimInstance
|
|
444
|
+
{
|
|
445
|
+
GENERATED_BODY()
|
|
446
|
+
// Separate state machine / sequences for climbing
|
|
447
|
+
};
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
### Swapping at Runtime
|
|
451
|
+
|
|
452
|
+
```cpp
|
|
453
|
+
void AMyCharacter::EnterClimbing()
|
|
454
|
+
{
|
|
455
|
+
if (UAnimInstance* AnimInst = GetMesh()->GetAnimInstance())
|
|
456
|
+
{
|
|
457
|
+
AnimInst->LinkAnimClassLayers(UClimbingLocomotionLayer::StaticClass());
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
void AMyCharacter::ExitClimbing()
|
|
462
|
+
{
|
|
463
|
+
if (UAnimInstance* AnimInst = GetMesh()->GetAnimInstance())
|
|
464
|
+
{
|
|
465
|
+
AnimInst->LinkAnimClassLayers(UGroundLocomotionLayer::StaticClass());
|
|
466
|
+
// Or pass nullptr to reset all layers to defaults:
|
|
467
|
+
// AnimInst->LinkAnimClassLayers(nullptr);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
The transition is automatically inertially blended when an **Inertialization**
|
|
473
|
+
node is placed in the main AnimGraph.
|
|
474
|
+
|
|
475
|
+
---
|
|
476
|
+
|
|
477
|
+
## Inertial Blending for Smooth Layer Transitions
|
|
478
|
+
|
|
479
|
+
Add an **Inertialization** node in the main AnimGraph between the linked layer
|
|
480
|
+
output and the Output Pose. When `LinkAnimClassLayers` is called, the system
|
|
481
|
+
will inertialize the transition using the blend duration configured on the
|
|
482
|
+
`FAnimNode_LinkedAnimGraph` node (`PendingBlendInDuration` /
|
|
483
|
+
`PendingBlendOutDuration`).
|
|
484
|
+
|
|
485
|
+
To request inertialization from C++:
|
|
486
|
+
|
|
487
|
+
```cpp
|
|
488
|
+
// Request a 0.2s inertialized blend for all slots in the "DefaultGroup"
|
|
489
|
+
AnimInst->RequestSlotGroupInertialization(FName("DefaultGroup"), 0.2f);
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
---
|
|
493
|
+
|
|
494
|
+
## Root Motion Configuration
|
|
495
|
+
|
|
496
|
+
| Mode | Description |
|
|
497
|
+
|-----------------------------|----------------------------------------------------------|
|
|
498
|
+
| `NoRootMotionExtraction` | Root bone moves normally; no extraction to component |
|
|
499
|
+
| `IgnoreRootMotion` | Root motion is extracted and discarded (root stays put) |
|
|
500
|
+
| `RootMotionFromMontagesOnly`| Only montage root motion drives component movement |
|
|
501
|
+
| `RootMotionFromEverything` | All animations contribute root motion to component |
|
|
502
|
+
|
|
503
|
+
For typical third-person games with CharacterMovementComponent:
|
|
504
|
+
```cpp
|
|
505
|
+
// In Character BP defaults or constructor:
|
|
506
|
+
GetMesh()->GetAnimInstance()->RootMotionMode =
|
|
507
|
+
ERootMotionMode::RootMotionFromMontagesOnly;
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
In the `AnimInstance` class default:
|
|
511
|
+
```cpp
|
|
512
|
+
// Set in class constructor or CDO:
|
|
513
|
+
RootMotionMode = ERootMotionMode::RootMotionFromMontagesOnly;
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
For multiplayer, pair `RootMotionFromMontagesOnly` with GAS montage replication
|
|
517
|
+
so the server's `CharacterMovementComponent` processes root motion authoritatively.
|
|
518
|
+
|
|
519
|
+
---
|
|
520
|
+
|
|
521
|
+
## Common Locomotion Pitfalls
|
|
522
|
+
|
|
523
|
+
**Direction flips when speed is near zero**
|
|
524
|
+
- Only update `Direction` when `GroundSpeed > threshold`. Keep the last valid
|
|
525
|
+
direction when the character is decelerating to idle, preventing the blend
|
|
526
|
+
space from snapping.
|
|
527
|
+
|
|
528
|
+
**Blend space snapping on direction change**
|
|
529
|
+
- Use `SpringDamper` interpolation on the Direction axis with
|
|
530
|
+
`InterpolationTime = 0.08–0.12`. Too low = snappy, too high = sluggish turns.
|
|
531
|
+
|
|
532
|
+
**Aim offset fighting the state machine**
|
|
533
|
+
- Ensure the Aim Offset node is placed inside the locomotion state (not outside
|
|
534
|
+
the state machine), or apply it as an additive overlay after the state machine
|
|
535
|
+
output, so it blends correctly during transitions.
|
|
536
|
+
|
|
537
|
+
**Crouch/stand transition pops**
|
|
538
|
+
- Ensure crouch and stand blend spaces share samples at Speed=0. The blend space
|
|
539
|
+
sampler interpolates between them; if the idle samples differ significantly,
|
|
540
|
+
use a short cross-fade on the Idle->Crouch Idle transition (0.15s+).
|
|
541
|
+
|
|
542
|
+
**Upper body slot not blending**
|
|
543
|
+
- Verify the montage's slot track is named exactly `UpperBody` (case-sensitive)
|
|
544
|
+
and the Layered Blend Per Bone node references the same slot name. Mismatches
|
|
545
|
+
result in the montage playing on the wrong slot or not blending at all.
|
|
546
|
+
|
|
547
|
+
**State machine transition not firing from C++**
|
|
548
|
+
- `AddNativeTransitionBinding` must be called in `NativeInitializeAnimation`,
|
|
549
|
+
not in `BeginPlay` or later. The binding is registered against the compiled
|
|
550
|
+
state machine before the AnimGraph evaluates.
|