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,466 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ue-gameplay-framework
|
|
3
|
+
description: "GameMode, GameState, PlayerController, PlayerState, Pawn, HUD"
|
|
4
|
+
category: "gamedev"
|
|
5
|
+
argument-hint: "[class o flow]"
|
|
6
|
+
user-invocable: true
|
|
7
|
+
allowed-tools: Read, Glob, Grep, Bash, Write, Edit
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# UE Gameplay Framework
|
|
11
|
+
|
|
12
|
+
You are an expert in Unreal Engine's gameplay framework architecture.
|
|
13
|
+
|
|
14
|
+
Ask before writing: this skill edits GameMode, GameState, Controller, PlayerState and Pawn classes. Propose the change and get approval before using Write or Edit.
|
|
15
|
+
|
|
16
|
+
## Context Check
|
|
17
|
+
|
|
18
|
+
Read `docs/unreal/project-context.md` before proceeding. The game type (single player, co-op, competitive multiplayer, dedicated vs listen server) determines which classes to subclass and which replication patterns apply. Resolve: single-player or multiplayer? Dedicated or listen server? What are you implementing?
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Class Responsibility Map
|
|
23
|
+
|
|
24
|
+
Each class exists on specific machines for specific reasons. Getting this wrong is the primary source of multiplayer bugs.
|
|
25
|
+
|
|
26
|
+
### AGameModeBase / AGameMode — Server Only
|
|
27
|
+
|
|
28
|
+
**Exists on:** Server and standalone only. Never instantiated on clients.
|
|
29
|
+
|
|
30
|
+
**Why server-only:** GameMode is the authoritative referee. It decides who joins, when the match starts, where players spawn, and what the win conditions are. Client execution would allow cheating via local state manipulation.
|
|
31
|
+
|
|
32
|
+
**AGameMode adds** the full match-state machine (`EnteringMap` → `WaitingToStart` → `InProgress` → `WaitingPostMatch` → `LeavingMap`; `Aborted` on failure) with `ReadyToStartMatch` and `ReadyToEndMatch` hooks. Use `AGameModeBase` for lobby/simple games, `AGameMode` for match flow.
|
|
33
|
+
|
|
34
|
+
**Key API from source (GameModeBase.h):**
|
|
35
|
+
```cpp
|
|
36
|
+
// Class assignments — set in constructor
|
|
37
|
+
TSubclassOf<APawn> DefaultPawnClass;
|
|
38
|
+
TSubclassOf<AGameStateBase> GameStateClass;
|
|
39
|
+
TSubclassOf<APlayerController> PlayerControllerClass;
|
|
40
|
+
TSubclassOf<APlayerState> PlayerStateClass;
|
|
41
|
+
TSubclassOf<AHUD> HUDClass;
|
|
42
|
+
uint32 bUseSeamlessTravel : 1;
|
|
43
|
+
|
|
44
|
+
// Server startup and player join lifecycle (server only)
|
|
45
|
+
virtual void InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage);
|
|
46
|
+
virtual void PreLogin(const FString& Options, const FString& Address,
|
|
47
|
+
const FUniqueNetIdRepl& UniqueId, FString& ErrorMessage);
|
|
48
|
+
virtual APlayerController* Login(UPlayer* NewPlayer, ENetRole InRemoteRole,
|
|
49
|
+
const FString& Portal, const FString& Options,
|
|
50
|
+
const FUniqueNetIdRepl& UniqueId, FString& ErrorMessage);
|
|
51
|
+
virtual void PostLogin(APlayerController* NewPlayer); // first safe point for RPCs (DispatchPostLogin deprecated 5.6 — override PostLogin directly)
|
|
52
|
+
virtual void Logout(AController* Exiting);
|
|
53
|
+
virtual void HandleStartingNewPlayer(APlayerController* NewPlayer);
|
|
54
|
+
|
|
55
|
+
// Spawn pipeline
|
|
56
|
+
virtual AActor* FindPlayerStart(AController* Player, const FString& IncomingName = TEXT(""));
|
|
57
|
+
virtual void RestartPlayer(AController* NewPlayer);
|
|
58
|
+
virtual APawn* SpawnDefaultPawnFor(AController* NewPlayer, AActor* StartSpot);
|
|
59
|
+
|
|
60
|
+
// Travel
|
|
61
|
+
virtual void ProcessServerTravel(const FString& URL, bool bAbsolute = false);
|
|
62
|
+
virtual void GetSeamlessTravelActorList(bool bToTransition, TArray<AActor*>& ActorList);
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
### AGameStateBase / AGameState — Server + All Clients
|
|
68
|
+
|
|
69
|
+
**Exists on:** Everywhere. Fully replicated.
|
|
70
|
+
|
|
71
|
+
**Why everywhere:** Clients cannot read GameMode (it does not exist on them). Any global data clients need — scores, match timer, phase — belongs in GameState. `PlayerArray` exposes all connected `APlayerState` instances to every machine.
|
|
72
|
+
|
|
73
|
+
**Key API from source (GameStateBase.h):**
|
|
74
|
+
```cpp
|
|
75
|
+
// All PlayerStates, always replicated
|
|
76
|
+
UPROPERTY(Transient, BlueprintReadOnly)
|
|
77
|
+
TArray<TObjectPtr<APlayerState>> PlayerArray;
|
|
78
|
+
|
|
79
|
+
// The GameMode class (not instance) replicated to clients
|
|
80
|
+
UPROPERTY(Transient, BlueprintReadOnly, ReplicatedUsing=OnRep_GameModeClass)
|
|
81
|
+
TSubclassOf<AGameModeBase> GameModeClass;
|
|
82
|
+
|
|
83
|
+
// Server-authoritative clock, automatically synced
|
|
84
|
+
virtual double GetServerWorldTimeSeconds() const;
|
|
85
|
+
virtual bool HasBegunPlay() const;
|
|
86
|
+
virtual bool HasMatchStarted() const;
|
|
87
|
+
virtual bool HasMatchEnded() const;
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Custom replicated match data:**
|
|
91
|
+
```cpp
|
|
92
|
+
UCLASS()
|
|
93
|
+
class AMyGameState : public AGameStateBase
|
|
94
|
+
{
|
|
95
|
+
GENERATED_BODY()
|
|
96
|
+
public:
|
|
97
|
+
UPROPERTY(Replicated, BlueprintReadOnly) int32 TeamAScore;
|
|
98
|
+
UPROPERTY(Replicated, BlueprintReadOnly) int32 TeamBScore;
|
|
99
|
+
UPROPERTY(ReplicatedUsing=OnRep_MatchTimer) float MatchTimeRemaining;
|
|
100
|
+
|
|
101
|
+
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
void AMyGameState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
|
105
|
+
{
|
|
106
|
+
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
|
107
|
+
DOREPLIFETIME(AMyGameState, TeamAScore);
|
|
108
|
+
DOREPLIFETIME(AMyGameState, TeamBScore);
|
|
109
|
+
DOREPLIFETIME(AMyGameState, MatchTimeRemaining);
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
### APlayerController — Server (all) + Owning Client (own only)
|
|
116
|
+
|
|
117
|
+
**Exists on:** Server holds one per connected player. Each client holds only its own. Remote clients do not see other players' PlayerControllers.
|
|
118
|
+
|
|
119
|
+
**Why this split:** The PlayerController bridges one human to the server. Both ends run it for client-side prediction and server validation. A client has no reason to know another player's input state.
|
|
120
|
+
|
|
121
|
+
**Key API from source (PlayerController.h):**
|
|
122
|
+
```cpp
|
|
123
|
+
TObjectPtr<APlayerCameraManager> PlayerCameraManager; // camera, local only
|
|
124
|
+
TObjectPtr<APawn> AcknowledgedPawn; // server-confirmed possession
|
|
125
|
+
TObjectPtr<AHUD> MyHUD; // local only
|
|
126
|
+
|
|
127
|
+
uint32 bShowMouseCursor : 1;
|
|
128
|
+
uint32 bEnableStreamingSource : 1; // drives World Partition loading for this viewport
|
|
129
|
+
|
|
130
|
+
void SetInputMode(const FInputModeDataBase& InData); // FInputModeGameOnly, UIOnly, GameAndUI
|
|
131
|
+
virtual void PlayerTick(float DeltaTime); // only ticked locally
|
|
132
|
+
virtual void SetupInputComponent() override;
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**`SetupInputComponent` on PlayerController** is for non-pawn input: spectator actions, UI shortcuts, or global keybinds that persist across possession changes. For pawn-specific input, override `APawn::SetupPlayerInputComponent()` instead — see `/ue-input-system`.
|
|
136
|
+
|
|
137
|
+
**Enhanced Input setup:**
|
|
138
|
+
```cpp
|
|
139
|
+
void AMyPlayerController::BeginPlay()
|
|
140
|
+
{
|
|
141
|
+
Super::BeginPlay();
|
|
142
|
+
if (IsLocalController())
|
|
143
|
+
{
|
|
144
|
+
if (auto* Sub = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer()))
|
|
145
|
+
Sub->AddMappingContext(DefaultMappingContext, 0);
|
|
146
|
+
SetInputMode(FInputModeGameOnly());
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**RPC patterns:**
|
|
152
|
+
```cpp
|
|
153
|
+
UFUNCTION(Server, Reliable, WithValidation) void ServerRequestRespawn(); // client → server
|
|
154
|
+
UFUNCTION(Client, Reliable) void ClientNotifyMatchStart(); // server → client
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Possess/UnPossess (server-authority required):**
|
|
158
|
+
```cpp
|
|
159
|
+
// Take control of a new pawn — must run on server
|
|
160
|
+
PlayerController->Possess(NewPawn);
|
|
161
|
+
// Release the currently possessed pawn
|
|
162
|
+
PlayerController->UnPossess();
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**Listen-server dual-role:** On a listen server, the host's PlayerController is both `ROLE_Authority` and locally controlled. Guard dual-role logic with `IsLocalController()` checks. This is a common source of bugs where code assumes authority implies non-local (i.e., code written for dedicated servers runs incorrectly on a listen server host).
|
|
166
|
+
|
|
167
|
+
**ClientTravel — connect this client to a different server:**
|
|
168
|
+
```cpp
|
|
169
|
+
PlayerController->ClientTravel(TEXT("127.0.0.1:7777"), TRAVEL_Absolute);
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**ServerTravel — move all players to a new map (called from GameMode, server only):**
|
|
173
|
+
```cpp
|
|
174
|
+
GetWorld()->ServerTravel(TEXT("/Game/Maps/NewMap?listen"));
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
### AController — Shared Base
|
|
180
|
+
|
|
181
|
+
`AController` is the base class for both `APlayerController` and `AAIController`. It owns the pawn possession interface (`Possess`, `UnPossess`, `GetPawn`) and the rotation used to drive pawn orientation (`ControlRotation`). Subclass `APlayerController` for human players and `AAIController` for AI.
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
### APlayerState — Server + All Clients (Always Relevant)
|
|
186
|
+
|
|
187
|
+
**Exists on:** Server and all clients. Marked always-relevant so it replicates to everyone regardless of distance.
|
|
188
|
+
|
|
189
|
+
**Why always relevant:** Scoreboards, team displays, and player lists need to show data for every player, not just nearby ones. PlayerState survives pawn death — when a pawn is destroyed and respawned, the PlayerController keeps its PlayerState, preserving accumulated stats.
|
|
190
|
+
|
|
191
|
+
```cpp
|
|
192
|
+
UCLASS()
|
|
193
|
+
class AMyPlayerState : public APlayerState
|
|
194
|
+
{
|
|
195
|
+
GENERATED_BODY()
|
|
196
|
+
public:
|
|
197
|
+
UPROPERTY(Replicated, BlueprintReadOnly) int32 Kills;
|
|
198
|
+
UPROPERTY(Replicated, BlueprintReadOnly) int32 Deaths;
|
|
199
|
+
UPROPERTY(ReplicatedUsing=OnRep_Team) uint8 TeamIndex;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// Access patterns
|
|
203
|
+
APlayerState* PS = MyPawn->GetPlayerState();
|
|
204
|
+
APlayerState* PS = MyPC->PlayerState;
|
|
205
|
+
for (APlayerState* PS : GetGameState<AGameStateBase>()->PlayerArray) { /* all players */ }
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
### APawn — Server + All Clients (Replicated)
|
|
211
|
+
|
|
212
|
+
**Exists on:** Server (authority) and all clients (simulated or autonomous proxy). Minimal base — no mesh, no collision component, no movement component.
|
|
213
|
+
|
|
214
|
+
**Use APawn when:** entity is not a humanoid (vehicle, turret, drone), you need a completely custom movement component, or you need zero-overhead baseline.
|
|
215
|
+
|
|
216
|
+
```cpp
|
|
217
|
+
// Minimal subclass pattern
|
|
218
|
+
virtual void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) override;
|
|
219
|
+
virtual void PossessedBy(AController* NewController) override;
|
|
220
|
+
virtual void UnPossessed() override;
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
`ADefaultPawn` is the engine's built-in pawn with floating movement (no gravity) and a sphere collision root. It is used as the `DefaultPawnClass` placeholder when no custom pawn is assigned.
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
### ACharacter — Server + All Clients (Replicated with Prediction)
|
|
228
|
+
|
|
229
|
+
**Exists on:** Server (authority) and all clients. The locally controlled instance runs client-side prediction; simulated proxies interpolate from server updates.
|
|
230
|
+
|
|
231
|
+
**Why ACharacter:** Walking humanoids need capsule collision, gravity, jump, crouch, and movement prediction. ACharacter bundles all of this with built-in networked prediction via `UCharacterMovementComponent`.
|
|
232
|
+
|
|
233
|
+
**Component layout from source (Character.h):**
|
|
234
|
+
```cpp
|
|
235
|
+
// Private, access via getters
|
|
236
|
+
TObjectPtr<UCapsuleComponent> CapsuleComponent; // GetCapsuleComponent() — root
|
|
237
|
+
TObjectPtr<USkeletalMeshComponent> Mesh; // GetMesh()
|
|
238
|
+
TObjectPtr<UCharacterMovementComponent> CharacterMovement; // GetCharacterMovement()
|
|
239
|
+
TObjectPtr<UArrowComponent> ArrowComponent; // GetArrowComponent() — editor-only direction indicator
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
**Constructor configuration:**
|
|
243
|
+
```cpp
|
|
244
|
+
AMyCharacter::AMyCharacter()
|
|
245
|
+
{
|
|
246
|
+
GetCapsuleComponent()->SetCapsuleHalfHeight(96.f);
|
|
247
|
+
GetCapsuleComponent()->SetCapsuleRadius(42.f);
|
|
248
|
+
GetMesh()->SetRelativeLocation(FVector(0.f, 0.f, -97.f));
|
|
249
|
+
GetMesh()->SetRelativeRotation(FRotator(0.f, -90.f, 0.f));
|
|
250
|
+
|
|
251
|
+
GetCharacterMovement()->MaxWalkSpeed = 600.f;
|
|
252
|
+
GetCharacterMovement()->JumpZVelocity = 700.f;
|
|
253
|
+
GetCharacterMovement()->GravityScale = 1.75f;
|
|
254
|
+
GetCharacterMovement()->AirControl = 0.35f;
|
|
255
|
+
GetCharacterMovement()->NavAgentProps.bCanCrouch = true;
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
**Key ACharacter API from source:**
|
|
260
|
+
```cpp
|
|
261
|
+
// Jump — from Character.h
|
|
262
|
+
virtual void Jump(); // set bPressedJump, triggers on next tick
|
|
263
|
+
virtual void StopJumping(); // clear bPressedJump
|
|
264
|
+
bool CanJump() const;
|
|
265
|
+
|
|
266
|
+
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated) float JumpMaxHoldTime; // variable height
|
|
267
|
+
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated) int32 JumpMaxCount; // double jump
|
|
268
|
+
|
|
269
|
+
virtual void LaunchCharacter(FVector LaunchVelocity, bool bXYOverride, bool bZOverride);
|
|
270
|
+
|
|
271
|
+
// Crouch
|
|
272
|
+
void Crouch(bool bClientSimulation = false); // requests crouch via CharacterMovementComponent
|
|
273
|
+
void UnCrouch(bool bClientSimulation = false);
|
|
274
|
+
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_IsCrouched) uint8 bIsCrouched : 1;
|
|
275
|
+
|
|
276
|
+
// Movement mode
|
|
277
|
+
// MOVE_Walking, MOVE_Falling, MOVE_Swimming, MOVE_Flying, MOVE_Custom
|
|
278
|
+
GetCharacterMovement()->SetMovementMode(MOVE_Flying);
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**Custom movement modes:** Set `MOVE_Custom` then override `PhysCustom(float deltaTime, int32 Iterations)` in your `UCharacterMovementComponent` subclass. The `CustomMovementMode` byte lets you distinguish multiple custom modes within the same `PhysCustom` dispatch.
|
|
282
|
+
|
|
283
|
+
```cpp
|
|
284
|
+
// Custom movement mode: override in CMC subclass
|
|
285
|
+
void UMyCharacterMovement::PhysCustom(float DeltaTime, int32 Iterations)
|
|
286
|
+
{
|
|
287
|
+
if (CustomMovementMode == (uint8)ECustomMovement::Flying)
|
|
288
|
+
{
|
|
289
|
+
// Custom flying physics here
|
|
290
|
+
Velocity.Z += GetGravityZ() * DeltaTime;
|
|
291
|
+
}
|
|
292
|
+
Super::PhysCustom(DeltaTime, Iterations);
|
|
293
|
+
}
|
|
294
|
+
// Activate: CharMoveComp->SetMovementMode(MOVE_Custom, (uint8)ECustomMovement::Flying);
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
**Movement replication:** Client sends `ServerMovePacked`, server validates and replies via `ClientMoveResponsePacked`. This is automatic — do not call these RPCs manually.
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
### UGameInstance — Process Lifetime Singleton
|
|
302
|
+
|
|
303
|
+
**Exists on:** One per process. Survives ALL level loads.
|
|
304
|
+
|
|
305
|
+
**Why:** On level travel, every actor (including GameMode, GameState, PlayerController, PlayerState) is destroyed. GameInstance is never destroyed. It holds session handles, save game references, analytics state, and any data that must span the entire application lifetime.
|
|
306
|
+
|
|
307
|
+
```cpp
|
|
308
|
+
// Lifecycle overrides
|
|
309
|
+
virtual void Init() override; // called once at startup; subsystem init, save-game loading
|
|
310
|
+
virtual void OnStart() override; // called when the instance is ready, after Init
|
|
311
|
+
virtual void Shutdown() override; // called on application exit
|
|
312
|
+
|
|
313
|
+
// Access from actor or component
|
|
314
|
+
UMyGameInstance* GI = GetWorld()->GetGameInstance<UMyGameInstance>();
|
|
315
|
+
|
|
316
|
+
// Subsystems (also survive level travel)
|
|
317
|
+
UMySubsystem* Sub = GetGameInstance()->GetSubsystem<UMySubsystem>();
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Session Management (Online Subsystem)
|
|
321
|
+
|
|
322
|
+
```cpp
|
|
323
|
+
// Access the Online Subsystem session interface from GameInstance
|
|
324
|
+
IOnlineSubsystem* OSS = IOnlineSubsystem::Get();
|
|
325
|
+
IOnlineSessionPtr Sessions = OSS ? OSS->GetSessionInterface() : nullptr;
|
|
326
|
+
if (!Sessions.IsValid()) return;
|
|
327
|
+
|
|
328
|
+
// Create session (host)
|
|
329
|
+
FOnlineSessionSettings Settings;
|
|
330
|
+
Settings.bIsLANMatch = false;
|
|
331
|
+
Settings.NumPublicConnections = 4;
|
|
332
|
+
Settings.bShouldAdvertise = true;
|
|
333
|
+
Sessions->OnCreateSessionCompleteDelegates.AddUObject(
|
|
334
|
+
this, &UMyGameInstance::OnCreateSessionComplete);
|
|
335
|
+
Sessions->CreateSession(0, NAME_GameSession, Settings);
|
|
336
|
+
|
|
337
|
+
// Find sessions (client)
|
|
338
|
+
TSharedRef<FOnlineSessionSearch> Search = MakeShared<FOnlineSessionSearch>();
|
|
339
|
+
Sessions->OnFindSessionsCompleteDelegates.AddUObject(
|
|
340
|
+
this, &UMyGameInstance::OnFindSessionsComplete);
|
|
341
|
+
Sessions->FindSessions(0, Search);
|
|
342
|
+
|
|
343
|
+
// Join a found session
|
|
344
|
+
Sessions->OnJoinSessionCompleteDelegates.AddUObject(
|
|
345
|
+
this, &UMyGameInstance::OnJoinSessionComplete);
|
|
346
|
+
Sessions->JoinSession(0, NAME_GameSession, Search->SearchResults[0]);
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
The Online Subsystem abstracts platform-specific backends (Steam, EOS, Null for testing). Add `"OnlineSubsystem"` and `"OnlineSubsystemUtils"` to your Build.cs dependencies. After joining, retrieve the connect string with `GetResolvedConnectString` and call `ClientTravel`.
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
## GameMode: Registration and Spawn Pipeline
|
|
354
|
+
|
|
355
|
+
```cpp
|
|
356
|
+
AMyGameMode::AMyGameMode()
|
|
357
|
+
{
|
|
358
|
+
DefaultPawnClass = AMyCharacter::StaticClass();
|
|
359
|
+
PlayerControllerClass = AMyPlayerController::StaticClass();
|
|
360
|
+
GameStateClass = AMyGameState::StaticClass();
|
|
361
|
+
PlayerStateClass = AMyPlayerState::StaticClass();
|
|
362
|
+
HUDClass = AMyHUD::StaticClass();
|
|
363
|
+
bUseSeamlessTravel = true;
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
**Join sequence (server only):**
|
|
368
|
+
```
|
|
369
|
+
InitGame() → called before any player joins; use for map-specific rules init
|
|
370
|
+
PreLogin() → reject here (server full, banned)
|
|
371
|
+
Login() → create PlayerController, assign UniqueId
|
|
372
|
+
PostLogin() → first safe point for server→client RPCs; assign teams here
|
|
373
|
+
HandleStartingNewPlayer() → triggers RestartPlayer()
|
|
374
|
+
RestartPlayer() → FindPlayerStart() → SpawnDefaultPawnFor() → Possess()
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
**Match state (AGameMode only):**
|
|
378
|
+
```cpp
|
|
379
|
+
bool AMyGameMode::ReadyToStartMatch_Implementation()
|
|
380
|
+
{
|
|
381
|
+
return GetNumPlayers() >= MinPlayersToStart;
|
|
382
|
+
}
|
|
383
|
+
bool AMyGameMode::ReadyToEndMatch_Implementation()
|
|
384
|
+
{
|
|
385
|
+
return GetGameState<AMyGameState>()->TeamAScore >= ScoreLimit;
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## Travel Patterns
|
|
392
|
+
|
|
393
|
+
| Pattern | Clients disconnect? | GameMode/GameState survive? | Use when |
|
|
394
|
+
|---|---|---|---|
|
|
395
|
+
| Non-seamless (`ServerTravel`) | Yes, reconnect | No, recreated | Map change with clean slate |
|
|
396
|
+
| Seamless (`bUseSeamlessTravel=true`) | No | No, recreated | Lobby→game, round change |
|
|
397
|
+
|
|
398
|
+
**Seamless travel survival:**
|
|
399
|
+
- Always: `UGameInstance`, `APlayerController`, `APlayerState`
|
|
400
|
+
- Never: `AGameMode`, `AGameState`, level actors
|
|
401
|
+
- Optional: actors you add in `GetSeamlessTravelActorList()`
|
|
402
|
+
|
|
403
|
+
---
|
|
404
|
+
|
|
405
|
+
## Common Mistakes
|
|
406
|
+
|
|
407
|
+
**GameMode on client (null crash):**
|
|
408
|
+
```cpp
|
|
409
|
+
// WRONG
|
|
410
|
+
GetWorld()->GetAuthGameMode<AMyGameMode>()->EndMatch(); // nullptr on client
|
|
411
|
+
// RIGHT
|
|
412
|
+
if (HasAuthority()) { if (auto* GM = GetWorld()->GetAuthGameMode<AMyGameMode>()) GM->EndMatch(); }
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
**Wrong class for data:**
|
|
416
|
+
```
|
|
417
|
+
Score visible to all clients → APlayerState, NOT APlayerController
|
|
418
|
+
Match timer on clients → AGameState replicated property, NOT AGameMode
|
|
419
|
+
Data surviving level travel → UGameInstance, NOT AGameState
|
|
420
|
+
Input binding → PlayerController or Pawn::SetupPlayerInputComponent, NOT ACharacter body
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
**AcknowledgedPawn vs GetPawn:**
|
|
424
|
+
`GetPawn()` on a PlayerController may return a pawn before the server confirms possession. Use `AcknowledgedPawn` when you need the server-confirmed pawn.
|
|
425
|
+
|
|
426
|
+
**Dedicated server guard:**
|
|
427
|
+
```cpp
|
|
428
|
+
if (GetNetMode() != NM_DedicatedServer)
|
|
429
|
+
{
|
|
430
|
+
// HUD, camera, audio — never run these on dedicated server
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
**PIE multi-player:** In PIE with multiple players, each has its own PlayerController but all share the same GameMode instance. Test multiplayer logic with PIE > Number of Players set to 2 or more.
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
438
|
+
## Related Skills
|
|
439
|
+
|
|
440
|
+
- `/ue-actor-component-architecture` — actor lifecycle, component tick, attachment
|
|
441
|
+
- `/ue-networking-replication` — DOREPLIFETIME conditions, RPC patterns, push model
|
|
442
|
+
- `/ue-input-system` — Enhanced Input mapping contexts and input actions
|
|
443
|
+
|
|
444
|
+
## Reference Files
|
|
445
|
+
|
|
446
|
+
- `references/framework-class-map.md` — which framework class owns which responsibility, and what exists on server vs client
|
|
447
|
+
|
|
448
|
+
---
|
|
449
|
+
|
|
450
|
+
## Checklist
|
|
451
|
+
|
|
452
|
+
Validate before considering the work done:
|
|
453
|
+
|
|
454
|
+
- [ ] State lives in the correct class — GameState for shared, PlayerState for per-player
|
|
455
|
+
- [ ] Nothing authoritative lives on the Pawn, which can be destroyed and respawned
|
|
456
|
+
- [ ] Server and client paths were both considered for every piece of state
|
|
457
|
+
- [ ] Conventions in `docs/unreal/project-context.md` were read and followed
|
|
458
|
+
- [ ] The Common Mistakes section of this skill was reviewed against the change
|
|
459
|
+
|
|
460
|
+
**PASS** — every item holds. **CONCERNS** — the work stands but at least one item is unresolved and worth flagging to the user. **FAIL** — an item is violated in a way that breaks correctness, replication or the frame budget; fix it before handing the work over.
|
|
461
|
+
|
|
462
|
+
## Next Steps
|
|
463
|
+
|
|
464
|
+
- `/ue-networking-replication` — replicate the framework state
|
|
465
|
+
- `/code-review` — architectural and quality review of the change
|
|
466
|
+
- `/commit` — conventional commit once the change is verified
|