Skip to content

Commit 306e169

Browse files
Merge pull request #256 from EmbarrassingMoment/v.1.2.0
V.1.2.0
2 parents e807be5 + 32d5260 commit 306e169

17 files changed

Lines changed: 814 additions & 32 deletions

CHANGELOG.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,29 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66

77
---
88

9+
## [Unreleased]
10+
11+
## [1.2.0] - 2026-05-03
12+
13+
### Added
14+
15+
**Built-in Effects (+1, total 26)**
16+
- **Diamond Band Wipe** — A dynamic transition where a diamond-shaped band expands from the center, splitting apart to reveal the underlying scene.
17+
18+
**Sequence System**
19+
- `UTransitionSequence` data asset for chaining multiple transitions back-to-back.
20+
- `PlaySequence` / `StopSequence` / `IsSequencePlaying` / `GetCurrentSequenceStep` API on `UTransitionManagerSubsystem`.
21+
- `PlaySequenceAndWait` latent Blueprint node.
22+
- `OnSequenceCompleted` and `OnSequenceStepChanged` delegates.
23+
24+
### Fixed
25+
26+
- Fixed a one-frame background flash between sequence steps caused by the previous step's PostProcessVolume being destroyed before the next step's effect was initialized. During a sequence, the effect now hot-swaps across steps and is only torn down when the full sequence completes.
27+
- Fixed an issue where an incorrect material instance was set for DA_FadeToBlack.
28+
- Refactoring to a more efficient node configuration for M_Transition_Diamond.
29+
30+
---
31+
932
## [1.1.0] - 2026-04-09
1033

1134
### Added

CLAUDE.md

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Build & Setup
6+
7+
This is an Unreal Engine 5.5 plugin project. There are no CLI build scripts — all compilation happens through the UE editor or Visual Studio 2022.
8+
9+
**Setup steps:**
10+
1. Right-click `TransitionFX_Dev.uproject`**Generate Visual Studio project files**
11+
2. Open `TransitionFX_Dev.uproject` in UE 5.5; click **Yes** to rebuild missing modules
12+
3. First launch compiles shaders (several minutes)
13+
14+
**Requirements:** Visual Studio 2022 (C++ Game Development workload), UE 5.5+, Windows with DX12/SM6 GPU.
15+
16+
**Testing:** No automated test suite. Test manually using:
17+
- The `L_ShowCase` level (showcases all 26 transition effects)
18+
- The in-editor **Transition Preview Panel** (real-time playback with easing/duration controls)
19+
20+
## Architecture
21+
22+
TransitionFX is a **data-driven, GameInstance-scoped** transition system built on two C++ modules:
23+
24+
### Module Boundaries
25+
26+
| Module | Path | Role |
27+
|--------|------|------|
28+
| `TransitionFX` (Runtime) | `Plugins/TransitionFX/Source/TransitionFX/` | Core state machine, effect rendering, Blueprint API |
29+
| `TransitionFXEditor` (Editor) | `Plugins/TransitionFX/Source/TransitionFXEditor/` | Preview panel, GIF exporter, asset type actions |
30+
31+
### Data Flow
32+
33+
```
34+
Blueprint / C++ call
35+
36+
UTransitionBlueprintLibrary (latent actions — PlayTransitionAndWait, OpenLevelWithTransition)
37+
38+
UTransitionManagerSubsystem (GameInstance subsystem — state machine, pooling, event broadcasting)
39+
40+
ITransitionEffect / UPostProcessTransitionEffect (PostProcess volume + dynamic material instance)
41+
42+
Material parameter "Progress" [0.0→1.0] drives SDF shader on screen
43+
```
44+
45+
### Core Classes
46+
47+
| Class | File | Role |
48+
|-------|------|------|
49+
| `UTransitionManagerSubsystem` | `Public/TransitionManagerSubsystem.h` | Central tick-driven state machine; manages effect pool, audio, input blocking, level transitions, and sequences |
50+
| `UTransitionPreset` | `Public/TransitionPreset.h` | Data Asset holding all config: effect class, material, duration, easing, audio, input blocking |
51+
| `ITransitionEffect` | `Public/ITransitionEffect.h` | Interface for effect implementations (`Initialize`, `UpdateProgress`, `Cleanup`, `SetInvert`) |
52+
| `UPostProcessTransitionEffect` | `Public/PostProcessTransitionEffect.h` | Concrete implementation: creates a PostProcess volume + dynamic material instance |
53+
| `UTransitionBlueprintLibrary` | `Public/TransitionBlueprintLibrary.h` | Blueprint-callable latent actions and helper nodes |
54+
| `UTransitionSequence` | `Public/TransitionSequence.h` | Data Asset for chaining multiple transitions; each `FTransitionSequenceEntry` holds preset, mode, duration override, delay |
55+
| `UTransitionFXConfig` | `Public/TransitionFXConfig.h` | Compile-time constants: material parameter names (`Progress`, `Invert`, `TransitionColor`), default asset path |
56+
57+
### Effect Pool
58+
59+
`UTransitionManagerSubsystem` maintains a `TMap<UClass*, FTransitionEffectPool>` capped at **3 instances per class**. Always return effects to the pool via `CleanupAndPoolCurrentEffect()` — never destroy them directly.
60+
61+
### Easing
62+
63+
Easing is applied in the subsystem's `Tick()` using `ETransitionEasing` (12+ built-in curves + `UCurveFloat` custom). The raw `[0,1]` progress is eased before being written to the material's `Progress` parameter.
64+
65+
### Level Transition Pattern
66+
67+
`OpenLevelWithTransition()` uses a two-step delegate chain:
68+
1. Fade-out completes → `OnLevelTransitionFadeOutFinished()` opens the level
69+
2. `OnPostLoadMapWithWorld()` fires → auto-reverse fade-in plays
70+
71+
`PrepareAutoReverseTransition()` stores state so it survives level unload.
72+
73+
### Sequence System (Phase 1, unreleased v1.2.0)
74+
75+
Sequence logic lives inline in `UTransitionManagerSubsystem` with a comment marking it for extraction into a `UTransitionSequencePlayer` in Phase 2. The `bIsDispatchingSequenceStep` flag prevents external `StartTransition()` calls from interrupting internal sequence steps.
76+
77+
## Key Files
78+
79+
```
80+
Plugins/TransitionFX/
81+
├── Source/TransitionFX/
82+
│ ├── Public/
83+
│ │ ├── TransitionManagerSubsystem.h ← all public API + delegates
84+
│ │ ├── TransitionPreset.h ← data asset schema
85+
│ │ ├── ITransitionEffect.h ← effect interface
86+
│ │ ├── TransitionBlueprintLibrary.h ← Blueprint nodes
87+
│ │ ├── TransitionSequence.h ← sequence data asset
88+
│ │ └── TransitionFXConfig.h ← material param name constants
89+
│ └── Private/
90+
│ ├── TransitionManagerSubsystem.cpp ← ~1,000 LOC, core tick loop
91+
│ └── TransitionBlueprintLibrary.cpp ← latent action implementations
92+
├── Source/TransitionFXEditor/
93+
│ ├── Public/TransitionPreviewPanel.h ← editor preview UI
94+
│ └── Private/
95+
│ ├── STransitionPreviewPanel.cpp ← preview panel (800+ LOC)
96+
│ └── GifEncoder.cpp ← GIF89a encoder for docs
97+
└── Content/
98+
├── Data/ ← 26 DA_*.uasset transition presets
99+
├── Materials/ ← M_Transition_*.uasset master materials + instances
100+
└── MaterialFunctions/ ← 9 reusable SDF helper functions
101+
```
102+
103+
## Conventions
104+
105+
- **PCH**: `PCHUsage = UseExplicitOrSharedPCHs` — always include the module's own header explicitly.
106+
- **UPROPERTY Transient**: All runtime state (pool, current effect, audio component) uses `UPROPERTY(Transient)` to avoid serialization.
107+
- **Weak pointers for cache**: `TWeakObjectPtr` for `CachedPlayerController` to handle destruction safely.
108+
- **Category**: All `UFUNCTION`/`UPROPERTY` use `Category = "TransitionFX"` or `"TransitionFX|Sequence"`.
109+
- **No Blueprint-only builds assumption**: The plugin must work for both Blueprint-only and C++ projects; keep all required logic in the Runtime module.
110+
111+
## Platform & Compatibility Notes
112+
113+
- **Win64 only** (DX12 SM6). Console and mobile are untested — SDF effects are GPU-bound.
114+
- **Not compatible with UEFN** (Unreal Editor for Fortnite).
115+
- **PostProcess limitation**: Transitions do not cover UMG/Slate UI layers.
116+
- **Single active transition**: One subsystem per GameInstance; no simultaneous transitions.
117+
- **No multiplayer replication**: Operates locally per client.
118+
119+
## CI/CD
120+
121+
`.github/workflows/release.yml` triggers on version tags (`v*`) to build a distribution ZIP (excluding binaries/intermediates), parse CHANGELOG.md, and publish a GitHub release.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
version https://git-lfs.github.com/spec/v1
2-
oid sha256:d5020cbd4c2ff9c727b3ead625180a79ed02cf69bc99ac01ca16001fc06ee48f
3-
size 116343
2+
oid sha256:b9a6f18b6f744d80dada5355d3041c7950da5d8a4f6d6e5e70b882ea8d267a0f
3+
size 116495
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:2a0ad4dd189c61e83f55bad8c69ebeae1c1d9438326139ff1a42b1ab8b01ccf5
3+
size 1834
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
version https://git-lfs.github.com/spec/v1
2-
oid sha256:3c3e6df04cac4355017c6b460f9191367ee7bba3cbbfa868e779a62c57665e5f
2+
oid sha256:0866cf0701d1d7b732e174fb3e009c284c1b9e5a9e5988261b74ca5f3997e596
33
size 1820
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:f51112d4b979001030eba9850b5e37e82423146af8b4740836edb9a43cf9fa51
3+
size 5413
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
version https://git-lfs.github.com/spec/v1
2-
oid sha256:569f6f3b20fc878b3adf235e3064eb5c3dbfb5ba69d0c94a79866c28a6dfebd0
3-
size 28694
2+
oid sha256:8c6aeb05b2aae4b5087b1818fd0bf493573cb5c61885fe74c654f7bb8c9afde0
3+
size 32347
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:aa0c024b920b087cb545c89255dd5155a0406d6756c8cf039de18d85b24992b1
3+
size 38355

Plugins/TransitionFX/Source/TransitionFX/Private/TransitionBlueprintLibrary.cpp

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "LatentActions.h"
77
#include "TransitionFXConfig.h"
88
#include "TransitionManagerSubsystem.h"
9+
#include "TransitionSequence.h"
910
#include "Engine/Engine.h"
1011
#include "Engine/World.h"
1112
#include "Kismet/GameplayStatics.h"
@@ -127,6 +128,46 @@ class FOpenLevelTransitionLatentAction : public FPendingLatentAction
127128
#endif
128129
};
129130

131+
/**
132+
* Latent action that polls the transition manager and completes when the
133+
* currently playing sequence finishes or the manager becomes invalid.
134+
*/
135+
class FTransitionSequenceLatentAction : public FPendingLatentAction
136+
{
137+
public:
138+
FLatentActionInfo ExecutionFunction;
139+
int32 OutputLink;
140+
FWeakObjectPtr CallbackTarget;
141+
TWeakObjectPtr<UTransitionManagerSubsystem> Manager;
142+
143+
FTransitionSequenceLatentAction(const FLatentActionInfo& InLatentInfo, UTransitionManagerSubsystem* InManager)
144+
: ExecutionFunction(InLatentInfo)
145+
, OutputLink(InLatentInfo.Linkage)
146+
, CallbackTarget(InLatentInfo.CallbackTarget)
147+
, Manager(InManager)
148+
{
149+
}
150+
151+
virtual void UpdateOperation(FLatentResponse& Response) override
152+
{
153+
bool bFinished = true;
154+
155+
if (Manager.IsValid())
156+
{
157+
bFinished = !Manager->IsSequencePlaying();
158+
}
159+
160+
Response.FinishAndTriggerIf(bFinished, ExecutionFunction.ExecutionFunction, OutputLink, CallbackTarget);
161+
}
162+
163+
#if WITH_EDITOR
164+
virtual FString GetDescription() const override
165+
{
166+
return TEXT("Waiting for transition sequence...");
167+
}
168+
#endif
169+
};
170+
130171
/** Starts a transition and registers a latent action that completes when the transition finishes. */
131172
void UTransitionBlueprintLibrary::PlayTransitionAndWait(const UObject* WorldContextObject, UTransitionPreset* Preset, ETransitionMode Mode, float PlaySpeed, bool bInvert, FTransitionParameters OverrideParams, struct FLatentActionInfo LatentInfo)
132173
{
@@ -387,6 +428,45 @@ void UTransitionBlueprintLibrary::OpenLevelWithTransitionAndWait(const UObject*
387428
}
388429
}
389430

431+
/**
432+
* Starts a transition sequence and registers a latent action that completes
433+
* when the sequence finishes. If Sequence is null or has no entries, the
434+
* latent action finishes on the next poll to avoid hanging Blueprints.
435+
*/
436+
void UTransitionBlueprintLibrary::PlaySequenceAndWait(const UObject* WorldContextObject, UTransitionSequence* Sequence, struct FLatentActionInfo LatentInfo)
437+
{
438+
UWorld* World = GEngine ? GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull) : nullptr;
439+
if (!World)
440+
{
441+
return;
442+
}
443+
444+
FLatentActionManager& LatentActionManager = World->GetLatentActionManager();
445+
if (LatentActionManager.FindExistingAction<FTransitionSequenceLatentAction>(LatentInfo.CallbackTarget, LatentInfo.UUID) != nullptr)
446+
{
447+
return;
448+
}
449+
450+
UTransitionManagerSubsystem* Manager = World->GetGameInstance() ? World->GetGameInstance()->GetSubsystem<UTransitionManagerSubsystem>() : nullptr;
451+
452+
if (!Sequence || Sequence->Entries.Num() == 0)
453+
{
454+
UE_LOG(LogTransitionFX, Warning, TEXT("PlaySequenceAndWait: Sequence is null or empty. Finishing immediately."));
455+
// Passing nullptr causes FTransitionSequenceLatentAction::UpdateOperation to return true on first poll.
456+
LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, new FTransitionSequenceLatentAction(LatentInfo, nullptr));
457+
return;
458+
}
459+
460+
if (!Manager)
461+
{
462+
LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, new FTransitionSequenceLatentAction(LatentInfo, nullptr));
463+
return;
464+
}
465+
466+
Manager->PlaySequence(Sequence);
467+
LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, new FTransitionSequenceLatentAction(LatentInfo, Manager));
468+
}
469+
390470
/** Converts duration to play speed, starts the transition, and registers a latent action for completion. */
391471
void UTransitionBlueprintLibrary::PlayTransitionAndWaitWithDuration(const UObject* WorldContextObject, UTransitionPreset* Preset, ETransitionMode Mode, float Duration, bool bInvert, FTransitionParameters OverrideParams, struct FLatentActionInfo LatentInfo)
392472
{

0 commit comments

Comments
 (0)