DynamicEntryBox with EntryBoxType Radial is stacking Entries if SectorCentralAngle is multiple of 360.0f and DistributeItemsEvenly is enabled.
The licensee has provided an analysis:
On
void SRadialBox::Construct( const FArguments& InArgs )
SectorCentralAngle member is initialized to 0 when it's set to multiples of 360.f in the Designer. This causes entries of a DynamicEntryBox (configured to be of type Radial) to be stacked upon each other when the "DistributeItemsEvenly" option is set and the "SectorCentralAngle" is set to be multiples of 360.f.
They also noticed that there is an inconsistency between initialization and update logic of the member which is causing the issue to not be noticed in the Designer. As, when the property is updated in the Designer the Update logic is used inside `UDynamicEntryBoxBase::SetRadialSettings` which is further invoked from `UDynamicEntryBoxBase::SynchronizeProperties()`.
The licensee suggests a fix: using `FMath::Wrap(Angle, 0.f, 360.f)` inside `SRadialBox::InternalNormalizeAngle`
I've reproduced this issue on every engine version from 5.0 up to 5.7 (CL42864112).
To make the reproduction easier I've attached a repro project on UE5.5. Simply Play In Editor (PIE) and check that all 3 entries are stacked on top of each other.
From scratch: the licensee has provided images that give additional information on how to setup the repro, please refer to those if there's any doubt.
Create and launch a new Unreal Project
Create a New Blueprint Widget and setup the Layout (a Dynamic Entry Box wrapped inside a Canvas Panel) as described in the attached WidgetLayout.png image.
Configure the Dynamic Entry Box as described in the attached WidgetSettings.png image
Set the "Entry Box Type" to be "Radial", along with a sample Entry Widget Class(ex: "AudioKnobLarge") to be a Widget of significant size that is visible on the Viewport.
Enable "Distribute Items Evenly" and set the "Sector Central Angle" to be 180.f in the "Radial Box Settings".
Ensure the Anchor settings of the entry box are set so that the Widgets are visible on the Viewport.
Setup logic to spin up entries in the Event graph as described in CreatingEntriesIntheBox.png
Setup logic to create and add this widget to the HUD inside the Level Blueprint as described in AddingWidgetTotheHUD.png
Launch PIE and confirm that the widget works as shows entries distributed evenly as expected as seen in ValidCase.png
Go back to the widget and now set the "Sector Central Angle" to 360.f. Now when you launch the widget notice that the widgets are stacked upon each other instead of being distributed evenly across a circle as seen in IssueCase.png
Expected result: when PIE, the entries should be evenly spaced
Actual result: when PIE, the entries stack on top of each other
[Inline Frame] UnrealEditor-Slate.dll!SRadialBox::InternalNormalizeAngle(float) Line 165 C++
UnrealEditor-Slate.dll!SRadialBox::Construct(const SRadialBox::FArguments & InArgs) Line 33 C++
[Inline Frame] UnrealEditor-UMG.dll!RequiredArgs::T0RequiredArgs::CallConstruct(SRadialBox *) Line 745 C++
[Inline Frame] UnrealEditor-UMG.dll!TSlateDecl<SRadialBox,RequiredArgs::T0RequiredArgs>::operator<<=(const SRadialBox::FArguments &) Line 975 C++
UnrealEditor-UMG.dll!UDynamicEntryBoxBase::RebuildWidget() Line 284 C++
UnrealEditor-UMG.dll!UWidget::TakeWidget_Private(TFunctionRef<TSharedPtr<SObjectWidget,1> __cdecl(UUserWidget *,TSharedRef<SWidget,1>)> ConstructMethod) Line 985 C++
UnrealEditor-UMG.dll!UWidget::TakeWidget() Line 967 C++
UnrealEditor-UMG.dll!UCanvasPanelSlot::BuildSlot(TSharedRef<SConstraintCanvas,1> Canvas) Line 33 C++
UnrealEditor-UMG.dll!UCanvasPanel::RebuildWidget() Line 59 C++
UnrealEditor-UMG.dll!UWidget::TakeWidget_Private(TFunctionRef<TSharedPtr<SObjectWidget,1> __cdecl(UUserWidget *,TSharedRef<SWidget,1>)> ConstructMethod) Line 985 C++
UnrealEditor-UMG.dll!UWidget::TakeWidget() Line 967 C++
UnrealEditor-UMG.dll!UUserWidget::RebuildWidget() Line 1118 C++
UnrealEditor-UMG.dll!UWidget::TakeWidget_Private(TFunctionRef<TSharedPtr<SObjectWidget,1> __cdecl(UUserWidget *,TSharedRef<SWidget,1>)> ConstructMethod) Line 985 C++
UnrealEditor-UMG.dll!UWidget::TakeWidget() Line 967 C++
UnrealEditor-UMGEditor.dll!FWidgetBlueprintEditorUtils::DrawSWidgetInRenderTargetInternal(UUserWidget * WidgetInstance, FRenderTarget * RenderTarget2D, UTextureRenderTarget2D * TextureRenderTarget, UE::Math::TVector2<double> ThumbnailSize, bool bIsForThumbnail, TOptional<UE::Math::TVector2<double>> ThumbnailCustomSize, EThumbnailPreviewSizeMode ThumbnailSizeMode) Line 2900 C++
[Inline Frame] UnrealEditor-UMGEditor.dll!FWidgetBlueprintEditorUtils::DrawSWidgetInRenderTargetForThumbnail(UUserWidget *) Line 2883 C++
UnrealEditor-UMGEditor.dll!UWidgetBlueprintThumbnailRenderer::Draw(UObject * Object, int X, int Y, unsigned int Width, unsigned int Height, FRenderTarget * RenderTarget, FCanvas * Canvas, bool bAdditionalViewFamily) Line 203 C++
UnrealEditor-UnrealEd.dll!ThumbnailTools::RenderThumbnail(UObject * InObject, const unsigned int InImageWidth, const unsigned int InImageHeight, ThumbnailTools::EThumbnailTextureFlushMode::Type InFlushMode, FTextureRenderTargetResource * InTextureRenderTargetResource, FObjectThumbnail * OutThumbnail) Line 5281 C++
UnrealEditor-UnrealEd.dll!ThumbnailTools::GenerateThumbnailForObjectToSaveToDisk(UObject * InObject) Line 5372 C++
UnrealEditor-UnrealEd.dll!UUnrealEdEngine::GeneratePackageThumbnailsIfRequired(const wchar_t * Str, FOutputDevice & Ar, TArray<FString,TSizedDefaultAllocator<32>> & GeneratedThumbNamesList) Line 453 C++
UnrealEditor-UnrealEd.dll!UUnrealEdEngine::Exec(UWorld * InWorld, const wchar_t * Stream, FOutputDevice & Ar) Line 653 C++
[Inline Frame] UnrealEditor-UnrealEd.dll!SaveAsset(UPackage *) Line 1061 C++
UnrealEditor-UnrealEd.dll!InternalSavePackage(UPackage * PackageToSave, bool bUseDialog, bool & bOutPackageLocallyWritable, FOutputDevice & SaveOutput) Line 3569 C++
UnrealEditor-UnrealEd.dll!InternalPromptForCheckoutAndSave(const TArray<UPackage *,TSizedDefaultAllocator<32>> & FinalSaveList, bool bUseDialog, TArray<UPackage *,TSizedDefaultAllocator<32>> & OutFailedPackages) Line 4216 C++
UnrealEditor-UnrealEd.dll!FEditorFileUtils::PromptForCheckoutAndSave(const TArray<UPackage *,TSizedDefaultAllocator<32>> & InPackages, FEditorFileUtils::FPromptForCheckoutAndSaveParams & InOutParams) Line 4610 C++
UnrealEditor-UnrealEd.dll!FEditorFileUtils::PromptForCheckoutAndSave(const TArray<UPackage *,TSizedDefaultAllocator<32>> & InPackages, bool bCheckDirty, bool bPromptToSave, TArray<UPackage *,TSizedDefaultAllocator<32>> * OutFailedPackages, bool bAlreadyCheckedOut, bool bCanBeDeclined) Line 4689 C++
UnrealEditor-Kismet.dll!FBlueprintCompilationManagerImpl::CompileSynchronouslyImpl(const FBPCompileRequest & Request) Line 386 C++
UnrealEditor-UnrealEd.dll!FKismetEditorUtilities::CompileBlueprint(UBlueprint * BlueprintObj, EBlueprintCompileOptions CompileFlags, FCompilerResultsLog * pResults) Line 777 C++
UnrealEditor-Kismet.dll!FBlueprintEditor::Compile() Line 4173 C++
UnrealEditor-UMGEditor.dll!FWidgetBlueprintEditor::Compile() Line 1590 C++
[Inline Frame] UnrealEditor-Kismet.dll!Invoke(void(FBlueprintEditor::*)()) Line 66 C++
[Inline Frame] UnrealEditor-Kismet.dll!UE::Core::Private::Tuple::TTupleBase<TIntegerSequence<unsigned int>>::ApplyAfter(void(FBlueprintEditor::*)() &) Line 317 C++
UnrealEditor-Kismet.dll!TBaseSPMethodDelegateInstance<0,FBlueprintEditor,1,void __cdecl(void),FDefaultDelegateUserPolicy>::ExecuteIfSafe() Line 299 C++
[Inline Frame] UnrealEditor-Slate.dll!TDelegate<void __cdecl(void),FDefaultDelegateUserPolicy>::ExecuteIfBound() Line 634 C++
UnrealEditor-Slate.dll!FUIAction::Execute() Line 139 C++
UnrealEditor-Slate.dll!FUICommandList::ExecuteAction(const TSharedRef<FUICommandInfo const ,1> InUICommandInfo) Line 117 C++
UnrealEditor-Slate.dll!SToolBarButtonBlock::OnClicked() Line 496 C++
[Inline Frame] UnrealEditor-Slate.dll!Invoke(FReply(SToolBarButtonBlock::*)()) Line 66 C++
[Inline Frame] UnrealEditor-Slate.dll!UE::Core::Private::Tuple::TTupleBase<TIntegerSequence<unsigned int>>::ApplyAfter(FReply(SToolBarButtonBlock::*)() &) Line 317 C++
UnrealEditor-Slate.dll!TBaseSPMethodDelegateInstance<0,SToolBarButtonBlock,1,FReply __cdecl(void),FDefaultDelegateUserPolicy>::Execute() Line 282 C++
[Inline Frame] UnrealEditor-Slate.dll!TDelegate<FReply __cdecl(void),FDefaultDelegateUserPolicy>::Execute() Line 613 C++
UnrealEditor-Slate.dll!SButton::ExecuteOnClick() Line 467 C++
UnrealEditor-Slate.dll!SButton::OnMouseButtonUp(const FGeometry & MyGeometry, const FPointerEvent & MouseEvent) Line 392 C++
UnrealEditor-Slate.dll!FSlateApplication::RoutePointerUpEvent::__l8::<lambda_2>::operator()(const FArrangedWidget & TargetWidget, const FPointerEvent & Event) Line 5346 C++
UnrealEditor-Slate.dll!FEventRouter::Route<FReply,FEventRouter::FToLeafmostPolicy,FPointerEvent,`FSlateApplication::RoutePointerUpEvent'::`8'::<lambda_2>>(FSlateApplication * ThisApplication, FEventRouter::FToLeafmostPolicy RoutingPolicy, FPointerEvent EventCopy, const FSlateApplication::RoutePointerUpEvent::__l8::<lambda_2> & Lambda, ESlateDebuggingInputEvent DebuggingInputEvent) Line 456 C++
UnrealEditor-Slate.dll!FSlateApplication::RoutePointerUpEvent(const FWidgetPath & WidgetsUnderPointer, const FPointerEvent & PointerEvent) Line 5332 C++
UnrealEditor-Slate.dll!FSlateApplication::ProcessMouseButtonUpEvent(const FPointerEvent & MouseEvent) Line 5917 C++
UnrealEditor-Slate.dll!FSlateApplication::OnMouseUp(const EMouseButtons::Type Button, const UE::Math::TVector2<double> CursorPos) Line 5873 C++
UnrealEditor-ApplicationCore.dll!FWindowsApplication::ProcessDeferredMessage(const FDeferredWindowsMessage & DeferredMessage) Line 2271 C++
UnrealEditor-ApplicationCore.dll!FWindowsApplication::DeferMessage(TSharedPtr<FWindowsWindow,1> & NativeWindow, HWND__ * InHWnd, unsigned int InMessage, unsigned __int64 InWParam, __int64 InLParam, int MouseX, int MouseY, unsigned int RawInputFlags) Line 2783 C++
UnrealEditor-ApplicationCore.dll!FWindowsApplication::ProcessMessage(HWND__ * hwnd, unsigned int msg, unsigned __int64 wParam, __int64 lParam) Line 1944 C++
[Inline Frame] UnrealEditor-ApplicationCore.dll!WindowsApplication_WndProc(HWND__ *) Line 939 C++
UnrealEditor-ApplicationCore.dll!FWindowsApplication::AppWndProc(HWND__ * hwnd, unsigned int msg, unsigned __int64 wParam, __int64 lParam) Line 945 C++
[External Code]
[Inline Frame] UnrealEditor-ApplicationCore.dll!WinPumpMessages() Line 116 C++
UnrealEditor-ApplicationCore.dll!FWindowsPlatformApplicationMisc::PumpMessages(bool bFromMainLoop) Line 145 C++
UnrealEditor.exe!FEngineLoop::Tick() Line 5806 C++
[Inline Frame] UnrealEditor.exe!EngineTick() Line 69 C++
UnrealEditor.exe!GuardedMain(const wchar_t * CmdLine) Line 188 C++
UnrealEditor.exe!LaunchWindowsStartup(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * __formal, int nCmdShow, const wchar_t * CmdLine) Line 266 C++
UnrealEditor.exe!WinMain(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * pCmdLine, int nCmdShow) Line 317 C++
[External Code]
There's no existing public thread on this issue, so head over to Questions & Answers just mention UE-290640 in the post.