VerseIsland
Feed Learn Leaderboard Guides Reference Explore Search Docs Verse Library
Sign in
Feed Learn 📖 Codex Leaderboard Guides Reference Explore Search Docs Verse Library
Verse Library › UI
Browse Verse Library

Library

Devices 634
  • Devices171
  • Triggers & Logic118
  • UI & HUD40
  • Audio39
  • Items & Inventory29
  • Events27
  • Scoring & Objectives25
  • Spawning19
  • Classes & Interfaces18
  • Game Modes17
  • Props & Meshes14
  • Variables & Mutability12
  • Player Management11
  • Modules & Structure10
  • Round Flow8
  • Animation6
  • Concurrency6
  • Functions6
  • Getting Started6
  • Teams & Classes6
  • NPCs & AI5
  • Tutorials5
  • VFX & Particles5
  • Arrays & Maps4
  • Cameras4
  • Debugging4
  • Control Flow3
  • Cinematics2
  • Other2
  • Persistence2
  • Scene Graph2
  • Structs & Enums2
  • Building1
  • Physics1
  • Publishing & Islands1
  • Syntax & Types1
  • Vehicles1
  • Weapons1
Verse Language 257
  • Control Flow35
  • Functions26
  • Arrays & Maps25
  • Classes & Interfaces22
  • Variables & Mutability22
  • Concurrency21
  • Syntax & Types18
  • Modules & Structure12
  • Debugging8
  • Devices8
  • Events7
  • Player Management6
  • Structs & Enums6
  • Triggers & Logic6
  • Game Modes5
  • Failure & Options4
  • Scene Graph4
  • UI & HUD4
  • Getting Started3
  • Round Flow3
  • Spawning3
  • VFX & Particles3
  • Reference2
  • Animation1
  • Best Practices1
  • Other1
  • Props & Meshes1
Materials/VFX 154
  • Triggers & Logic27
  • UI & HUD17
  • Devices16
  • VFX & Particles14
  • Props & Meshes11
  • Scoring & Objectives9
  • Game Modes7
  • Items & Inventory7
  • Functions6
  • Arrays & Maps5
  • Animation3
  • Cinematics3
  • Classes & Interfaces3
  • Control Flow3
  • Events3
  • Modules & Structure3
  • Round Flow3
  • Spawning3
  • Getting Started2
  • Scene Graph2
  • Variables & Mutability2
  • Concurrency1
  • Persistence1
  • Player Management1
  • Structs & Enums1
  • Syntax & Types1
General 153
  • Devices22
  • Events20
  • Triggers & Logic20
  • Classes & Interfaces10
  • Variables & Mutability8
  • Round Flow6
  • Scoring & Objectives6
  • UI & HUD6
  • Functions5
  • Player Management5
  • Game Modes4
  • Getting Started4
  • Props & Meshes4
  • Control Flow3
  • Syntax & Types3
  • VFX & Particles3
  • Arrays & Maps2
  • Best Practices2
  • NPCs & AI2
  • Other2
  • Physics2
  • Scene Graph2
  • Spawning2
  • Structs & Enums2
  • Tutorials2
  • Building1
  • Cameras1
  • Cinematics1
  • Failure & Options1
  • Items & Inventory1
  • Teams & Classes1
Player 133
  • Player Management22
  • Teams & Classes19
  • Classes & Interfaces12
  • Devices11
  • Events9
  • Persistence7
  • Triggers & Logic7
  • Arrays & Maps6
  • Functions4
  • Game Modes4
  • Items & Inventory4
  • Round Flow4
  • Debugging3
  • Getting Started3
  • Modules & Structure3
  • Concurrency2
  • Control Flow2
  • Scoring & Objectives2
  • Spawning2
  • Structs & Enums2
  • Physics1
  • Props & Meshes1
  • UI & HUD1
  • Variables & Mutability1
  • Weapons1
Building & Props 131
  • Variables & Mutability15
  • Props & Meshes14
  • Triggers & Logic12
  • Classes & Interfaces10
  • Devices10
  • UI & HUD10
  • Events7
  • Teams & Classes7
  • Animation5
  • Building5
  • Functions5
  • VFX & Particles5
  • Structs & Enums4
  • Control Flow3
  • Game Modes3
  • Items & Inventory3
  • Round Flow3
  • Scene Graph3
  • Arrays & Maps2
  • Concurrency2
  • Reference2
  • Modules & Structure1
NPCs & AI 76
  • NPCs & AI35
  • Spawning7
  • Animation5
  • Variables & Mutability5
  • Classes & Interfaces3
  • Debugging3
  • UI & HUD3
  • Audio2
  • Concurrency2
  • Game Modes2
  • Props & Meshes2
  • Triggers & Logic2
  • Devices1
  • Functions1
  • Getting Started1
  • Reference1
  • VFX & Particles1
Spawning 73
  • Spawning31
  • Events7
  • Concurrency6
  • Player Management6
  • Vehicles5
  • Props & Meshes4
  • Devices3
  • Scene Graph3
  • Items & Inventory2
  • Teams & Classes2
  • Control Flow1
  • Publishing & Islands1
  • Reference1
  • Scoring & Objectives1
Cinematics 71
  • Cameras19
  • Cinematics15
  • Devices11
  • Audio5
  • Animation4
  • Triggers & Logic4
  • Functions2
  • Game Modes2
  • UI & HUD2
  • Classes & Interfaces1
  • Concurrency1
  • Control Flow1
  • Items & Inventory1
  • Modules & Structure1
  • Scene Graph1
  • Templates1
UI 57
  • UI & HUD35
  • Devices5
  • Scoring & Objectives4
  • Classes & Interfaces3
  • Round Flow2
  • Scene Graph2
  • Arrays & Maps1
  • Props & Meshes1
  • Structs & Enums1
  • Templates1
  • Triggers & Logic1
  • Variables & Mutability1
Scene Graph 40
  • Scene Graph9
  • Items & Inventory8
  • Classes & Interfaces3
  • Events3
  • Props & Meshes3
  • Functions2
  • Arrays & Maps1
  • Building1
  • Control Flow1
  • Debugging1
  • Modules & Structure1
  • Player Management1
  • Reference1
  • Structs & Enums1
  • Syntax & Types1
  • Triggers & Logic1
  • UI & HUD1
  • VFX & Particles1
Vehicles 38
  • Spawning11
  • Devices7
  • Vehicles7
  • Props & Meshes4
  • Functions2
  • Game Modes2
  • Failure & Options1
  • Items & Inventory1
  • Reference1
  • Teams & Classes1
  • Tutorials1
Tutorials 37
  • Game Modes6
  • Triggers & Logic5
  • Classes & Interfaces4
  • Events4
  • Props & Meshes3
  • Building2
  • Devices2
  • Items & Inventory2
  • Scoring & Objectives2
  • Animation1
  • Arrays & Maps1
  • Cameras1
  • Concurrency1
  • Physics1
  • Player Management1
  • Structs & Enums1
Weapons 30
  • Game Modes6
  • Weapons6
  • Devices5
  • Items & Inventory3
  • Scoring & Objectives3
  • Spawning2
  • Teams & Classes2
  • Best Practices1
  • Functions1
  • Triggers & Logic1
Items 28
  • Items & Inventory23
  • Devices2
  • Triggers & Logic2
  • Spawning1
Assets 20
  • Modules & Structure6
  • Arrays & Maps3
  • Functions3
  • Scene Graph2
  • Best Practices1
  • Devices1
  • Events1
  • Failure & Options1
  • Physics1
  • Variables & Mutability1
Animation 19
  • Animation10
  • Variables & Mutability4
  • Devices1
  • Player Management1
  • Props & Meshes1
  • Scene Graph1
  • Syntax & Types1
Release Notes 17
  • Classes & Interfaces5
  • Modules & Structure3
  • Concurrency2
  • Control Flow2
  • Variables & Mutability2
  • Debugging1
  • Devices1
  • Items & Inventory1
Input & Controls 16
  • Functions4
  • UI & HUD3
  • Control Flow2
  • Animation1
  • Arrays & Maps1
  • Cinematics1
  • Debugging1
  • Devices1
  • Events1
  • Items & Inventory1
Publishing 8
  • Control Flow5
  • Arrays & Maps1
  • Devices1
  • Syntax & Types1
Audio 7
  • Audio5
  • UI & HUD2
Landscape 7
  • Other4
  • VFX & Particles2
  • Scene Graph1
Persistence 6
  • Structs & Enums2
  • Items & Inventory1
  • Persistence1
  • Publishing & Islands1
  • Variables & Mutability1
Physics 5
  • Functions2
  • Classes & Interfaces1
  • Devices1
  • Syntax & Types1
Creator Economy 3
  • Publishing & Islands2
  • Modules & Structure1
Discovery 3
  • Devices1
  • NPCs & AI1
  • Structs & Enums1
Editor & Project 3
  • Other1
  • Props & Meshes1
  • Tutorials1

EZUI Customizable Options Screen

Module — 2 files
These files compile together (same module folder).
ez_ui_optionsscreen.verse
<#
    A basic options screen suitable for triggering devices via a
    tabbed menu and is easily configured within the creative
    device's custom properties
#>

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }

#imports
using { /UnrealEngine.com/Temporary/UI }
using { /Fortnite.com/UI }
using { /Verse.org/Colors }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /Verse.org/Assets }
using {EZUI}

EZ_OptionsScreen:= class () 
{
    
    EZ_LinkedCreativeDevice<public> : ez_ui_optionsscreen      # a backreference to the device that created this EZUI class
    EZ_LinkedPlayer<public> : player                           # the player linked to this instance of the EZUI class
    EZ_UI_ID<public> : int                                     # used to control what UI is initialised by the class
    EZ_OptionsButtonArray<public> : []EZ_OptionsButton         # all options buttons that form the tabbed menu

    
    EZ_OptionsTabs_HorizonalPos <public>  : type{_X:float where -900.000000 <= _X, _X <= 900.000000}
    EZ_OptionsTabs_VerticalPos <public>  : type{_X:float where -900.000000 <= _X, _X <= 900.000000}
    
    EZ_OptionsTab_Padding <public>  : float
    EZ_OptionsTab_Distribution <public>  : float
    EZ_OptionsTab_Colour <public> : color 
    EZ_OptionsTab_Opacity <public>  : type {_X:float where 0.000000 <= _X, _X <= 1.000000}
    EZ_OptionsTab_OutlineColour <public> : color
    EZ_OptionsTab_LineThickness <public>  : float
    EZ_OptionsTab_OutlineOpacity <public>  : type {_X:float where 0.000000 <= _X, _X <= 1.000000}
    EZ_OptionsTab_Size <public> : vector2 
    
    EZ_SubOptionsPage_HorizonalPos <public>  : type{_X:float where -900.000000 <= _X, _X <= 900.000000}
    EZ_SubOptionsPage_VerticalPos <public>  : type{_X:float where -900.000000 <= _X, _X <= 900.000000}

    EZ_SubOptionsElement_Padding <public>  : float
    EZ_SubOptionsElement_Distribution <public>  : float
    EZ_SubOptionsElement_Colour <public> : color
    EZ_SubOptionsElement_Opacity <public>  : type {_X:float where 0.000000 <= _X, _X <= 1.000000}
    EZ_SubOptionsElement_OutlineColour <public> : color 
    EZ_SubOptionsElement_OutlineOpacity <public>  : type {_X:float where 0.000000 <= _X, _X <= 1.000000}
    EZ_SubOptionsElement_Size <public> : vector2  
    EZ_SubOptionsElement_LineThickness <public>  : float 

    EZ_UIBackground_Colour <public> : color
   
    EZ_OptionsDistribution : float = 0.5
    EZ_SubOptionsPadding : margin = margin{Top :=2.0, Left := 2.0, Right := 2.0, Bottom := 2.0}
    EZ_SubOptionsDistribution : float = 0.25
    
    var IsInitialized<private> : logic = false                  # a lock to prevent recreating the canvas after the first execution of Initialize()
    var MainCanvas<private> : canvas = canvas{}                 # the canvas that holds all the screen elements and will be added to the PlayerUI

    var OptionsButtonSlotArray : []stack_box_slot = array{}     # a list of all slots created for the tabbed menu
    var OptionsButtonsArray : []widget = array{}                # a list of all buttons created for the tabbed menu
    var SubOptionsSlotArray : []stack_box_slot = array{}        # a list of all sub options slots for the currently selected tab
    var SubOptionsElementArray : []widget = array{}             # a list of all sub options elements for the currently selected tab
    var SubOptionsButtonArray : []widget = array{}             # a list of all sub options buttons

    ColorBlock<private> : color_block = color_block
    {
        DefaultColor := color{R := 0.0, G := 0.0, B := 0.0}
        DefaultOpacity := 0.5
        DefaultDesiredSize := vector2{X := 1920.0, Y := 1080.0}
    }

    OptionsButtonsWrapper <private> : stack_box = stack_box { Orientation := orientation.Horizontal, Slots := array{} }  # the tabbed menu bar
    SubOptionsWrapper <private> : stack_box = stack_box { Orientation := orientation.Vertical, Slots := array{} }  # contains the current tab's page elements

    var SubOptionsButtonToTriggerMap <private> : [widget]trigger_device = map{} # suboption button / output trigger
    var OptionsButtonToClassMap <private> : [widget]EZ_OptionsButton = map{} # option button / options class
    var SubOptionsButtonToClassMap <private> : [widget]EZ_SubOptionsUI = map{} # option button / suboptions UI class


    
    # do all the things
    Ini<public>() : void =
        if (IsInitialized?): 
            return     

        if(EZ_UI_ID = 1): 
            BuildOptionsScreen()
            UpdateOptionsButtons()
        
    #.........................


    OpenUI<public>() : void =
        if (PlayerUI := GetPlayerUI[EZ_LinkedPlayer]):
            PlayerUI.AddWidget(GetMainCanvas(), player_ui_slot{InputMode := ui_input_mode.All}) #all canvas and content interactions active
    
    
    # closes specific UI using individual behavior
    CloseUI<public>(PlayerUI: player_ui, ActiveWidget : widget)<suspends> : void =

        if (EZ_UI_ID = 1):
            PlayerUI.RemoveWidget(GetMainCanvas())

    #.........................

    GetMainCanvas<public>() : canvas = MainCanvas 
    GetOptionsButtonsWrapper <public> () : stack_box = OptionsButtonsWrapper
    GetOptionsPageWrapper <public> () : stack_box = SubOptionsWrapper

     #.........................

    CloseUIButtonClick<private>(WidgetEvent : widget_message) : void = 
        if (PlayerUI := GetPlayerUI[WidgetEvent.Player]):
            spawn{CloseUI(PlayerUI,WidgetEvent.Source)}
     #.........................

    OnOptionsButtonClicked <private>(WidgetEvent : widget_message) : void =

        MyOptionsButtonClass := GetOptionsButtonClass(WidgetEvent.Source)

        if (MyOptionsButtonClass.IsExitButton?):
            CloseUIButtonClick(WidgetEvent)
            EZ_LinkedCreativeDevice.CloseUITrigger.Trigger(WidgetEvent.Player)
        else:
            UpdateSubOptionsPage (WidgetEvent.Source)
    
    #.........................

    # handles the creation of the tabbed options menu
    UpdateOptionsButtons <private> () : void =

        for(ThisOptionsButtonClass : EZ_OptionsButtonArray):
            NewButton := button_quiet {DefaultText := EZUI.TextToMsg(ThisOptionsButtonClass.OptionsButtonText)}
            NewButton.SetEnabled(false) # start disabled
            NewButton.OnClick().Subscribe(OnOptionsButtonClicked)
        
            # updates the list of all slots, used to build the UI
            NewButtonSlot := stack_box_slot
            {
                Padding := margin{Top :=EZ_OptionsTab_Padding, Left := EZ_OptionsTab_Padding, Right := EZ_OptionsTab_Padding, Bottom := EZ_OptionsTab_Padding}
                Distribution := option{EZ_OptionsTab_Distribution}
                Widget := overlay:
                    Slots := array: 
                        overlay_slot:
                            Widget := color_block # UI outline
                            {        
                                DefaultColor := EZ_OptionsTab_OutlineColour
                                DefaultOpacity := EZ_OptionsTab_OutlineOpacity
                                DefaultDesiredSize := vector2{X := EZ_OptionsTab_Size.X + (EZ_OptionsTab_LineThickness*2), Y := EZ_OptionsTab_Size.Y + (EZ_OptionsTab_LineThickness*2)}

                            }

                        overlay_slot:
                            Widget := color_block # UI main
                            {        
                                DefaultColor := EZ_OptionsTab_Colour
                                DefaultOpacity := EZ_OptionsTab_Opacity
                                DefaultDesiredSize := EZ_OptionsTab_Size
    
                            }

                        overlay_slot:
                                Widget := NewButton
                                

            }

            set OptionsButtonSlotArray += array{NewButtonSlot}

            # updates the list of all created buttons, used to find the class which created the button as they will share the same index
            set OptionsButtonsArray += array{NewButton}

            if(set OptionsButtonToClassMap[NewButton] = ThisOptionsButtonClass): #instance specific button <> class map

            # adds the new slot and button to the wrapper
            OptionsButtonsWrapper.AddWidget(NewButtonSlot)
        
        ToggleUIInteractions() #enable

        # use first options button as the landing page
        if (DefaultPage := OptionsButtonsArray[0]):
            UpdateSubOptionsPage(DefaultPage)


     #.........................

    # each button has a class which created it, this function takes several steps to display all sub options for the clicked button to create a tabbed menu
    UpdateSubOptionsPage <public> ( ActiveWidget : widget) : void =

        ToggleUIInteractions() # disable

        NewOutputMap : [widget]trigger_device = map{}

        spawn{ClearSubOptionsPage()} # done as async to hopefully speed up the UI
        set SubOptionsButtonToTriggerMap = NewOutputMap # clears old output map

        var NewSubOptionsArray : []EZ_SubOptionsUI = array{}
        for (i := 0..OptionsButtonsArray.Length -1 ): #1. searches the list of all created options buttons to find the one which was clicked
            if (OptionsButtonsArray[i] = ActiveWidget):
                Print("Button found @ {i}")
                if(MyOptionButtonClass := EZ_OptionsButtonArray[i]): #2. since the class that created the button shares the same index we can do a reverse lookup
                   set NewSubOptionsArray = MyOptionButtonClass.SubOptionsUIArray #3. create a new array for the suboptions page from this paired class
               

        # rebuilds the page
        for (ThisSubOption : NewSubOptionsArray):

            NewSubOptionButton := button_quiet{}
            ThisSubOption.SetInitialOptionState(NewSubOptionButton)
            NewSubOptionButton.SetEnabled(false) # start disabled
            NewSubOptionButton.OnClick().Subscribe(ThisSubOption.OnSubOptionsButtonClicked) # each button class instance handles it's own events

            NewSubOptionTextBlock := text_block 
            {
                DefaultText := EZUI.TextToMsg(ThisSubOption.SubOptionText)
                DefaultOverflowPolicy :=text_overflow_policy.Ellipsis
                DefaultTextColor := ThisSubOption.SubOptionTextColour
            }

            <# 
                UI elements within a stackbox don't allow you to resize them, offset or inhert from their 
                parent container so an overlay is needed to control the contents. it also allows for
                more complex multi-widget layouts
            #>
            NewSubOptionsElement := overlay
            {
                Slots := array: 
                    overlay_slot:
                        Widget := color_block # UI outline
                        {        
                            DefaultColor := EZ_SubOptionsElement_OutlineColour
                            DefaultOpacity := EZ_SubOptionsElement_OutlineOpacity
                            DefaultDesiredSize := vector2{X := EZ_SubOptionsElement_Size.X + (EZ_SubOptionsElement_LineThickness*2), Y := EZ_SubOptionsElement_Size.Y + (EZ_SubOptionsElement_LineThickness*2)}

                        }

                    overlay_slot:
                        Widget := color_block # UI main
                        {        
                            DefaultColor := EZ_SubOptionsElement_Colour
                            DefaultOpacity := EZ_SubOptionsElement_Opacity
                            DefaultDesiredSize := EZ_SubOptionsElement_Size
    
                        }

                    overlay_slot:
                        Widget := canvas:
                            Slots := array:
                                canvas_slot
                                {                        
                                    Anchors := anchors{Minimum := vector2{X := 0.5 , Y := 0.5}, Maximum := vector2{X := 0.5, Y := 0.5}}
                                    Offsets := margin{Top := 0.0, Left := -250.0, Right := 0.0, Bottom := 0.0}
                                    Alignment := vector2{X := 0.5, Y := 0.5}
                                    ZOrder := 0
                                    SizeToContent := true
                                    Widget := NewSubOptionTextBlock
                                }

                    overlay_slot:
                        Widget := canvas:
                            Slots := array:
                                canvas_slot
                                {                        
                                    Anchors := anchors{Minimum := vector2{X := 0.5 , Y := 0.5}, Maximum := vector2{X := 0.5, Y := 0.5}}
                                    Offsets := margin{Top := 0.0, Left := 375.0, Right := 0.0, Bottom := 0.0}
                                    Alignment := vector2{X := 0.5, Y := 0.5}
                                    ZOrder := 0
                                    SizeToContent := true
                                    Widget := NewSubOptionButton
                                }
                        
            }
        
            NewSubOptionsSlot := stack_box_slot
            {
                Padding := margin{Top := EZ_SubOptionsElement_Padding, Left := EZ_SubOptionsElement_Padding, Right := EZ_SubOptionsElement_Padding, Bottom := EZ_SubOptionsElement_Padding}
                Distribution := option{EZ_SubOptionsElement_Distribution}
                Widget := NewSubOptionsElement
            }

            set SubOptionsSlotArray += array{NewSubOptionsSlot} # replace the old suboptions slots array

            set SubOptionsElementArray += array{NewSubOptionsElement} # replace the old suboptions element array

            set SubOptionsButtonArray += array{NewSubOptionButton} # replace the old suboptions button array

            if(set SubOptionsButtonToTriggerMap[NewSubOptionButton] = ThisSubOption.OutputTrigger):

            SubOptionsWrapper.AddWidget(NewSubOptionsSlot) # add the new suboptions elements to the wrapper

        ToggleUIInteractions() # enable

    #.........................
    ClearSubOptionsPage () <suspends> : void =
        for (i := 0..SubOptionsElementArray.Length -1 ):      
            if(TargetWidget := SubOptionsElementArray[i]):
                GetOptionsPageWrapper().RemoveWidget(TargetWidget)
                #Print ("Removed Widget at index {i}")

    #.........................

    ToggleUIInteractions() : void =

        # options buttons
        for (b: OptionsButtonsArray):
            IsEnabled := b.IsEnabled()
            if (IsEnabled?):
                b.SetEnabled(false)
            else:
                b.SetEnabled(true)

        # sub options buttons
        for (b : SubOptionsButtonArray):
            IsEnabled := b.IsEnabled()
            if (IsEnabled?):
                b.SetEnabled(false)
            else:
                b.SetEnabled(true)

    #.........................
    GetOptionsButtonClass(TargetWidget : widget) : EZ_OptionsButton =
        var MyClass : EZ_OptionsButton = EZ_OptionsButton{}

        for (Widget-> Class : OptionsButtonToClassMap):
            if(Widget = TargetWidget):
                set MyClass = Class
        
        return MyClass

    GetSubOptionsButtonClass() : EZ_SubOptionsUI=
        x : EZ_SubOptionsUI = EZ_SubOptionsUI{}
        return x

    #.........................


    # Builds a canvas 
    BuildOptionsScreen <private>() : void =

        ColorBlock.SetColor (EZ_UIBackground_Colour)

        set MainCanvas = canvas
        {
            Slots := array
                {
                    canvas_slot #background
                    {
                        Anchors := anchors{Minimum := vector2{X := 0.5 , Y := 0.5}, Maximum := vector2{X := 0.5, Y := 0.5}}
                        Offsets := margin{Top := 0.0, Left := 0.0, Right := 0.0, Bottom := 0.0}
                        Alignment := vector2{X := 0.5, Y := 0.5}
                        ZOrder := 0
                        SizeToContent := true
                        Widget := ColorBlock
                    }

                    canvas_slot #tabbed menu bar wrapper
                    {
                        Anchors := anchors{Minimum := vector2{X := 0.5 , Y := 0.5}, Maximum := vector2{X := 0.5, Y := 0.5}}
                        Offsets := margin{Top := EZ_OptionsTabs_VerticalPos, Left := EZ_OptionsTabs_HorizonalPos, Right := 0.0, Bottom := 0.0}
                        Alignment := vector2{X := 0.5, Y := 0.5}
                        ZOrder := 2
                        SizeToContent := true

                        Widget := stack_box:
                            Orientation := orientation.Horizontal
                            Slots := array:
                                stack_box_slot:
                                    HorizontalAlignment := horizontal_alignment.Center
                                    VerticalAlignment := vertical_alignment.Center
                                    Padding := margin{Top := 0.0, Left := 0.0, Right := 0.0, Bottom := 0.0}
                                    Distribution := option{EZ_OptionsTab_Distribution}
                                    Widget := OptionsButtonsWrapper

                    }
                    canvas_slot # suboptions page wrapper
                    {
                        Anchors := anchors{Minimum := vector2{X := 0.0 , Y := 0.0}, Maximum := vector2{X := 1.0, Y := 1.0}}
                        Offsets := margin{Top := 0.0, Left := 0.0, Right := 0.5, Bottom := 0.0}
                        Alignment := vector2{X := 0.5, Y := 0.5}
                        ZOrder := 1
                        SizeToContent := false

                        Widget := canvas:
                            Slots := array:
                                canvas_slot:
                                    Anchors := anchors{Minimum := vector2{X := 0.0 , Y := 0.0}, Maximum := vector2{X := 1.0, Y := 1.0}}
                                    Offsets := margin{Top := 350.0, Left := 0.0, Right := 0.0, Bottom := 350.0}
                                    Alignment := vector2{X := 0.5, Y := 0.5}
                                    ZOrder := 1
                                    SizeToContent := true

                                    Widget := SubOptionsWrapper
                                      
                    }


                }
        }
        
            set IsInitialized = true # Lock the inialization state to prevent from running this function again
    #.........................

} # END EZ_OptionsScreen------------------------------------------------------------------------------------------------------------------------------------

EZ_OptionsButton := class<concrete>()
{
    @editable var OptionsButtonText : string = "OptionMenuText"
    @editable IsExitButton : logic = false
    @editable SubOptionsUIArray : []EZ_SubOptionsUI = array{}


} # END EZ_OptionsButton ------------------------------------------------------------------------------------------------------------------------------------

EZ_SubOptionsUI := class<concrete>()
{
    @editable var SubOptionsButtonText1 : string = "ButtonText1"
    @editable var SubOptionsButtonText2 : string = "ButtonText2"
    @editable var SubOptionText : string = "SubOptionText"
    @editable var SubOptionTextColour : color =color{R := 0.0, G := 0.0, B := 0.1} 
    @editable OutputTrigger : trigger_device = trigger_device {}
    @editable var IsEnabled : logic = false

    OnSubOptionsButtonClicked (WidgetEvent : widget_message) : void =

            if (ButtonWidget := text_button_base[WidgetEvent.Source]):

                if (IsEnabled?):
                    ButtonWidget.SetText(EZUI.TextToMsg(SubOptionsButtonText2))
                    set IsEnabled = false
                else:
                    ButtonWidget.SetText(EZUI.TextToMsg(SubOptionsButtonText1))
                    set IsEnabled = true

                OutputTrigger.Trigger(WidgetEvent.Player)
    
    SetInitialOptionState ( MyButton : text_button_base) : void =

        if ( IsEnabled ?):
            MyButton.SetText(EZUI.TextToMsg(SubOptionsButtonText1))
        else:
            MyButton.SetText(EZUI.TextToMsg(SubOptionsButtonText2))
    

        


} # END EZ_SubOptionsUI ------------------------------------------------------------------------------------------------------------------------------------

ez_ui_optionsscreen := class(creative_device):

    @editable ShowUITrigger : trigger_device = trigger_device {}
    @editable CloseUITrigger : trigger_device = trigger_device {} #added based upon feedback by jackherrer1113 Nov'23

    @editable OptionsTabs_HorizonalPos : type{_X:float where -900.000000 <= _X, _X <= 900.000000} = 0.0
    @editable OptionsTabs_VerticalPos : type{_X:float where -500.000000 <= _X, _X <= 500.000000} = -250.0
    @editable OptionsTab_Padding : float = 2.0
    @editable OptionsTab_Distribution : type{_X:float where 0.000000 <= _X, _X <= 1.000000} = 0.25
    @editable OptionsTab_Colour : color = color{R := 0.5, G := 0.5, B := 0.7}
    @editable OptionsTab_Opacity : type{_X:float where 0.000000 <= _X, _X <= 1.000000} = 1.0
    @editable OptionsTab_OutlineColour : color = color{R := 0.0, G := 0.0, B := 0.0}
    @editable OptionsTab_LineThickness : float = 5.0
    @editable OptionsTab_OutlineOpacity : type{_X:float where 0.000000 <= _X, _X <= 1.000000} = 0.75
    @editable OptionsTab_Size : vector2 = vector2{X := 250.0, Y := 50.0}
    
    @editable SubOptionsPage_HorizonalPos : type{_X:float where -900.000000 <= _X, _X <= 900.000000} = 0.0
    @editable SubOptionsPage_VerticalPos : type{_X:float where -500.000000 <= _X, _X <= 500.000000} = 0.0
    @editable SubOptionsElement_Padding : float = 2.0
    @editable SubOptionsElement_Distribution : type{_X:float where 0.000000 <= _X, _X <= 1.000000} = 0.25
    @editable SubOptionsElement_Colour : color = color{R := 0.5, G := 0.5, B := 0.65}
    @editable SubOptionsElement_Opacity : type{_X:float where 0.000000 <= _X, _X <= 1.000000} = 1.0
    @editable SubOptionsElement_OutlineColour : color = color{R := 0.0, G := 0.0, B := 0.0}
    @editable SubOptionsElement_OutlineOpacity : type{_X:float where 0.000000 <= _X, _X <= 1.000000} = 0.75
    @editable SubOptionsElement_Size : vector2 = vector2{X := 1000.0, Y := 50.0}
    @editable SubOptionsElement_LineThickness : float = 3.0

    @editable UIBackground_Colour : color =color{R := 0.0, G := 0.0, B := 0.0}

    @editable var OptionsButtonArray : []EZ_OptionsButton = array{}



    # UI regisitration
    var PlayerToUIMap : [player]EZ_OptionsScreen = map{} #[playerdata]/ez_ui class


    OnBegin<override>()<suspends> : void =
        ShowUITrigger.TriggeredEvent.Subscribe(OpenUI)

    # all the steps needed to open the Pop Up
    OpenUI<private>(MaybePlayer : ?agent) : void = 
    {
    
        # check that the event was triggered by a player and stop if it wasn't
        Result := EZUI.IsAPlayer(MaybePlayer,Self)
        if (Result = false):
            return
        #.........................

        var UI_ID : int = 1 
            
        if(PlayerAgent := MaybePlayer?): #set the typing to agent from ?agent

            if ( InteractingPlayer := player[PlayerAgent] ) 
            {   
                var OptPlayerUI :?EZ_OptionsScreen = false
        
                if (ThisPlayerUI := PlayerToUIMap[InteractingPlayer])
                { set OptPlayerUI = option{ThisPlayerUI} }
                    
                else if (ThisPlayerUI := set PlayerToUIMap[InteractingPlayer] = EZ_OptionsScreen

                            #everything that is passed to the EZUI class
                            { 
                                EZ_LinkedPlayer := InteractingPlayer,
                                EZ_LinkedCreativeDevice := Self, 
                                EZ_UI_ID := UI_ID,
                                EZ_OptionsButtonArray := OptionsButtonArray,

                                EZ_OptionsTabs_HorizonalPos := OptionsTabs_HorizonalPos,
                                EZ_OptionsTabs_VerticalPos := OptionsTabs_VerticalPos,

                                EZ_OptionsTab_Padding := OptionsTab_Padding,
                                EZ_OptionsTab_Distribution := OptionsTab_Distribution,
                                EZ_OptionsTab_Colour := OptionsTab_Colour,
                                EZ_OptionsTab_Opacity := OptionsTab_Opacity,
                                EZ_OptionsTab_OutlineColour := OptionsTab_OutlineColour,
                                EZ_OptionsTab_LineThickness := OptionsTab_LineThickness,
                                EZ_OptionsTab_OutlineOpacity := OptionsTab_OutlineOpacity,
                                EZ_OptionsTab_Size := OptionsTab_Size,
                                
                                EZ_SubOptionsPage_HorizonalPos := SubOptionsPage_HorizonalPos,
                                EZ_SubOptionsPage_VerticalPos := SubOptionsPage_VerticalPos,

                                EZ_SubOptionsElement_Padding := SubOptionsElement_Padding,
                                EZ_SubOptionsElement_Distribution := SubOptionsElement_Distribution,
                                EZ_SubOptionsElement_Colour:= SubOptionsElement_Colour,
                                EZ_SubOptionsElement_Opacity := SubOptionsElement_Opacity,
                                EZ_SubOptionsElement_OutlineColour := SubOptionsElement_OutlineColour,
                                EZ_SubOptionsElement_OutlineOpacity := SubOptionsElement_OutlineOpacity,
                                EZ_SubOptionsElement_Size := SubOptionsElement_Size,
                                EZ_SubOptionsElement_LineThickness := SubOptionsElement_LineThickness,
                            
                                EZ_UIBackground_Colour := UIBackground_Colour
                                


    
                            }
                ) 
                    
                { ThisPlayerUI.Ini(), set OptPlayerUI = option{ThisPlayerUI} }
        
                # Try to open the Interface for the player
                if (PlayerUI := OptPlayerUI?):
                    PlayerUI.OpenUI()
                else 
                    { Print("Error when getting saved or creating ThisPlayerUI") }
            }
        #.........................
    }





                    



                    

            
file_2.verse
<#
   This module needs to be used for any EZUI scripts and 
   contains premade features for the framework. Call these
   functions with "EZUI.<function>" from your scripts after
   importing them with "using {EZUI}"
#>

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }

#imports
using { /UnrealEngine.com/Temporary/UI }
using { /Fortnite.com/UI }
using { /Verse.org/Colors }
using { /Fortnite.com/Playspaces }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /Verse.org/Assets }
using { /Fortnite.com/Characters }


EZUI<public> := module:

    # Custom data types  ---------------------------------------------------------------------------------------

    ezui_opacity_range<public> := struct<concrete>{ @editable Value:type{_X:float where 0.000000 <= _X, _X <= 1.000000} = 0.0}
    ezui_anchor_range<public> := struct<concrete>{ @editable Value:type{_X:float where 0.000000 <= _X, _X <= 1.000000} = 0.0}

    # Toolkit ---------------------------------------------------------------------------------------

    <# 
        EZUI.TextToMsg
        comverts a text string to a UI message
    #>
    TextToMsg<public><localizes> (t : string) : message = "{t}" 

    <# 
        EZUI.IntToMsg
        comverts an int to a UI message
    #>
    IntToMsg<public><localizes>(i : int) : message = "{i}"

    <#
        EZUI.IsAPlayer
        used to determine if a TriggerEvent is from a player, or to check if the 
        input is a player, to allow for a UI to be safely referenced
    #>
    IsAPlayer<public>(MaybeAgent : ?agent, VerseDevice : creative_device) : logic =

        if(not MaybeAgent?):
            #Print("EZUI: Not an Agent")
            return false

        if(MaybePlayer := MaybeAgent?):
            for (k -> v : VerseDevice.GetPlayspace().GetPlayers()):
                if (v = player[MaybePlayer]):
                    #Print("EZUI: Is a Player")
                    return true
        
        #Print("EZUI: Not a Player")
        return false
      
    <# 
        EZUI.InsertWidget
        add a specific widget to a specific player's UI
    #>
    InsertWidget<public>(Player : player, NewWidget : widget) : void =
        if (PlayerUI := GetPlayerUI[Player]):
            PlayerUI.AddWidget(NewWidget)
    
    <# 
        EZUI.DeleteWidget
        removes a specific widget from a specific player's UI
    #>
    DeleteWidget<public>(Player : player, Widget : widget) : void =
        if (PlayerUI := GetPlayerUI[Player]):
            PlayerUI.RemoveWidget(Widget)

    <# 
        EZUI.GetMyHUDController
        returns the HUD controller for the requesting verse device's playspace
        and is used to show or hide all or parts of the default Fortnite UI
    #>
    GetMyHUDController <public> (VerseDevice : creative_device) : fort_hud_controller =
        Playspace := VerseDevice.GetPlayspace()
        HUDController := Playspace.GetHUDController()
        return HUDController

    
    <# 
        EZUI.DisablePlayerControls
        EZUI.RestorePlayerControls
        lock player movement while a UI is open and
        restore it when needed
    #>
    DisablePlayerControls <public> (Player : player) : void =
            if(PlayerAgent := agent[Player]):
                PlayerCharacter := PlayerAgent.GetFortCharacter[] or Err("[EZUI] : DisablePlayerControls()  - Should be unfailable")
                PlayerCharacter.PutInStasis(stasis_args{})
    
    RestorePlayerControls <public> (Player : player) : void =
            if(PlayerAgent := agent[Player]):
                PlayerCharacter := PlayerAgent.GetFortCharacter[] or Err("[EZUI] : RestorePlayerControls()  - Should be unfailable")
                PlayerCharacter.ReleaseFromStasis()





    # VFX library ---------------------------------------------------------------------------------

    <#
        EZUI.FlickerVFX
        Used to create a flickering effect on UI elements for a defined
        duration and then ending with one of four ways:
            1 - visible
            2 = invisible
            3 = invisible and collapsed
            4 = removed
    
    #>
    FlickerVFX <public> (ThisWidget : widget, Duration : float, VFXEnd : int, LinkedPlayer : player)<suspends> : void =
        var Countdown : float = (Duration * 10)
        
        loop:
                ThisWidget.SetVisibility(widget_visibility.Hidden)
                Sleep(0.05)
                ThisWidget.SetVisibility(widget_visibility.Visible)
                Sleep(0.05)
                set Countdown = Countdown - 1.0
        
                if(Countdown = 0.0):

                    if (VFXEnd = 1):
                        ThisWidget.SetVisibility(widget_visibility.Visible)
                        
                    if (VFXEnd = 2):
                        ThisWidget.SetVisibility(widget_visibility.Hidden)
                    
                    if (VFXEnd = 3):
                        ThisWidget.SetVisibility(widget_visibility.Collapsed) 

                    if (VFXEnd = 4):
                        if (PlayerUI := GetPlayerUI[LinkedPlayer]):
                            PlayerUI.RemoveWidget(ThisWidget)           
                       
                    break

    <#
        EZUI.FadeVFX
        EZUI.FadeTextVFX
        smoothly modifies the opacity of a color_block over a period
        (in seconds) between two values or does the same with a text_block
    #>
    FadeVFX <public> (TargetWidget : color_block, FadeColor : color, InitialOpacity : float, FinalOpacity : float, FadeDuration : float) <suspends>: void =
        #Print("[EZUI] : FadeInVFX")

        var ElapsedTime : float = 0.0
        var StartTime : float = 0.0

        TargetWidget.SetColor(FadeColor)

        loop:
            CurrentTime := GetSimulationElapsedTime()

            if (StartTime = 0.0):
                set StartTime = CurrentTime

            set ElapsedTime = Min(FadeDuration , CurrentTime - StartTime)
            
            Value := Lerp(InitialOpacity, FinalOpacity, ElapsedTime / FadeDuration )
            OpacityValue: type{_X:float where 0.000000 <= _X, _X <= 1.000000 } = type{_X:float where 0.000000 <= _X, _X <= 1.000000 }[Clamp(Value, 0.0, 1.0)] or Err("Should be unfailable")
            TargetWidget.SetOpacity(OpacityValue)
            #Print("[EZUI] : Widget Opacity is {Value}")

            if (ElapsedTime = FadeDuration):
                break

            Sleep(0.0)

    FadeTextVFX <public> (TargetWidget : text_block, FadeColor : color, InitialOpacity : float, FinalOpacity : float, FadeDuration : float) <suspends>: void =
        #Print("[EZUI] : FadeInVFX")
        
        var ElapsedTime : float = 0.0
        var StartTime : float = 0.0
        
        TargetWidget.SetTextColor(FadeColor)
        
            loop:
                CurrentTime := GetSimulationElapsedTime()
        
                if (StartTime = 0.0):
                    set StartTime = CurrentTime
        
                set ElapsedTime = Min(FadeDuration , CurrentTime - StartTime)
                    
                Value := Lerp(InitialOpacity, FinalOpacity, ElapsedTime / FadeDuration )
                OpacityValue: type{_X:float where 0.000000 <= _X, _X <= 1.000000 } = type{_X:float where 0.000000 <= _X, _X <= 1.000000 }[Clamp(Value, 0.0, 1.0)] or Err("Should be unfailable")
                TargetWidget.SetTextOpacity(OpacityValue)
                #Print("[EZUI] : Widget Opacity is {Value}")
        
                if (ElapsedTime = FadeDuration):
                    break
        
                Sleep(0.0)
    

                    
<#
    a special thamks to: 
        Mayapple
        SprinterMax
        Daigorō
        UEFN Discord community

    ****************************************************************************************
    Made with EZUI, please follow @kryyative on Instagram Threads and @KryyssX on X (Twitter)
    for more useful Snippets, UEFN & UE5 news,tutorials and community spotlights!
    ****************************************************************************************

    If you found EZUI useful please include the text above in your credits to show your support
    for my work and encourage others to use EZUI for their projects. Thank you! ^_^
#>
Sign in to download module

Copy-paste each file above is always free.

Comments

    Sign in to vote, comment, or suggest an edit. Sign in
    📄
    Source
    @float
    Epic Games Community
    @float

    Created by a member of the Epic Games community. Rights remain with the original author, under Epic's community content terms. Terms ↗

    View original Sources & licensing
    Request removal
    Last updated Jun 23, 2026
    Verse module (2 files)
    Compiles · verified Jun 24, 2026

    Download the full module (zip) from the code panel above.

    Keep exploring
    More in UI · 3 of 9
    Prev
    fortnite functions for easier ui creation
    Next
    fortnite eliminated ui
    Related topics
    • Fortnite Ezui Popup With Custom Art Fnc Graphics Support Per Player Ui Ui
    • Fortnite Ezui Customisable Security Keypad With Sfx Audio
    • Fortnite Fully Customizable Ui Button Verse Device Devices
    • Fortnite Ezui Customisable Fadein Out Screen Fx Ui
    • Getting Started With Devices In Fortnite Devices

    Related

    Open in graph →

    Related topics

    • Fortnite Ezui Popup With Custom Art Fnc Graphics Support Per Player Ui
    • Fortnite Ezui Customisable Security Keypad With Sfx
    • Fortnite Fully Customizable Ui Button Verse Device
    • Fortnite Ezui Customisable Fadein Out Screen Fx
    • Getting Started With Devices In Fortnite
    VerseIsland · an archipelago of Verse & UEFN knowledge
    🗺️ Island atlas Learn Guides History About & Press Sources & Licensing Status

    Not affiliated with Epic Games. Fortnite, UEFN, Unreal Engine, and Verse are trademarks of Epic Games, Inc. Content is attributed to its source — see Sources & Licensing.

    🦜

    Scout · your island guide

    The Isle of Verse