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,484 @@
|
|
|
1
|
+
# Common UI Plugin — Setup and Patterns
|
|
2
|
+
|
|
3
|
+
> Source: `Engine/Plugins/Runtime/CommonUI/Source/CommonUI/Public/`
|
|
4
|
+
|
|
5
|
+
Common UI is Epic's cross-platform UI framework built on top of UMG. It handles input method detection (gamepad vs. mouse vs. touch), input routing through an activation tree, and provides base classes for platform-agnostic buttons and screens.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Plugin Setup
|
|
10
|
+
|
|
11
|
+
### 1. Enable the Plugin
|
|
12
|
+
|
|
13
|
+
In `<ProjectName>.uproject`:
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"Plugins": [
|
|
18
|
+
{ "Name": "CommonUI", "Enabled": true },
|
|
19
|
+
{ "Name": "CommonInput", "Enabled": true }
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### 2. Build.cs Dependencies
|
|
25
|
+
|
|
26
|
+
```csharp
|
|
27
|
+
// <ProjectName>.Build.cs
|
|
28
|
+
PrivateDependencyModuleNames.AddRange(new string[]
|
|
29
|
+
{
|
|
30
|
+
"CommonUI",
|
|
31
|
+
"CommonInput",
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 3. Configure the Game Viewport Client
|
|
36
|
+
|
|
37
|
+
Common UI requires a custom viewport client to detect input method changes.
|
|
38
|
+
|
|
39
|
+
**Project Settings → Maps & Modes → Game Viewport Client Class** → set to `CommonGameViewportClient`.
|
|
40
|
+
|
|
41
|
+
Or in `DefaultEngine.ini`:
|
|
42
|
+
|
|
43
|
+
```ini
|
|
44
|
+
[/Script/Engine.Engine]
|
|
45
|
+
GameViewportClientClassName=/Script/CommonUI.CommonGameViewportClient
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`UCommonGameViewportClient` detects whether the most recent input was from a gamepad, mouse, or touch, and broadcasts this change via `UCommonInputSubsystem`. All `UCommonButtonBase` and `UCommonActivatableWidget` instances respond to this automatically.
|
|
49
|
+
|
|
50
|
+
### 4. Configure Common Input Settings
|
|
51
|
+
|
|
52
|
+
In **Project Settings → Common Input Settings**:
|
|
53
|
+
|
|
54
|
+
- **Default Joystick Name**: Gamepad action icon set to display
|
|
55
|
+
- **Input Data**: Map platform + input type combinations to icon data tables
|
|
56
|
+
- **Platform Input**: Define which input types each target platform supports
|
|
57
|
+
|
|
58
|
+
Or via `DefaultInput.ini`:
|
|
59
|
+
|
|
60
|
+
```ini
|
|
61
|
+
[/Script/CommonInput.CommonInputSettings]
|
|
62
|
+
InputData=/Game/UI/CommonInputData.CommonInputData
|
|
63
|
+
bEnableInputMethodTrigger=True
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Core Classes
|
|
69
|
+
|
|
70
|
+
### UCommonActivatableWidget
|
|
71
|
+
|
|
72
|
+
The foundation of Common UI's screen management. A widget that can be activated (brought to focus) and deactivated without being created or destroyed.
|
|
73
|
+
|
|
74
|
+
**Header:** `CommonActivatableWidget.h`
|
|
75
|
+
**Base class:** `UCommonUserWidget` → `UUserWidget`
|
|
76
|
+
|
|
77
|
+
```cpp
|
|
78
|
+
// Activation state
|
|
79
|
+
bool IsActivated() const;
|
|
80
|
+
void ActivateWidget();
|
|
81
|
+
void DeactivateWidget();
|
|
82
|
+
|
|
83
|
+
// Delegates (non-dynamic multicast)
|
|
84
|
+
FSimpleMulticastDelegate& OnActivated() const;
|
|
85
|
+
FSimpleMulticastDelegate& OnDeactivated() const;
|
|
86
|
+
|
|
87
|
+
// Focus
|
|
88
|
+
UWidget* GetDesiredFocusTarget() const;
|
|
89
|
+
void ClearFocusRestorationTarget();
|
|
90
|
+
void RequestRefreshFocus();
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Key UPROPERTY settings (configure in Blueprint defaults or C++ constructor):**
|
|
94
|
+
|
|
95
|
+
```cpp
|
|
96
|
+
// Auto-activate when constructed (false by default)
|
|
97
|
+
UPROPERTY(EditAnywhere, Category = Activation)
|
|
98
|
+
bool bAutoActivate = false;
|
|
99
|
+
|
|
100
|
+
// Acts as a modal — blocks input routing to all ancestors
|
|
101
|
+
UPROPERTY(EditAnywhere, Category = Activation)
|
|
102
|
+
bool bIsModal = false;
|
|
103
|
+
|
|
104
|
+
// Receives "Back" action and deactivates on it
|
|
105
|
+
UPROPERTY(EditAnywhere, Category = Back)
|
|
106
|
+
bool bIsBackHandler = false;
|
|
107
|
+
|
|
108
|
+
// Restore focus to the previously-focused widget when re-activating
|
|
109
|
+
UPROPERTY(EditAnywhere, Category = Activation)
|
|
110
|
+
bool bAutoRestoreFocus = false;
|
|
111
|
+
|
|
112
|
+
// Set visibility automatically on activation/deactivation
|
|
113
|
+
UPROPERTY(EditAnywhere, Category = Activation)
|
|
114
|
+
bool bSetVisibilityOnActivated = false;
|
|
115
|
+
UPROPERTY(EditAnywhere, Category = Activation)
|
|
116
|
+
ESlateVisibility ActivatedVisibility = ESlateVisibility::SelfHitTestInvisible;
|
|
117
|
+
UPROPERTY(EditAnywhere, Category = Activation)
|
|
118
|
+
bool bSetVisibilityOnDeactivated = false;
|
|
119
|
+
UPROPERTY(EditAnywhere, Category = Activation)
|
|
120
|
+
ESlateVisibility DeactivatedVisibility = ESlateVisibility::Collapsed;
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Subclassing pattern:**
|
|
124
|
+
|
|
125
|
+
```cpp
|
|
126
|
+
// MyMenuScreen.h
|
|
127
|
+
#pragma once
|
|
128
|
+
#include "CommonActivatableWidget.h"
|
|
129
|
+
#include "MyMenuScreen.generated.h"
|
|
130
|
+
|
|
131
|
+
UCLASS()
|
|
132
|
+
class MYGAME_API UMyMenuScreen : public UCommonActivatableWidget
|
|
133
|
+
{
|
|
134
|
+
GENERATED_BODY()
|
|
135
|
+
|
|
136
|
+
protected:
|
|
137
|
+
virtual void NativeOnActivated() override;
|
|
138
|
+
virtual void NativeOnDeactivated() override;
|
|
139
|
+
virtual UWidget* NativeGetDesiredFocusTarget() const override;
|
|
140
|
+
virtual TOptional<FUIInputConfig> GetDesiredInputConfig() const override;
|
|
141
|
+
|
|
142
|
+
UPROPERTY(meta=(BindWidget)) TObjectPtr<UCommonButtonBase> CloseButton;
|
|
143
|
+
UPROPERTY(meta=(BindWidget)) TObjectPtr<UCommonButtonBase> ConfirmButton;
|
|
144
|
+
};
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
```cpp
|
|
148
|
+
// MyMenuScreen.cpp
|
|
149
|
+
void UMyMenuScreen::NativeOnActivated()
|
|
150
|
+
{
|
|
151
|
+
Super::NativeOnActivated();
|
|
152
|
+
CloseButton->OnClicked().AddUObject(this, &UMyMenuScreen::DeactivateWidget);
|
|
153
|
+
ConfirmButton->OnClicked().AddUObject(this, &UMyMenuScreen::HandleConfirm);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
void UMyMenuScreen::NativeOnDeactivated()
|
|
157
|
+
{
|
|
158
|
+
CloseButton->OnClicked().RemoveAll(this);
|
|
159
|
+
ConfirmButton->OnClicked().RemoveAll(this);
|
|
160
|
+
Super::NativeOnDeactivated();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
UWidget* UMyMenuScreen::NativeGetDesiredFocusTarget() const
|
|
164
|
+
{
|
|
165
|
+
// The first focusable button receives focus when this screen activates
|
|
166
|
+
return ConfirmButton;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
TOptional<FUIInputConfig> UMyMenuScreen::GetDesiredInputConfig() const
|
|
170
|
+
{
|
|
171
|
+
// ECommonInputMode::Menu — enables gamepad menu navigation, hides cursor on gamepad
|
|
172
|
+
return FUIInputConfig(ECommonInputMode::Menu, EMouseCaptureMode::NoCapture);
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
### UCommonActivatableWidgetStack
|
|
179
|
+
|
|
180
|
+
**Header:** `Widgets/CommonActivatableWidgetContainer.h`
|
|
181
|
+
**Base class:** `UCommonActivatableWidgetContainerBase`
|
|
182
|
+
|
|
183
|
+
The correct class for screen layer stacks. Shows only the topmost widget; deactivating the top widget reveals the previous one. Use this for game layer, menu layer, and modal layer containers — do NOT use `UCommonActivatableWidgetSwitcher` here, which inherits from `UCommonAnimatedSwitcher`/`UWidgetSwitcher` and is a different (index-based switcher) widget that does not have `AddWidget()`.
|
|
184
|
+
|
|
185
|
+
```cpp
|
|
186
|
+
// In a root HUD widget:
|
|
187
|
+
UPROPERTY(meta=(BindWidget))
|
|
188
|
+
TObjectPtr<UCommonActivatableWidgetStack> MenuLayer;
|
|
189
|
+
|
|
190
|
+
// Push a new screen onto the stack:
|
|
191
|
+
void UMyHUDWidget::ShowOptionsScreen()
|
|
192
|
+
{
|
|
193
|
+
UMyOptionsScreen* Screen = Cast<UMyOptionsScreen>(
|
|
194
|
+
MenuLayer->AddWidget(UMyOptionsScreen::StaticClass()));
|
|
195
|
+
// AddWidget creates and activates the widget internally
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Go back (deactivate top widget — stack re-activates the previous one):
|
|
199
|
+
void UMyHUDWidget::GoBack()
|
|
200
|
+
{
|
|
201
|
+
if (UCommonActivatableWidget* Active = MenuLayer->GetActiveWidget())
|
|
202
|
+
{
|
|
203
|
+
Active->DeactivateWidget();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
**Important:** `UCommonActivatableWidgetStack::AddWidget(TSubclassOf<UCommonActivatableWidget>)` creates the widget and pushes it. It returns `UCommonActivatableWidget*` (cast to your type). Deactivating the top widget automatically re-activates the widget beneath it.
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
### UCommonButtonBase
|
|
213
|
+
|
|
214
|
+
**Header:** `CommonButtonBase.h`
|
|
215
|
+
**Base class:** `UCommonUserWidget`
|
|
216
|
+
|
|
217
|
+
A replacement for `UButton` that is aware of the current input method (mouse, gamepad, touch) and can display platform-appropriate icons (e.g., "A button" on Xbox, "Cross" on PlayStation).
|
|
218
|
+
|
|
219
|
+
```cpp
|
|
220
|
+
// OnClicked returns FCommonButtonEvent (DECLARE_EVENT-based) — NOT a DYNAMIC delegate
|
|
221
|
+
// Use AddUObject or AddWeakLambda instead of AddDynamic
|
|
222
|
+
FCommonButtonEvent& OnClicked();
|
|
223
|
+
|
|
224
|
+
// Example binding in NativeOnActivated (remove in NativeOnDeactivated)
|
|
225
|
+
MyButton->OnClicked().AddUObject(this, &UMyScreen::HandleButtonClicked);
|
|
226
|
+
MyButton->OnClicked().RemoveAll(this);
|
|
227
|
+
|
|
228
|
+
// Enable/disable
|
|
229
|
+
MyButton->SetIsEnabled(false);
|
|
230
|
+
|
|
231
|
+
// Selected state (toggle-style buttons)
|
|
232
|
+
MyButton->SetIsSelected(true);
|
|
233
|
+
bool bSelected = MyButton->GetSelected();
|
|
234
|
+
|
|
235
|
+
// Hovering
|
|
236
|
+
MyButton->SetIsInteractionEnabled(true);
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
**Styling:** `UCommonButtonBase` uses a `UCommonButtonStyle` data asset instead of `FButtonStyle`. Configure this in the widget Blueprint.
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
### UCommonUIActionRouter
|
|
244
|
+
|
|
245
|
+
**Header:** Not typically subclassed — managed by the Common UI subsystem.
|
|
246
|
+
|
|
247
|
+
The action router manages which activatable widget receives input actions (confirm, cancel/back, etc.) based on the current activation tree. When `UCommonActivatableWidget::ActivateWidget()` is called, the widget registers as a node in the action routing tree. Input actions are routed to the leaf-most active node first.
|
|
248
|
+
|
|
249
|
+
**You rarely interact with `UCommonUIActionRouter` directly.** Instead, configure per-widget behavior via:
|
|
250
|
+
- `bIsBackHandler` on `UCommonActivatableWidget`
|
|
251
|
+
- `GetDesiredInputConfig()` override
|
|
252
|
+
- `InputMapping` (Enhanced Input context) on `UCommonActivatableWidget`
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
### UCommonInputSubsystem
|
|
257
|
+
|
|
258
|
+
Provides runtime information about the current input method and allows observing changes.
|
|
259
|
+
|
|
260
|
+
```cpp
|
|
261
|
+
// Get the subsystem
|
|
262
|
+
UCommonInputSubsystem* InputSubsystem = UCommonInputSubsystem::Get(GetOwningLocalPlayer());
|
|
263
|
+
|
|
264
|
+
// Query current input type
|
|
265
|
+
ECommonInputType CurrentType = InputSubsystem->GetCurrentInputType();
|
|
266
|
+
// ECommonInputType: None, MouseAndKeyboard, Gamepad, Touch
|
|
267
|
+
|
|
268
|
+
// Is gamepad active?
|
|
269
|
+
bool bGamepad = CurrentType == ECommonInputType::Gamepad;
|
|
270
|
+
|
|
271
|
+
// Observe input type changes
|
|
272
|
+
InputSubsystem->OnInputMethodChangedNative.AddUObject(
|
|
273
|
+
this, &UMyWidget::HandleInputMethodChanged);
|
|
274
|
+
|
|
275
|
+
UFUNCTION()
|
|
276
|
+
void UMyWidget::HandleInputMethodChanged(ECommonInputType NewInputType)
|
|
277
|
+
{
|
|
278
|
+
// Update cursor visibility, icon display, etc.
|
|
279
|
+
bool bShowMouse = NewInputType == ECommonInputType::MouseAndKeyboard
|
|
280
|
+
|| NewInputType == ECommonInputType::Touch;
|
|
281
|
+
GetOwningPlayer()->SetShowMouseCursor(bShowMouse);
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## Input Configuration
|
|
288
|
+
|
|
289
|
+
### FUIInputConfig
|
|
290
|
+
|
|
291
|
+
Returned from `UCommonActivatableWidget::GetDesiredInputConfig()` to define how input is handled while this widget is the active leaf.
|
|
292
|
+
|
|
293
|
+
```cpp
|
|
294
|
+
// Menu mode: gamepad navigates widgets, no mouse capture
|
|
295
|
+
return FUIInputConfig(ECommonInputMode::Menu, EMouseCaptureMode::NoCapture);
|
|
296
|
+
|
|
297
|
+
// Game mode: game receives all input
|
|
298
|
+
return FUIInputConfig(ECommonInputMode::Game, EMouseCaptureMode::CapturePermanently);
|
|
299
|
+
|
|
300
|
+
// All mode: both game and UI receive input (e.g., for HUD with tooltips)
|
|
301
|
+
return FUIInputConfig(ECommonInputMode::All, EMouseCaptureMode::NoCapture);
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
| `ECommonInputMode` | Description |
|
|
305
|
+
|---|---|
|
|
306
|
+
| `Menu` | Game input suspended. UI navigation active. |
|
|
307
|
+
| `Game` | Game receives input. UI does not route actions. |
|
|
308
|
+
| `All` | Both game and UI receive input simultaneously. |
|
|
309
|
+
|
|
310
|
+
### Enhanced Input Integration
|
|
311
|
+
|
|
312
|
+
`UCommonActivatableWidget` supports an `InputMapping` property for adding an `UInputMappingContext` while the widget is active:
|
|
313
|
+
|
|
314
|
+
```cpp
|
|
315
|
+
// Set in the widget's Blueprint defaults or C++ constructor:
|
|
316
|
+
UPROPERTY(EditAnywhere, Category="Input")
|
|
317
|
+
TObjectPtr<UInputMappingContext> InputMapping;
|
|
318
|
+
|
|
319
|
+
UPROPERTY(EditAnywhere, Category="Input")
|
|
320
|
+
int32 InputMappingPriority = 0;
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
The mapping context is added when `ActivateWidget()` is called and removed when `DeactivateWidget()` is called via `ActivateMappingContext()` / `DeactivateMappingContext()`.
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## UI Layer Architecture Pattern
|
|
328
|
+
|
|
329
|
+
A typical Common UI hierarchy for a game with main menu, pause, and HUD:
|
|
330
|
+
|
|
331
|
+
```
|
|
332
|
+
AHUD or PlayerController
|
|
333
|
+
└── URootUIWidget (UUserWidget, AddToViewport ZOrder=0)
|
|
334
|
+
├── UCommonActivatableWidgetStack "GameLayer"
|
|
335
|
+
│ └── UMyHUDWidget (always active during gameplay)
|
|
336
|
+
├── UCommonActivatableWidgetStack "MenuLayer"
|
|
337
|
+
│ ├── UMyMainMenuWidget
|
|
338
|
+
│ ├── UMyOptionsScreen
|
|
339
|
+
│ └── UMyPauseMenuWidget
|
|
340
|
+
└── UCommonActivatableWidgetStack "ModalLayer"
|
|
341
|
+
└── UMyConfirmationDialog (modal, bIsModal=true)
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
**Implementation:**
|
|
345
|
+
|
|
346
|
+
```cpp
|
|
347
|
+
// URootUIWidget.h
|
|
348
|
+
UCLASS()
|
|
349
|
+
class MYGAME_API URootUIWidget : public UUserWidget
|
|
350
|
+
{
|
|
351
|
+
GENERATED_BODY()
|
|
352
|
+
|
|
353
|
+
public:
|
|
354
|
+
template<typename T>
|
|
355
|
+
T* PushMenuWidget(TSubclassOf<T> WidgetClass)
|
|
356
|
+
{
|
|
357
|
+
return Cast<T>(MenuLayer->AddWidget(WidgetClass));
|
|
358
|
+
// AddWidget creates, pushes, and activates the widget
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
void PopMenuWidget()
|
|
362
|
+
{
|
|
363
|
+
if (UCommonActivatableWidget* Active = MenuLayer->GetActiveWidget())
|
|
364
|
+
{
|
|
365
|
+
Active->DeactivateWidget();
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
protected:
|
|
370
|
+
UPROPERTY(meta=(BindWidget))
|
|
371
|
+
TObjectPtr<UCommonActivatableWidgetStack> GameLayer;
|
|
372
|
+
|
|
373
|
+
UPROPERTY(meta=(BindWidget))
|
|
374
|
+
TObjectPtr<UCommonActivatableWidgetStack> MenuLayer;
|
|
375
|
+
|
|
376
|
+
UPROPERTY(meta=(BindWidget))
|
|
377
|
+
TObjectPtr<UCommonActivatableWidgetStack> ModalLayer;
|
|
378
|
+
};
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
383
|
+
## Common UI Gamepad Navigation
|
|
384
|
+
|
|
385
|
+
Common UI provides automatic focus management for gamepad navigation. When a `UCommonActivatableWidget` activates:
|
|
386
|
+
|
|
387
|
+
1. `GetDesiredFocusTarget()` is called.
|
|
388
|
+
2. The returned widget receives Slate keyboard focus.
|
|
389
|
+
3. Directional pad / left stick navigates between focusable widgets automatically.
|
|
390
|
+
4. "Back" / B-button / Escape triggers the back action if `bIsBackHandler = true`.
|
|
391
|
+
|
|
392
|
+
**Focus target best practices:**
|
|
393
|
+
|
|
394
|
+
```cpp
|
|
395
|
+
UWidget* UMyMenuScreen::NativeGetDesiredFocusTarget() const
|
|
396
|
+
{
|
|
397
|
+
// Return the first interactive widget in tab order
|
|
398
|
+
// This is the widget that gets focus when the screen activates
|
|
399
|
+
return FirstButton;
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
**Ensure your buttons are focusable:**
|
|
404
|
+
|
|
405
|
+
In `UCommonButtonBase` Blueprint defaults or via C++:
|
|
406
|
+
```cpp
|
|
407
|
+
// IsFocusable is true by default on UCommonButtonBase
|
|
408
|
+
// For standard UButton, explicitly set it:
|
|
409
|
+
MyButton->InitIsFocusable(true); // Call before Slate widget is constructed
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
---
|
|
413
|
+
|
|
414
|
+
## Common Mistakes
|
|
415
|
+
|
|
416
|
+
**Not removing the delegate binding when deactivated**
|
|
417
|
+
```cpp
|
|
418
|
+
// BAD: leaks bindings, fires after the screen is gone
|
|
419
|
+
void UMyScreen::NativeOnActivated() {
|
|
420
|
+
MyButton->OnClicked().AddUObject(this, &UMyScreen::HandleClick);
|
|
421
|
+
// Never removed!
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// GOOD
|
|
425
|
+
void UMyScreen::NativeOnActivated() {
|
|
426
|
+
MyButton->OnClicked().AddUObject(this, &UMyScreen::HandleClick);
|
|
427
|
+
}
|
|
428
|
+
void UMyScreen::NativeOnDeactivated() {
|
|
429
|
+
MyButton->OnClicked().RemoveAll(this);
|
|
430
|
+
}
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
**Using AddDynamic with UCommonButtonBase::OnClicked()**
|
|
434
|
+
```cpp
|
|
435
|
+
// WRONG: OnClicked() returns FCommonButtonEvent, not a DYNAMIC delegate
|
|
436
|
+
MyButton->OnClicked().AddDynamic(this, &UMyScreen::HandleClick); // Compile error
|
|
437
|
+
|
|
438
|
+
// CORRECT
|
|
439
|
+
MyButton->OnClicked().AddUObject(this, &UMyScreen::HandleClick);
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
**Skipping the viewport client configuration**
|
|
443
|
+
|
|
444
|
+
Common UI's input method detection requires `UCommonGameViewportClient`. Without it, `UCommonInputSubsystem::GetCurrentInputType()` always returns `MouseAndKeyboard` on non-console platforms, and `UCommonButtonBase` icon display does not work.
|
|
445
|
+
|
|
446
|
+
**Mixing Common UI and raw SetInputMode calls**
|
|
447
|
+
|
|
448
|
+
Once Common UI is active, avoid calling `GetOwningPlayer()->SetInputMode(FInputModeUIOnly())`. Common UI manages input mode internally through `FUIInputConfig`. Direct calls bypass the activation tree and cause conflicts.
|
|
449
|
+
|
|
450
|
+
```cpp
|
|
451
|
+
// BAD with Common UI
|
|
452
|
+
GetOwningPlayer()->SetInputMode(FInputModeUIOnly());
|
|
453
|
+
|
|
454
|
+
// GOOD: return the config from the activatable widget
|
|
455
|
+
TOptional<FUIInputConfig> UMyScreen::GetDesiredInputConfig() const {
|
|
456
|
+
return FUIInputConfig(ECommonInputMode::Menu, EMouseCaptureMode::NoCapture);
|
|
457
|
+
}
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
**Not calling Super in activation overrides**
|
|
461
|
+
```cpp
|
|
462
|
+
// REQUIRED — Super sets up internal state, delegates, and focus routing
|
|
463
|
+
void UMyScreen::NativeOnActivated() {
|
|
464
|
+
Super::NativeOnActivated(); // Must be called
|
|
465
|
+
// Your code
|
|
466
|
+
}
|
|
467
|
+
void UMyScreen::NativeOnDeactivated() {
|
|
468
|
+
// Your cleanup
|
|
469
|
+
Super::NativeOnDeactivated(); // Must be called
|
|
470
|
+
}
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
## Checklist: Adding a New Screen
|
|
476
|
+
|
|
477
|
+
1. Create a `UCommonActivatableWidget` subclass (C++ or BP).
|
|
478
|
+
2. In C++: override `NativeOnActivated`, `NativeOnDeactivated`, `NativeGetDesiredFocusTarget`, `GetDesiredInputConfig`.
|
|
479
|
+
3. In the UMG Blueprint: design the layout using `UCommonButtonBase` for all interactive buttons.
|
|
480
|
+
4. Set `bIsBackHandler = true` if this screen should close on Back/Escape/B-button.
|
|
481
|
+
5. Set `bAutoActivate = false` (default) unless you need activation on construction.
|
|
482
|
+
6. Push via the appropriate layer stack (game, menu, or modal).
|
|
483
|
+
7. Verify focus flows correctly on gamepad by testing `NativeGetDesiredFocusTarget`.
|
|
484
|
+
8. Verify `GetDesiredInputConfig` returns the correct `ECommonInputMode`.
|