Subject: [FIXED] Sequencer Event Track Creates Unnecessary Child Overrides for Parent Blueprint Events
Well, I found the issue myself. Because 5 years and Epic, well…
So, when using a Level Sequence to trigger a Blueprint event, if the event is defined in a parent Blueprint and you try to bind to it from a child Blueprint instance, the Sequencer editor doesn’t bind directly to the parent’s event. Instead, it auto-creates a new, empty Custom Event in the child Blueprint with the same name, which then calls the parent function.
This creates unnecessary nodes and makes the Blueprint graph cluttered and confusing, especially when you explicitly want to call the parent’s implementation without an override.
Why It Happens
The root cause is a discrepancy between how the Sequencer editor handles event binding and how the engine resolves function calls at runtime.
- Runtime: At runtime, the engine correctly uses
FindFunctionByName
with EIncludeSuperFlag::IncludeSuper
, so it walks up the inheritance chain and finds the function in the parent class. This is why existing bindings still work even if you delete the child override.
- Editor-Time: The UI logic for binding the event, specifically in
FMovieSceneDirectorBlueprintEndpointCustomization::HandleQuickBindActionSelected
, does not correctly handle the case where the selected function exists only in a parent class. When it fails to find a UK2Node
for the function in the current Blueprint, it falls back to creating a new endpoint, leading to the unwanted child event.
The Fix
The solution involves a two-part change to D:\UnrealEngine\Engine\Source\Editor\MovieSceneTools\Private\MovieSceneDirectorBlueprintEndpointCustomization.cpp
to allow the editor to find and bind to nodes in parent Blueprints.
Step 1: Update HandleQuickBindActionSelected
to Find Parent Nodes
We need to modify this function to explicitly search the parent Blueprint’s graphs for the corresponding node if the selected function is not in the child.
// In FMovieSceneDirectorBlueprintEndpointCustomization::HandleQuickBindActionSelected(...)
// ... after getting FunctionToCall
if (FunctionToCall)
{
// START of new code block
UClass* FunctionOwnerClass = FunctionToCall->GetOwnerClass();
UBlueprint* FunctionBlueprint = Cast<UBlueprint>(FunctionOwnerClass->ClassGeneratedBy);
if (FunctionBlueprint && FunctionBlueprint != Blueprint)
{
// This is a function from a parent Blueprint.
// Find the corresponding UK2Node in the parent's graphs.
UK2Node* ParentNode = nullptr;
for (UEdGraph* Graph : FunctionBlueprint->UbergraphPages)
{
for (UEdGraphNode* Node : Graph->Nodes)
{
UK2Node_CustomEvent* CustomEvent = Cast<UK2Node_CustomEvent>(Node);
if (CustomEvent && CustomEvent->GetFunctionName() == FunctionToCall->GetFName())
{
ParentNode = CustomEvent;
break;
}
}
if (ParentNode) break;
}
if (!ParentNode)
{
for (UEdGraph* Graph : FunctionBlueprint->FunctionGraphs)
{
if (Graph->GetFName() == FunctionToCall->GetFName())
{
for (UEdGraphNode* Node : Graph->Nodes)
{
if (UK2Node_FunctionEntry* FunctionEntry = Cast<UK2Node_FunctionEntry>(Node))
{
ParentNode = FunctionEntry;
break;
}
}
}
if (ParentNode) break;
}
}
if (ParentNode)
{
// We found the node in the parent. Pass it to SetEndpoint.
SetEndpoint(EndpointDefinition, ParentNode, ParentNode, EAutoCreatePayload::Variables);
return; // We are done.
}
}
// END of new code block
// Original code continues here...
UClass* OuterClass = CastChecked<UClass>(FunctionToCall->GetOuter());
if (OuterClass->ClassGeneratedBy == Blueprint && Blueprint->SkeletonGeneratedClass)
{
// ...
Step 2: Relax the Blueprint Check in SetEndpoint
The SetEndpoint
function has a check that ensures the node being bound belongs to the same Blueprint as the sequence. We need to relax this to allow a parent’s node to be bound.
// In FMovieSceneDirectorBlueprintEndpointCustomization::SetEndpoint(...)
// Find this line:
if (!ensureAlwaysMsgf(
Pair.Value.Blueprint == Blueprint && Pair.Key == Sequence,
TEXT("Attempting to assign an endpoint to objects with different Sequence Director Blueprints.")))
// And change it to this:
if (!ensureAlwaysMsgf(
(Pair.Value.Blueprint == Blueprint || (Blueprint && Pair.Value.Blueprint->ParentClass && Pair.Value.Blueprint->ParentClass->IsChildOf(Blueprint->GeneratedClass))) && Pair.Key == Sequence,
TEXT("Attempting to assign an endpoint to objects with different Sequence Director Blueprints.")))
After making these changes in your engine source code, a rebuild is necessary. Once rebuilt, the Sequencer Event Track will correctly bind to inherited events without creating unnecessary nodes.
Peace 