DreamGUI
Reference

UDreamRingMenu

A ring menu whose hierarchy is code, not an asset: wedges around a hub, picked by direction.

Inherits from UDreamUIControl.

Declared in Public/Controls/DreamRingMenu.h.

A ring menu whose hierarchy is code, not an asset: wedges around a hub, picked by direction.

The radial menu every action game has and no UI framework ships, in the DreamGUI idiom. Items are a UPROPERTY array, so .dui, the designer, Blueprint and a <-> binding all drive it with no glue; the look comes from the project sheet like every other control's.

THE WEDGE IS A RECT

There is no new visual and no new shader here. UDreamRectBlock already draws a rounded rect with a border, a radial-fill mask, gradients and shadows -- and an annulus sector is exactly that rect with the corner radius at 100%, the body off (so the BORDER is the ring), and the mask set to the slice. UDreamProgressBar's Radial shape found the first half of this; the second half is that the mask's start and sweep are per-item rather than a percentage. So every wedge inherits gradients, inner and outer shadows and soft edges for free, and a project that wants a fancier ring styles the rect rather than waiting for a feature.

The cost is honest and worth stating: each wedge is a SQUARE the size of the whole ring, of which the shader keeps one slice. Every wedge therefore overlaps every other, and a rect hit test would hand the topmost one the entire circle -- which is why the hit shape is UDreamRingSectorRaycast and not the default.

WHY THE HIT TEST IS A CUSTOM RAYCAST AND NOT A TICK

The obvious build is a control that reads the pointer every frame and works out an angle. This one does not, and the difference is not style: with an exact sector as each wedge's hit shape the whole existing event pipeline just works -- enter, exit, press, click, the selectable's four transitions, event bubbling, the modal layer, the drag threshold. A ticking control would have had to reimplement each of those and would still have been wrong about which of two overlapping menus the pointer was in. The angle maths lives in one place, in the raycast, and the gamepad route (HighlightByDirection) calls the same two static functions rather than a second copy.

The gap between wedges is DRAWN ONLY. Were it in the hit shape too, dragging across one would exit a wedge and enter nothing, and the highlight would blink off between every pair of items.

HIGHLIGHT AND SELECTION ARE TWO THINGS

Highlight is where the pointer (or the stick) is; selection is what has been committed. A menu separates them -- hover, then click -- and a weapon wheel does not, which is one bool (bSelectOnHighlight) rather than two controls. Both are readable, both fire, and OnItemActivated fires on every commit even when the same item is chosen twice, because "invoke this command again" is a thing a menu has to be able to say.

Native.RingMenu Wheel {
    HitArea            = Slice
    bSelectOnHighlight = true
    Style.SweepAngle   = 360
    OnItemActivated -> HandleWheelPick

Properties

NameTypeCategoryIn the details panelFrom BlueprintDescription
StyleFDreamRingMenuStyleRing MenuyesSetStyleThis instance's own look. The project sheet wins while StyleSource says so AND a sheet actually exists; with no sheet in the project this IS the look in effect -- which is why it stays editable instead of being gated on the enum.
ItemsTArray<FDreamRingMenuItem>Ring MenuyesSetItemsThe wheel. Order is clockwise from the style's StartAngle.
SelectedIndexint32Ring MenuyesSetSelectedIndexThe committed item. -1 is none, which is the resting state of a menu nobody has chosen from.
HitAreaEDreamRingHitAreaRing MenuyesGetHitArea / SetHitAreaSee EDreamRingHitArea.
SliceHitRadiusScalefloatRing MenuyesGetSliceHitRadiusScale / SetSliceHitRadiusScaleHow far a Slice reaches, as a multiple of the ring's outer radius. Ignored by Ring.
DeadZoneRadiusfloatRing MenuyesGetDeadZoneRadius / SetDeadZoneRadiusThe radius inside which the POINTER picks nothing, in ring units. Zero (the default) means the style's InnerRadius, which is the hub's edge and the answer nine times in ten; a Slice menu that wants a bigger "let go here to cancel" middle than its hub states one here.
StickDeadZonefloatRing MenuyesSetStickDeadZoneHow far a stick must be pushed before HighlightByDirection picks anything, as the vector's MAGNITUDE -- an axis pair is already normalized, so this is 0 to 1 and has nothing to do with the ring's radii. What stops a resting stick's jitter from choosing an item every frame.
LabelFacingEDreamRingLabelFacingRing MenuyesGetLabelFacing / SetLabelFacingSee EDreamRingLabelFacing.
bSelectOnHighlightboolRing MenuyesSetSelectOnHighlightHighlighting an item commits it, with no click -- the weapon wheel, where the pointer's direction IS the choice and releasing the key is the confirm.
bAllowDeselectboolRing MenuyesSetAllowDeselectClicking the item that is already selected clears the selection instead of re-choosing it.
bShowLabelsboolRing MenuyesGetShowLabels / SetShowLabelsOff leaves the wedges to their icons. A wheel of glyphs is an ordinary thing to want.
bHubFollowsHighlightboolRing MenuyesSetHubFollowsHighlightThe hub shows the highlighted item's label (falling back to the selected one's, then to HubText). Off leaves HubText alone, which is how the hub becomes a static title.
HubTextFTextRing MenuyesSetHubTextWhat the hub says with nothing highlighted. Empty is an empty hub.
WedgeTemplateClassTSubclassOf<UDreamUserWidget>Ring MenuyesSetWedgeTemplateClassA wedge's CONTENT, authored elsewhere: one instance of this class is created inside every wedge widget, filling it, and the built-in label steps aside. The wedge's shape, its hit sector, its highlight and its click stay the control's, so a template only has to draw an item.
RingNodeTObjectPtr<UDreamWidget>Ring Menu-read onlyThe node everything hangs under, and what Open and Close scale and fade.
BackdropNodeTObjectPtr<UDreamWidget>Ring Menu-read onlyThe unbroken ring behind the wedges, filling the gaps between them.
WedgeRootNodeTObjectPtr<UDreamWidget>Ring Menu-read onlyWhat the wedges are children of -- the template among them.
WedgeTemplateNodeTObjectPtr<UDreamWidget>Ring Menu-read onlyThe thing wedges are copied from -- authored once, inactive, never drawn itself. Public because it is half of the "hand it a template" story.
HubNodeTObjectPtr<UDreamWidget>Ring Menu-read onlyThe disc inside the ring.
HubLabelNodeTObjectPtr<UDreamWidget>Ring Menu-read only
WedgeNodesTArray<TObjectPtr<UDreamWidget> >Ring Menu-read onlyThe wedge widgets, parallel to Items.

Functions

FunctionKindDescription
void ActivateHighlighted()callableCommit whatever is highlighted: what a "release the button to choose" input calls.
void Close()callableHide it, clearing the highlight on the way out. A player whose focus is on a wedge -- or went nowhere from the ring -- gets back what they had focused when it opened, before the fade starts.
float GetDeadZoneRadius()pureGet Dead Zone Radius
int32 GetHighlightedIndex()pureGet Highlighted Index
EDreamRingHitArea GetHitArea()pureGet Hit Area
int32 GetItemCount()pureGet Item Count
float GetItemMidAngle(int32 InIndex)pureThe middle of an item's slice, which is where its icon and label ride.
float GetItemStartAngle(int32 InIndex)pureWhere an item's slice begins, in degrees clockwise from twelve.
float GetItemSweepAngle(int32 InIndex)pureHow wide an item's slice is, in degrees -- its share of the style's sweep, by weight.
EDreamRingLabelFacing GetLabelFacing()pureGet Label Facing
int32 GetSelectedIndex()pureGet Selected Index
bool GetShowLabels()pureGet Show Labels
float GetSliceHitRadiusScale()pureGet Slice Hit Radius Scale
UDreamWidget* GetWedgeWidget(int32 InIndex)pureThe wedge standing for an item, or null when there is none.
void HighlightByAngle(float InAngleDegrees)callableHighlight whichever item owns this angle, in degrees clockwise from twelve.
void HighlightByDirection(FVector2D InDirection)callableHighlight whichever item owns this direction -- the gamepad route.
int32 IndexAtAngle(float InAngleDegrees)pureWhich item owns an angle, or -1 when the sweep does not cover it.
bool IsOpen()pureIs Open
void Open()callableShow the ring, scaling and fading it in when there is a world to tween in. Every player's focus is noted first.
void RebuildItems()callableThrow the geometry at the wedges again, growing or shrinking the pool first if the item count moved. Called for you by ApplyStyle -- wedge geometry IS style -- and by SetItems.
void SetAllowDeselect(bool bInAllowDeselect)callableSet Allow Deselect
void SetDeadZoneRadius(float InRadius)callableSet Dead Zone Radius
void SetHighlightedIndex(int32 InIndex)callableMove the highlight. -1, or a disabled item, clears it.
void SetHitArea(EDreamRingHitArea InHitArea)callableThe five setters below each write the field and then make the push that field is read by -- the whole of what a BlueprintSetter buys on this family. The hit-shape trio (HitArea, the slice's reach, the dead zone) is consulted by the raycast override at the moment a pointer arrives rather than baked into anything drawn, so those three need no restyle at all; the two that decide what a wedge SHOWS do.
void SetHubFollowsHighlight(bool bInHubFollowsHighlight)callableWhether the hub shows the highlighted item's label. The hub is re-read at once.
void SetHubText(FText InHubText)callableWhat the hub says when nothing else has a claim on it. The hub is re-read at once.
void SetItems(TArray<FDreamRingMenuItem> InItems)callableReplace the wheel and rebuild it.
void SetLabelFacing(EDreamRingLabelFacing InFacing)callableRe-orients every wedge's label, which is geometry the style push writes.
void SetSelectedIndex(int32 InIndex)callableMoves the selection and fires both selection events. Out of range selects nothing.
void SetSelectedIndexWithoutNotify(int32 InIndex)callableThe same move, silently: for pushing an authored value in, which is not the user choosing.
void SetSelectOnHighlight(bool bInSelectOnHighlight)callableSet Select on Highlight
void SetShowLabels(bool bInShowLabels)callableWakes or sleeps every wedge's label, which the style push decides.
void SetSliceHitRadiusScale(float InScale)callableSet Slice Hit Radius Scale
void SetStickDeadZone(float InStickDeadZone)callableHow far the stick has to be pushed before it points at anything, 0..1. Read on every stick sample.
void SetStyle(FDreamRingMenuStyle InStyle)callableReplace the look and push it.
void SetWedgeTemplateClass(TSubclassOf<UDreamUserWidget> InWedgeTemplateClass)callableThe authored wedge content. Instanced into each wedge at build, so a new class means a rebuild.
void StepHighlight(int32 InDelta)callableStep the highlight round the ring, skipping disabled items. Wraps on a full wheel and stops at the ends of a partial one -- a half-ring has a first and a last item, and pretending otherwise is how a keyboard user ends up somewhere off screen.
void ToggleOpen()callableToggle Open

Events

EventSignatureDescription
OnHighlightChangedvoid DreamRingMenuIndexEvent__DelegateSignature(int32 Index)Where the pointer is. Fires with both routes -- the wedges' hover and HighlightByAngle.
OnSelectionChangedvoid DreamRingMenuIndexEvent__DelegateSignature(int32 Index)The committed item moved.
OnValueChangedBPvoid DreamRingMenuIndexEvent__DelegateSignature(int32 Index)The <-> convention: two-way bindings synthesize their reverse route against this exact name, so a value control carries it alongside its spoken events. Fires with OnSelectionChanged.
OnItemActivatedvoid DreamRingMenuActivatedEvent__DelegateSignature(int32 Index, FName Tag)An item was CHOSEN -- clicked, or confirmed through ActivateHighlighted. Fires every time, including when the choice is the item already selected, because a menu entry is a command and "do it again" has to be sayable. The tag rides along so a handler never switches on an index.
OnWedgeGeneratedvoid DreamRingMenuWedgeEvent__DelegateSignature(int32 Index, UDreamWidget* Wedge)One per wedge, every time it is BOUND to an item. The hook for a consumer whose wedges are richer than an icon over a label but who would rather not author a whole class: everything under the wedge is reachable from here by display name, and anything added under WedgeTemplateNode before the first rebuild rides into every copy.
OnOpenedvoid DreamRingMenuIndexEvent__DelegateSignature(int32 Index)Open and Close, each carrying the SELECTION at that moment -- which is the whole of what a "hold to open, release to choose" input wants back from a close.
OnClosedvoid DreamRingMenuIndexEvent__DelegateSignature(int32 Index)

On this page