Actor Components Incorrectly Appearing in Scene Component Hierarchy in Blueprint Editor

Hi,

We’ve been experiencing the following issue.

Non-scene UActorComponent subclasses created with CreateDefaultSubobject in C++ can incorrectly appear in the scene component hierarchy section of the Blueprint editor’s Components panel, instead of in the Actor Components section below the separator.

The bug is name-dependent:

- Actor components with names alphabetically before the root scene component’s name → incorrectly placed above separator

- Actor components with names alphabetically after the root scene component’s name → correctly placed below separator

Example from our codebase:

- Root scene component: PawnCollision0 (starts with “P”)

- NavigationInvokerComponent (starts with “N”) → appeared above separator (BUG: “N” < “P”)

- FlyComponent (starts with “F”) → appeared below separator (by chance, due to sort algorithm)

Root Cause

The bug is in FFBlueprintEditorDefaultSortUICustomization::SortSubobjectData in BlueprintEditorSCSEditorUICustomization.cpp.

The sort comparator correctly handles the case where A is a scene component and B is not:

 if (A.GetData()->IsSceneComponent())
 {
   if (B.GetData()->IsSceneComponent())
   {
     return A.GetData()->GetDisplayName().CompareTo(B.GetData()->GetDisplayName()) < 0;
   }
   return !B.GetData()->IsActor(); // A (scene) comes before B (non-scene)
 }

However, it does NOT handle the inverse case where A is NOT a scene component but B IS a scene component. Instead, it falls through to alphabetical comparison.

This creates a non-transitive comparator.

When the actor component’s name is alphabetically less than a scene component’s name (e.g., “NavigationInvokerComponent” < “PawnCollision”), the comparator incorrectly returns true, placing the actor component before the scene component.

[Attachment Removed]

Steps to Reproduce
1. Create a C++ Actor class with the following components created via CreateDefaultSubobject:

// In constructor:

// Root scene component - name starts with “S”

SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT(“SphereCollision”));

SetRootComponent(SphereComponent);

// Actor component with name alphabetically BEFORE “SphereCollision” - will show BUG

ActorComponentA = CreateDefaultSubobject<UActorComponent>(TEXT(“AComponentTest”)); // “A” < “S”

// Actor component with name alphabetically AFTER “SphereCollision” - will show correctly

ActorComponentB = CreateDefaultSubobject<UActorComponent>(TEXT(“ZComponentTest”)); // “Z” > “S”

2. Create a Blueprint that inherits from this C++ class

3. Open the Blueprint in the editor and observe the Components panel

Expected: Both AComponentTest and ZComponentTest appear below the separator line, in the “Actor Components” section.

Actual: AComponentTest appears above the separator (in the scene hierarchy section) because “A” < “S” alphabetically. ZComponentTest correctly appears below the separator because “Z” > “S”.

[Attachment Removed]

We fixed it by replacing the sort comparator with one that explicitly handles all cases symmetrically:

  bool FFBlueprintEditorDefaultSortUICustomization::SortSubobjectData(TArray<FSubobjectDataHandle>& SubobjectData)
  {
      SubobjectData.Sort([](const FSubobjectDataHandle& A, const FSubobjectDataHandle& B)
      {
          const FSubobjectData* AData = A.GetData();
          const FSubobjectData* BData = B.GetData();
 
          // If A is a child of B, sort B ahead
          if (AData->GetParentData() == BData)
          {
              return false;
          }
 
          const bool bAIsScene = AData->IsSceneComponent();
          const bool bBIsScene = BData->IsSceneComponent();
          const bool bAIsActor = AData->IsActor();
          const bool bBIsActor = BData->IsActor();
 
          // Actors should always be first
          if (bAIsActor && !bBIsActor)
          {
              return true;
          }
          if (bBIsActor && !bAIsActor)
          {
              return false;
          }
          if (bAIsActor && bBIsActor)
          {
              return AData->GetDisplayName().CompareTo(BData->GetDisplayName()) < 0;
          }
 
          // Scene components should sort ahead of non-scene components
          if (bAIsScene && !bBIsScene)
          {
              return true;
          }
          if (bBIsScene && !bAIsScene)
          {
              return false;  // <-- This was the missing case!
          }
 
          // Both are same type, sort by display name
          return AData->GetDisplayName().CompareTo(BData->GetDisplayName()) < 0;
      });
 
      return true;
  }

Does it make sense? :smiley:

[Attachment Removed]

Hi Mickael,

Thank you for reporting this issue and providing small repro steps. I was able to reproduce this behavior here, and I believe both your assessment and fix are correct.

This bug seems to have been introduced by CL 45871568 (git 40b3f0d) in September. Interestingly, it can be reproduced even without C++, by simply creating a blueprint actor, adding a non-scene component, renaming it to “A” and duplicating it several times. After around 8 duplicates, the sort will start producing visibly inconsistent results.

I have created an internal bug report for this issue. Here’s the tracking number for it: UE-358654. This link should become available once the engine devs mark it as public.

Let me know if you need anything else regarding this topic!

Best regards,

Vitor

[Attachment Removed]