UEFN Verse言語 メタバース

【UEFN】UIボタンの作り方を画像付きで徹底解説【Verse言語】

【UEFN】UIボタンの作り方を画像付きで徹底解説【Verse言語】
みなみ

UIボタンの作り方について教えてください!

りゅう

初心者向けにわかりやすく解説してほしいですね!

こんなお悩みを解決します。

 本記事の内容
 本記事の信頼性

この記事を書いている僕は、クリエイター兼ブロガーです。実際に現在進行形でUEFNを学んでいます。

今回は、UIボタンの作り方について解説します。

なお、当記事の内容は、DragonRage様の動画を参考にさせていただきました。

DragonRage様、ありがとうございます。)

※以下のコードの解説文は、プログラミング初心者の主が調べながら記述したものです。

コードの解釈に誤りがありましたら恐縮ですm(_ _)m

参考程度にどうぞ。

それでは、さっそくやっていきましょう。

あすか

UIボタンの作り方

※音量にご注意ください。

今回は上記の動画のようなボタンUIを作成していきます。

▼ まず、UEFNを開き、「Verse Explorer」をクリックします。

UIボタンの作り方 1
※クリックすると拡大表示されます。

▼ 青枠部分を右クリックし、「Add new Verse file to project」を選択します。

UIボタンの作り方 2

▼ デバイスネームに『menumanagerdevice』と入力し、「作成」をクリックします。

UIボタンの作り方 3

▼ Verse Explorerの青枠部分「menumanagerdevice.verse」をダブルクリックしましょう。

UIボタンの作り方 4

▼ コードを入力します。

UIボタンの作り方 5
 完全なスクリプトです。
using { /Fortnite.com/Devices }
using { /Fortnite.com/UI }
using { /Verse.org/Simulation }
using { /Verse.org/Verse }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /UnrealEngine.com/Temporary/UI }
using { /UnrealEngine.com/Temporary/SpatialMath }

# Locks the choice of button styles to the current three: {button_loud = 1, button_quiet = 2, button_regular = 3}
buttonchoice_int := type{_X : int where 1 <= _X, 3 >= _X}

menumanagerdevice := class(creative_device):

    # The trigger that will open up this ui menu
    @editable TriggerToShowUI : trigger_device = trigger_device{}

    # An array of button ui, configurable via the editor
    @editable var ButtonUI : []buttonui = array{}

    var MaybeMyUIPerPlayer : [player]?canvas = map{}

    StringToMessage<localizes>(String : string):message = "{String}"
    
    OnBegin<override>()<suspends>:void=
        InitButtonFunctions()
        TriggerToShowUI.TriggeredEvent.Subscribe(OpenUI)

    OpenUI(Agent : ?agent):void=
        if(ValidAgent := Agent?, Player := player[ValidAgent], PlayerUI := GetPlayerUI[Player]):
            if(MyUI := MaybeMyUIPerPlayer[Player]?):
                PlayerUI.RemoveWidget(MyUI)
                if(set MaybeMyUIPerPlayer[Player] = false) {}
            else:
                NewUI := CreateMyUI(ValidAgent)
                PlayerUI.AddWidget(NewUI, player_ui_slot{InputMode := ui_input_mode.All})
                if(set MaybeMyUIPerPlayer[Player] = option{NewUI}) {}

    CreateMyUI(Agent : agent) : canvas = 
        for(CurrButtonUI : ButtonUI):
            case(CurrButtonUI.ButtonStyle):
                1 => 
                    ThisButtonUI := button_loud{DefaultText := StringToMessage(CurrButtonUI.ButtonText)}
                    ThisButtonUI.OnClick().Subscribe(HandleSelectedUIButton)
                    if(set CurrButtonUI.ButtonWidgetPerPlayer[Agent] = ThisButtonUI){}
                2 =>
                    ThisButtonUI := button_quiet{DefaultText := StringToMessage(CurrButtonUI.ButtonText)}
                    ThisButtonUI.OnClick().Subscribe(HandleSelectedUIButton)
                    if(set CurrButtonUI.ButtonWidgetPerPlayer[Agent] = ThisButtonUI){}
                3 =>
                    ThisButtonUI := button_regular{DefaultText := StringToMessage(CurrButtonUI.ButtonText)}
                    ThisButtonUI.OnClick().Subscribe(HandleSelectedUIButton)
                    if(set CurrButtonUI.ButtonWidgetPerPlayer[Agent] = ThisButtonUI){}
                _ => 
                    Print("~ERROR: ButtonStyle integer over/under-flow")
        CanvasArray := ReturnCanvasSlots(Agent)
        MyInteractableButtons : canvas = canvas:
            Slots := CanvasArray
        return MyInteractableButtons

    ReturnCanvasSlots(Agent : agent):[]canvas_slot=
        var CanvasSlotArray : []canvas_slot = array{}
        for(ThisButtonUI : ButtonUI):
            if(ThisWidget := ThisButtonUI.ButtonWidgetPerPlayer[Agent]):
                Agent.LoadButtonState(ThisButtonUI)
                ThisSlot := canvas_slot:
                    Offsets := margin{Top := ThisButtonUI.ButtonPosition.Y, Left := ThisButtonUI.ButtonPosition.X, Bottom := ThisButtonUI.ButtonSize.Y, Right := ThisButtonUI.ButtonSize.X}
                    ZOrder := 0
                    SizeToContent := ThisButtonUI.SizeToContent
                    Widget := ThisWidget
                set CanvasSlotArray += array{ThisSlot}
        return CanvasSlotArray

    DeleteUI(Message : widget_message) : void=
        if(PlayerUI := GetPlayerUI[Message.Player], MyUI := MaybeMyUIPerPlayer[Message.Player]?):
            PlayerUI.RemoveWidget(MyUI)
            if(set MaybeMyUIPerPlayer[Message.Player] = false) {}
                
    HandleSelectedUIButton(Message : widget_message):void=
        if(PlayerUI := GetPlayerUI[Message.Player], MyUI := MaybeMyUIPerPlayer[Message.Player]?):
            Player := Message.Player
            ThisWidget := Message.Source
            StaleButtonUI := Player.FindButtonUI(ThisWidget)
            if(ThisButtonUI := StaleButtonUI?):
                if(ThisButtonUI.RemovesUIOnClick?):
                    DeleteUI(Message)
                ThisButtonUI.ActivateTrigger(Player)
            else:
                Print("~ERROR: No buttonui found!")
                
    (Agent : agent).FindButtonUI(ThisWidget : widget):?buttonui=
        for(ThisButtonUI : ButtonUI):
            if(StaleWidget := ThisButtonUI.ButtonWidgetPerPlayer[Agent]):
                if(ThisWidget = StaleWidget):
                    return option{ThisButtonUI}
        return false

    (ThisButtonUI : buttonui).ActivateTrigger(Player : player):void=
        ThisSignalDestination := ThisButtonUI.ButtonSignal_Send
        if(ThisSignalDestination.SendInstigator?):
            ThisSignalDestination.TriggerToActivate.Trigger(Player)
        ThisSignalDestination.TriggerToActivate.Trigger()

    # Loads all button states that have 'SaveButtonState' turned on
    (Agent : agent).LoadButtonState(ThisButtonUI : buttonui):void=
        if(ThisButtonUI.SaveButtonState?):
            if(ThisWidget := ThisButtonUI.ButtonWidgetPerPlayer[Agent]):
                if(ThisEnabledState := ThisButtonUI.EnabledStateMap[Agent]):
                    ThisWidget.SetEnabled(ThisEnabledState)
                if(ThisVisibleState := ThisButtonUI.VisibleStateMap[Agent]):
                    case(ThisVisibleState):
                        true => ThisWidget.SetVisibility(widget_visibility.Visible)
                        false => ThisWidget.SetVisibility(widget_visibility.Collapsed)
                        _ => Print("~ERROR: Failed to fetch 'ThisVisibleState")
            else:
                Print("~ERROR: Failed to fetch widget!")

    # Initializes the 'functions' tab for each button ui
    InitButtonFunctions():void=
        for(ThisButtonUI : ButtonUI):
            ThisButtonUI.ButtonSignal_Recieve.EnableButtonOnTrigger.TriggeredEvent.Subscribe(ThisButtonUI.EnableWidget) 
            ThisButtonUI.ButtonSignal_Recieve.DisableButtonOnTrigger.TriggeredEvent.Subscribe(ThisButtonUI.DisableWidget) 
            ThisButtonUI.ButtonSignal_Recieve.ShowButtonOnTrigger.TriggeredEvent.Subscribe(ThisButtonUI.ShowWidget)
            ThisButtonUI.ButtonSignal_Recieve.HideButtonOnTrigger.TriggeredEvent.Subscribe(ThisButtonUI.HideWidget)

buttonui := class<concrete>():

    @editable var ButtonText : string = ""
    @editable var ButtonPosition : vector2 = vector2{}
    @editable var ButtonSize : vector2 = vector2{X := 475.0, Y:= 80.0}

    # 'ButtonStyle' key: {button_loud = 1, button_quiet = 2, button_regular = 3}
    @editable var ButtonStyle : buttonchoice_int = 3

    @editable var RemovesUIOnClick : logic = true

    # Saves the state the button is in even after exiting. Example: Hiding a button, exiting the ui, and then opening the ui will still have that button hidden
    @editable SaveButtonState : logic = false
    
    @editable ButtonSignal_Recieve : ButtonUI_UserOptions_Functions = ButtonUI_UserOptions_Functions{}
    @editable ButtonSignal_Send : ButtonUI_UserOptions_Events = ButtonUI_UserOptions_Events{}

    var SizeToContent : logic = false

    var ButtonWidgetPerPlayer : [agent]widget = map{}

    var EnabledStateMap : [agent]logic = map{}
    var VisibleStateMap : [agent]logic = map{}

    EnableWidget(Agent : ?agent):void=
        if(ValidAgent := Agent?, ThisButtonWidget := ButtonWidgetPerPlayer[ValidAgent]):
            ThisButtonWidget.SetEnabled(true)
            if(SaveButtonState?):
                ValidAgent.SaveEnabledState(true)

    # Fades the button ui
    DisableWidget(Agent : ?agent):void=
        if(ValidAgent := Agent?, ThisButtonWidget := ButtonWidgetPerPlayer[ValidAgent]):
            ThisButtonWidget.SetEnabled(false)
            if(SaveButtonState?):
                ValidAgent.SaveEnabledState(false)

    # Saves the enabled state the button is currently in
    (Agent : agent).SaveEnabledState(State : logic):void=
        if(CurrentState := EnabledStateMap[Agent]):
            if(set EnabledStateMap[Agent] = State){}
        else:
            set EnabledStateMap = ConcatenateMaps(EnabledStateMap, map{Agent => State})

    ShowWidget(Agent : ?agent):void=
        if(ValidAgent := Agent?, ThisButtonWidget := ButtonWidgetPerPlayer[ValidAgent]):
            ThisButtonWidget.SetVisibility(widget_visibility.Visible)
            if(SaveButtonState?):
                ValidAgent.SaveVisibleState(true)

    # Completely removes the button ui
    HideWidget(Agent : ?agent):void=
        if(ValidAgent := Agent?, ThisButtonWidget := ButtonWidgetPerPlayer[ValidAgent]):
            ThisButtonWidget.SetVisibility(widget_visibility.Collapsed)
            if(SaveButtonState?):
                ValidAgent.SaveVisibleState(false)

    # Saves the visible state the button is currently in
    (Agent : agent).SaveVisibleState(State : logic):void=
        if(CurrentState := VisibleStateMap[Agent]):
            if(set VisibleStateMap[Agent] = State){}
        else:
            set VisibleStateMap = ConcatenateMaps(VisibleStateMap, map{Agent => State})

ButtonUI_UserOptions_Functions := class<concrete>():

    # The trigger that will enable this ui button, making it 'un-fade' and clickable
    @editable EnableButtonOnTrigger : trigger_device = trigger_device{}

    # The trigger that will disable this ui button, making it 'fade' and unclickable
    @editable DisableButtonOnTrigger : trigger_device = trigger_device{}

    # The trigger that will show this ui button, making it visible and clickable
    @editable ShowButtonOnTrigger : trigger_device = trigger_device{}

    # The trigger that will hide this ui button, making it not visible at all and therefore unclickable
    @editable HideButtonOnTrigger : trigger_device = trigger_device{}

ButtonUI_UserOptions_Events := class<concrete>():

    # The trigger/destination to activate
    @editable TriggerToActivate : trigger_device  = trigger_device{}

    # Do we send the instigator of the UI button?
    @editable SendInstigator : logic = true

▼ コードを入力したら、画面上部の「Verse」→「Verseコードをビルド」をクリックします。

UIボタンの作り方 30

▼ 「コンテンツドロワー」→「○○コンテンツ」→「Creative Devices」を選択します。

UIボタンの作り方 6

▼ 青枠のVerseクラスを世界に設置します。

UIボタンの作り方 7

▼ こんな感じ。

UIボタンの作り方 8

▼ 再び「コンテンツドロワー」を開き、「All」→「トリガー」で、トリガーを設置しましょう。

UIボタンの作り方 9

▼ トリガーを設置したら、デバイス(青枠の機械)をクリックし、画面右下「Menumanagerdevice」の「TriggerToShowUI」の「なし」を『トリガー』に変更します。

UIボタンの作り方 10

※赤枠部分が表示されていない方は、再度Verseコードをビルドしてください。

UIボタンの作り方 11

▼ 「ButtonUI」の『+』ボタンをクリックし、「インデックス」と書かれた部分の左にある「▶」をクリックします。

UIボタンの作り方 12

▼ ButtonTextに、表示させたい文字を入力しましょう。

UIボタンの作り方 13

▼ そしたら次は、再度「コンテンツドロワー」を開き、「○○コンテンツ」をクリック。

コンテンツ内ならどこでもいいので(例:4アイテムの上あたりとかどこでもOK)右クリックしましょう。

そしたら画面に表示されてるバーが表示されるので『ユーザーインターフェース』→『ウィジェットブループリント』をクリックします。

UIボタンの作り方 14

▼ 「User Widget」を選択します。

UIボタンの作り方 15

▼ 名前を付けましょう。なんでもOKです。

(例として「test」という名前を付けました。)

UIボタンの作り方 16

▼ 名前を入力したら、ダブルクリックします。

UIボタンの作り方 17

▼ 以下のような画面が表示されたら、『Canvas Panel』を矢印部分に配置します。

UIボタンの作り方 18

▼ 続いて、「UEFN Button Loud」「UEFN Button Quiet」「UEFN Button Regular」を設置しましょう。

UIボタンの作り方 19

▼ 設置したい位置を決めたら、画面右の「詳細」に表示されている『位置X,Y』『サイズX,Y』を先ほどのデバイスの設定画面『ButtonPosition』『ButtonSize』にコピペします。

コピペする前に必ずコンパイルしておきましょう。

UIボタンの作り方 20
※クリックすると拡大表示されます。

▼ 「コンテンツドロワー」→「All」をクリックしたら、『トリガー』&『アイテムグランター』を設置しましょう。

UIボタンの作り方 21

▼ アイテムグランターの武器は何でもOKです。

UIボタンの作り方 22

▼ アイテムグランターのユーザーオプションの「アイテムを付与する」を、『トリガー2』『On Triggered』に変更します。

UIボタンの作り方 23

▼ 再びデバイスの設定に戻り、ButtonSignal_Recieveの「HideButtoOnTrigger(非表示)」を『トリガー2』に。

ButtonSignal_Sendの「TriggerToActivate(関連するトリガーのアクティブ化)」を『トリガー2』に変更します。

下記の画像と同じ設定であれば問題ないです。

UIボタンの作り方 24

▼ そしたら、ButtonUIの『+』ボタンを押し、「インデックス1」を作りましょう。

インデックス1はもう一つのボタンの担うものです。

UIボタンの作り方 25

▼ もう片方のボタンを選択し、先ほどと同じように『位置X,Y』『サイズX,Y』をデバイスのインデックス1「ButtonPosition」「ButtoSize」にコピペします。

「RemovesUIOnClick」に『✓』を入れましょう。

※ButtonStyle「1が黄色」「2が透明」「3が青色」

UIボタンの作り方 26

▼ 3つ目のトリガーを設置しましょう。

UIボタンの作り方 27

▼ デバイスの設定に戻り、ButtonSignal_Recieveの「DisableButtonOnTrigger(無効化)」を『トリガー3』。

ButtonSignal_Sendの「TriggerToActivate(関連するトリガーのアクティブ化)」をトリガー3に設定します。

以下の画像の設定と同じであれば問題ないです。

UIボタンの作り方 28

このDisableButtonOnTriggerを設定しない限りボタンUIは無効にならないため、フリーズ等のバグが生じます。

あすか

▼ 『セッションを開始』をクリックして試してみましょう。

UIボタンの作り方 29

▼ 以下のようにボタンUIが表示されたら無事完了です。

【UEFN】UIボタンの作り方を画像付きで徹底解説【Verse言語】
 アレンジするとこんなのも作れます。
※音量にご注意ください。
 DragonRage様のYouTube
※音量にご注意ください。
 僕のTwitterアカウントです。

お疲れさまでした🍵

あすか

UIボタンの作り方│Verseコードを徹底解説

 完全なスクリプトです。
using { /Fortnite.com/Devices }
using { /Fortnite.com/UI }
using { /Verse.org/Simulation }
using { /Verse.org/Verse }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /UnrealEngine.com/Temporary/UI }
using { /UnrealEngine.com/Temporary/SpatialMath }

# Locks the choice of button styles to the current three: {button_loud = 1, button_quiet = 2, button_regular = 3}
buttonchoice_int := type{_X : int where 1 <= _X, 3 >= _X}

menumanagerdevice := class(creative_device):

    # The trigger that will open up this ui menu
    @editable TriggerToShowUI : trigger_device = trigger_device{}

    # An array of button ui, configurable via the editor
    @editable var ButtonUI : []buttonui = array{}

    var MaybeMyUIPerPlayer : [player]?canvas = map{}

    StringToMessage<localizes>(String : string):message = "{String}"
    
    OnBegin<override>()<suspends>:void=
        InitButtonFunctions()
        TriggerToShowUI.TriggeredEvent.Subscribe(OpenUI)

    OpenUI(Agent : ?agent):void=
        if(ValidAgent := Agent?, Player := player[ValidAgent], PlayerUI := GetPlayerUI[Player]):
            if(MyUI := MaybeMyUIPerPlayer[Player]?):
                PlayerUI.RemoveWidget(MyUI)
                if(set MaybeMyUIPerPlayer[Player] = false) {}
            else:
                NewUI := CreateMyUI(ValidAgent)
                PlayerUI.AddWidget(NewUI, player_ui_slot{InputMode := ui_input_mode.All})
                if(set MaybeMyUIPerPlayer[Player] = option{NewUI}) {}

    CreateMyUI(Agent : agent) : canvas = 
        for(CurrButtonUI : ButtonUI):
            case(CurrButtonUI.ButtonStyle):
                1 => 
                    ThisButtonUI := button_loud{DefaultText := StringToMessage(CurrButtonUI.ButtonText)}
                    ThisButtonUI.OnClick().Subscribe(HandleSelectedUIButton)
                    if(set CurrButtonUI.ButtonWidgetPerPlayer[Agent] = ThisButtonUI){}
                2 =>
                    ThisButtonUI := button_quiet{DefaultText := StringToMessage(CurrButtonUI.ButtonText)}
                    ThisButtonUI.OnClick().Subscribe(HandleSelectedUIButton)
                    if(set CurrButtonUI.ButtonWidgetPerPlayer[Agent] = ThisButtonUI){}
                3 =>
                    ThisButtonUI := button_regular{DefaultText := StringToMessage(CurrButtonUI.ButtonText)}
                    ThisButtonUI.OnClick().Subscribe(HandleSelectedUIButton)
                    if(set CurrButtonUI.ButtonWidgetPerPlayer[Agent] = ThisButtonUI){}
                _ => 
                    Print("~ERROR: ButtonStyle integer over/under-flow")
        CanvasArray := ReturnCanvasSlots(Agent)
        MyInteractableButtons : canvas = canvas:
            Slots := CanvasArray
        return MyInteractableButtons

    ReturnCanvasSlots(Agent : agent):[]canvas_slot=
        var CanvasSlotArray : []canvas_slot = array{}
        for(ThisButtonUI : ButtonUI):
            if(ThisWidget := ThisButtonUI.ButtonWidgetPerPlayer[Agent]):
                Agent.LoadButtonState(ThisButtonUI)
                ThisSlot := canvas_slot:
                    Offsets := margin{Top := ThisButtonUI.ButtonPosition.Y, Left := ThisButtonUI.ButtonPosition.X, Bottom := ThisButtonUI.ButtonSize.Y, Right := ThisButtonUI.ButtonSize.X}
                    ZOrder := 0
                    SizeToContent := ThisButtonUI.SizeToContent
                    Widget := ThisWidget
                set CanvasSlotArray += array{ThisSlot}
        return CanvasSlotArray

    DeleteUI(Message : widget_message) : void=
        if(PlayerUI := GetPlayerUI[Message.Player], MyUI := MaybeMyUIPerPlayer[Message.Player]?):
            PlayerUI.RemoveWidget(MyUI)
            if(set MaybeMyUIPerPlayer[Message.Player] = false) {}
                
    HandleSelectedUIButton(Message : widget_message):void=
        if(PlayerUI := GetPlayerUI[Message.Player], MyUI := MaybeMyUIPerPlayer[Message.Player]?):
            Player := Message.Player
            ThisWidget := Message.Source
            StaleButtonUI := Player.FindButtonUI(ThisWidget)
            if(ThisButtonUI := StaleButtonUI?):
                if(ThisButtonUI.RemovesUIOnClick?):
                    DeleteUI(Message)
                ThisButtonUI.ActivateTrigger(Player)
            else:
                Print("~ERROR: No buttonui found!")
                
    (Agent : agent).FindButtonUI(ThisWidget : widget):?buttonui=
        for(ThisButtonUI : ButtonUI):
            if(StaleWidget := ThisButtonUI.ButtonWidgetPerPlayer[Agent]):
                if(ThisWidget = StaleWidget):
                    return option{ThisButtonUI}
        return false

    (ThisButtonUI : buttonui).ActivateTrigger(Player : player):void=
        ThisSignalDestination := ThisButtonUI.ButtonSignal_Send
        if(ThisSignalDestination.SendInstigator?):
            ThisSignalDestination.TriggerToActivate.Trigger(Player)
        ThisSignalDestination.TriggerToActivate.Trigger()

    # Loads all button states that have 'SaveButtonState' turned on
    (Agent : agent).LoadButtonState(ThisButtonUI : buttonui):void=
        if(ThisButtonUI.SaveButtonState?):
            if(ThisWidget := ThisButtonUI.ButtonWidgetPerPlayer[Agent]):
                if(ThisEnabledState := ThisButtonUI.EnabledStateMap[Agent]):
                    ThisWidget.SetEnabled(ThisEnabledState)
                if(ThisVisibleState := ThisButtonUI.VisibleStateMap[Agent]):
                    case(ThisVisibleState):
                        true => ThisWidget.SetVisibility(widget_visibility.Visible)
                        false => ThisWidget.SetVisibility(widget_visibility.Collapsed)
                        _ => Print("~ERROR: Failed to fetch 'ThisVisibleState")
            else:
                Print("~ERROR: Failed to fetch widget!")

    # Initializes the 'functions' tab for each button ui
    InitButtonFunctions():void=
        for(ThisButtonUI : ButtonUI):
            ThisButtonUI.ButtonSignal_Recieve.EnableButtonOnTrigger.TriggeredEvent.Subscribe(ThisButtonUI.EnableWidget) 
            ThisButtonUI.ButtonSignal_Recieve.DisableButtonOnTrigger.TriggeredEvent.Subscribe(ThisButtonUI.DisableWidget) 
            ThisButtonUI.ButtonSignal_Recieve.ShowButtonOnTrigger.TriggeredEvent.Subscribe(ThisButtonUI.ShowWidget)
            ThisButtonUI.ButtonSignal_Recieve.HideButtonOnTrigger.TriggeredEvent.Subscribe(ThisButtonUI.HideWidget)

buttonui := class<concrete>():

    @editable var ButtonText : string = ""
    @editable var ButtonPosition : vector2 = vector2{}
    @editable var ButtonSize : vector2 = vector2{X := 475.0, Y:= 80.0}

    # 'ButtonStyle' key: {button_loud = 1, button_quiet = 2, button_regular = 3}
    @editable var ButtonStyle : buttonchoice_int = 3

    @editable var RemovesUIOnClick : logic = true

    # Saves the state the button is in even after exiting. Example: Hiding a button, exiting the ui, and then opening the ui will still have that button hidden
    @editable SaveButtonState : logic = false
    
    @editable ButtonSignal_Recieve : ButtonUI_UserOptions_Functions = ButtonUI_UserOptions_Functions{}
    @editable ButtonSignal_Send : ButtonUI_UserOptions_Events = ButtonUI_UserOptions_Events{}

    var SizeToContent : logic = false

    var ButtonWidgetPerPlayer : [agent]widget = map{}

    var EnabledStateMap : [agent]logic = map{}
    var VisibleStateMap : [agent]logic = map{}

    EnableWidget(Agent : ?agent):void=
        if(ValidAgent := Agent?, ThisButtonWidget := ButtonWidgetPerPlayer[ValidAgent]):
            ThisButtonWidget.SetEnabled(true)
            if(SaveButtonState?):
                ValidAgent.SaveEnabledState(true)

    # Fades the button ui
    DisableWidget(Agent : ?agent):void=
        if(ValidAgent := Agent?, ThisButtonWidget := ButtonWidgetPerPlayer[ValidAgent]):
            ThisButtonWidget.SetEnabled(false)
            if(SaveButtonState?):
                ValidAgent.SaveEnabledState(false)

    # Saves the enabled state the button is currently in
    (Agent : agent).SaveEnabledState(State : logic):void=
        if(CurrentState := EnabledStateMap[Agent]):
            if(set EnabledStateMap[Agent] = State){}
        else:
            set EnabledStateMap = ConcatenateMaps(EnabledStateMap, map{Agent => State})

    ShowWidget(Agent : ?agent):void=
        if(ValidAgent := Agent?, ThisButtonWidget := ButtonWidgetPerPlayer[ValidAgent]):
            ThisButtonWidget.SetVisibility(widget_visibility.Visible)
            if(SaveButtonState?):
                ValidAgent.SaveVisibleState(true)

    # Completely removes the button ui
    HideWidget(Agent : ?agent):void=
        if(ValidAgent := Agent?, ThisButtonWidget := ButtonWidgetPerPlayer[ValidAgent]):
            ThisButtonWidget.SetVisibility(widget_visibility.Collapsed)
            if(SaveButtonState?):
                ValidAgent.SaveVisibleState(false)

    # Saves the visible state the button is currently in
    (Agent : agent).SaveVisibleState(State : logic):void=
        if(CurrentState := VisibleStateMap[Agent]):
            if(set VisibleStateMap[Agent] = State){}
        else:
            set VisibleStateMap = ConcatenateMaps(VisibleStateMap, map{Agent => State})

ButtonUI_UserOptions_Functions := class<concrete>():

    # The trigger that will enable this ui button, making it 'un-fade' and clickable
    @editable EnableButtonOnTrigger : trigger_device = trigger_device{}

    # The trigger that will disable this ui button, making it 'fade' and unclickable
    @editable DisableButtonOnTrigger : trigger_device = trigger_device{}

    # The trigger that will show this ui button, making it visible and clickable
    @editable ShowButtonOnTrigger : trigger_device = trigger_device{}

    # The trigger that will hide this ui button, making it not visible at all and therefore unclickable
    @editable HideButtonOnTrigger : trigger_device = trigger_device{}

ButtonUI_UserOptions_Events := class<concrete>():

    # The trigger/destination to activate
    @editable TriggerToActivate : trigger_device  = trigger_device{}

    # Do we send the instigator of the UI button?
    @editable SendInstigator : logic = true

1つずつ解説していきます。

あすか

クラスの定義

▼ このスクリプトは、Fortniteのゲーム内のUIを管理するためのものです。

以下、コードの各部分の解説です。

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

# ボタンスタイルの選択肢を現在の3種類に固定します。: {button_loud = 1, button_quiet = 2, button_regular = 3}
buttonchoice_int := type{_X : int where 1 <= _X, 3 >= _X}

menumanagerdevice := class(creative_device):

    # UIメニューを開くトリガー
    @editable 
    TriggerToShowUI : trigger_device = trigger_device{}

    # エディターで設定可能なボタンUIの配列
    @editable 
    var ButtonUI : []buttonui = array{}

    var MaybeMyUIPerPlayer : [player]?canvas = map{}

    StringToMessage<localizes>(String : string):message = "{String}"

buttonchoice_int~の詳細

whereキーワードの詳細

button_loudクラスの詳細

Type Macroの詳細

buttonchoice_int~の論理

型の定義で{}を使うことについて

_X : intという記述が必要な理由

menumanagerdevice := class(creative_device):の詳細

TriggerToShowUI: trigger_device = trigger_device{}の詳細

var ButtonUI : []buttonui = array{}の詳細

var MaybeMyUIPerPlayer : [player]?canvas = map{}の詳細

Mapの詳細

Comparable(比較可能)の詳細

サブタイプの詳細

canvasに?をつける理由

@editable var ButtonUI : []buttonui = array{}のbuttonuiの詳細

@editable var ButtonUI : []buttonui = array{}│配列を使用する理由

var ButtonUI&var MaybeMyUIPerPlayerに『var』が付けられている理由

StringToMessage<localizes>(String : string):message = "{String}"の詳細

(String: string)の詳細

StringToMessage(String : string):message = "{String}"がmessage 型である理由

var ButtonWidgetPerPlayer : [agent]widget = map{}の詳細

var ButtonWidgetPerPlayer : [agent]widget = map{}

var EnabledStateMap ~VisibleStateMap : [agent]logic = map{}の詳細

var EnabledStateMap : [agent]logic = map{}
var VisibleStateMap : [agent]logic = map{}

EnableWidget(Agent : ?agent):void=の詳細

EnableWidget(Agent : ?agent):void=
        if(ValidAgent := Agent?, ThisButtonWidget := ButtonWidgetPerPlayer[ValidAgent]):
            ThisButtonWidget.SetEnabled(true)
            if(SaveButtonState?):
                ValidAgent.SaveEnabledState(true)

if(SaveButtonState?):に?が付く理由

DisableWidget(Agent : ?agent):void=の詳細

DisableWidget(Agent : ?agent):void=
    if(ValidAgent := Agent?, ThisButtonWidget := ButtonWidgetPerPlayer[ValidAgent]):
        ThisButtonWidget.SetEnabled(false)
        if(SaveButtonState?):
            ValidAgent.SaveEnabledState(false)

条件式とクエリ式の違い

クエリ式を採用した世界線とそうでない場合の世界線

よくある質問

UIボタンの作り方についてよくある質問をまとめてみました。

buttonuiクラスの定義に教えて

(Agent : agent).SaveEnabledState(State : logic):void=:Agentが先に記述されている理由は、ボタンの有効状態はエージェントに対して実行されているから?

条件式とクエリ式について

制御フローとは

まとめ

今回はボタンの作成方法について解説しました。

最後に、完全なスクリプトを再掲しておきます↓

 完全なスクリプト
using { /Fortnite.com/Devices }
using { /Fortnite.com/UI }
using { /Verse.org/Simulation }
using { /Verse.org/Verse }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /UnrealEngine.com/Temporary/UI }
using { /UnrealEngine.com/Temporary/SpatialMath }

# Locks the choice of button styles to the current three: {button_loud = 1, button_quiet = 2, button_regular = 3}
buttonchoice_int := type{_X : int where 1 <= _X, 3 >= _X}

menumanagerdevice := class(creative_device):

    # The trigger that will open up this ui menu
    @editable TriggerToShowUI : trigger_device = trigger_device{}

    # An array of button ui, configurable via the editor
    @editable var ButtonUI : []buttonui = array{}

    var MaybeMyUIPerPlayer : [player]?canvas = map{}

    StringToMessage<localizes>(String : string):message = "{String}"
    
    OnBegin<override>()<suspends>:void=
        InitButtonFunctions()
        TriggerToShowUI.TriggeredEvent.Subscribe(OpenUI)

    OpenUI(Agent : ?agent):void=
        if(ValidAgent := Agent?, Player := player[ValidAgent], PlayerUI := GetPlayerUI[Player]):
            if(MyUI := MaybeMyUIPerPlayer[Player]?):
                PlayerUI.RemoveWidget(MyUI)
                if(set MaybeMyUIPerPlayer[Player] = false) {}
            else:
                NewUI := CreateMyUI(ValidAgent)
                PlayerUI.AddWidget(NewUI, player_ui_slot{InputMode := ui_input_mode.All})
                if(set MaybeMyUIPerPlayer[Player] = option{NewUI}) {}

    CreateMyUI(Agent : agent) : canvas = 
        for(CurrButtonUI : ButtonUI):
            case(CurrButtonUI.ButtonStyle):
                1 => 
                    ThisButtonUI := button_loud{DefaultText := StringToMessage(CurrButtonUI.ButtonText)}
                    ThisButtonUI.OnClick().Subscribe(HandleSelectedUIButton)
                    if(set CurrButtonUI.ButtonWidgetPerPlayer[Agent] = ThisButtonUI){}
                2 =>
                    ThisButtonUI := button_quiet{DefaultText := StringToMessage(CurrButtonUI.ButtonText)}
                    ThisButtonUI.OnClick().Subscribe(HandleSelectedUIButton)
                    if(set CurrButtonUI.ButtonWidgetPerPlayer[Agent] = ThisButtonUI){}
                3 =>
                    ThisButtonUI := button_regular{DefaultText := StringToMessage(CurrButtonUI.ButtonText)}
                    ThisButtonUI.OnClick().Subscribe(HandleSelectedUIButton)
                    if(set CurrButtonUI.ButtonWidgetPerPlayer[Agent] = ThisButtonUI){}
                _ => 
                    Print("~ERROR: ButtonStyle integer over/under-flow")
        CanvasArray := ReturnCanvasSlots(Agent)
        MyInteractableButtons : canvas = canvas:
            Slots := CanvasArray
        return MyInteractableButtons

    ReturnCanvasSlots(Agent : agent):[]canvas_slot=
        var CanvasSlotArray : []canvas_slot = array{}
        for(ThisButtonUI : ButtonUI):
            if(ThisWidget := ThisButtonUI.ButtonWidgetPerPlayer[Agent]):
                Agent.LoadButtonState(ThisButtonUI)
                ThisSlot := canvas_slot:
                    Offsets := margin{Top := ThisButtonUI.ButtonPosition.Y, Left := ThisButtonUI.ButtonPosition.X, Bottom := ThisButtonUI.ButtonSize.Y, Right := ThisButtonUI.ButtonSize.X}
                    ZOrder := 0
                    SizeToContent := ThisButtonUI.SizeToContent
                    Widget := ThisWidget
                set CanvasSlotArray += array{ThisSlot}
        return CanvasSlotArray

    DeleteUI(Message : widget_message) : void=
        if(PlayerUI := GetPlayerUI[Message.Player], MyUI := MaybeMyUIPerPlayer[Message.Player]?):
            PlayerUI.RemoveWidget(MyUI)
            if(set MaybeMyUIPerPlayer[Message.Player] = false) {}
                
    HandleSelectedUIButton(Message : widget_message):void=
        if(PlayerUI := GetPlayerUI[Message.Player], MyUI := MaybeMyUIPerPlayer[Message.Player]?):
            Player := Message.Player
            ThisWidget := Message.Source
            StaleButtonUI := Player.FindButtonUI(ThisWidget)
            if(ThisButtonUI := StaleButtonUI?):
                if(ThisButtonUI.RemovesUIOnClick?):
                    DeleteUI(Message)
                ThisButtonUI.ActivateTrigger(Player)
            else:
                Print("~ERROR: No buttonui found!")
                
    (Agent : agent).FindButtonUI(ThisWidget : widget):?buttonui=
        for(ThisButtonUI : ButtonUI):
            if(StaleWidget := ThisButtonUI.ButtonWidgetPerPlayer[Agent]):
                if(ThisWidget = StaleWidget):
                    return option{ThisButtonUI}
        return false

    (ThisButtonUI : buttonui).ActivateTrigger(Player : player):void=
        ThisSignalDestination := ThisButtonUI.ButtonSignal_Send
        if(ThisSignalDestination.SendInstigator?):
            ThisSignalDestination.TriggerToActivate.Trigger(Player)
        ThisSignalDestination.TriggerToActivate.Trigger()

    # Loads all button states that have 'SaveButtonState' turned on
    (Agent : agent).LoadButtonState(ThisButtonUI : buttonui):void=
        if(ThisButtonUI.SaveButtonState?):
            if(ThisWidget := ThisButtonUI.ButtonWidgetPerPlayer[Agent]):
                if(ThisEnabledState := ThisButtonUI.EnabledStateMap[Agent]):
                    ThisWidget.SetEnabled(ThisEnabledState)
                if(ThisVisibleState := ThisButtonUI.VisibleStateMap[Agent]):
                    case(ThisVisibleState):
                        true => ThisWidget.SetVisibility(widget_visibility.Visible)
                        false => ThisWidget.SetVisibility(widget_visibility.Collapsed)
                        _ => Print("~ERROR: Failed to fetch 'ThisVisibleState")
            else:
                Print("~ERROR: Failed to fetch widget!")

    # Initializes the 'functions' tab for each button ui
    InitButtonFunctions():void=
        for(ThisButtonUI : ButtonUI):
            ThisButtonUI.ButtonSignal_Recieve.EnableButtonOnTrigger.TriggeredEvent.Subscribe(ThisButtonUI.EnableWidget) 
            ThisButtonUI.ButtonSignal_Recieve.DisableButtonOnTrigger.TriggeredEvent.Subscribe(ThisButtonUI.DisableWidget) 
            ThisButtonUI.ButtonSignal_Recieve.ShowButtonOnTrigger.TriggeredEvent.Subscribe(ThisButtonUI.ShowWidget)
            ThisButtonUI.ButtonSignal_Recieve.HideButtonOnTrigger.TriggeredEvent.Subscribe(ThisButtonUI.HideWidget)

buttonui := class<concrete>():

    @editable var ButtonText : string = ""
    @editable var ButtonPosition : vector2 = vector2{}
    @editable var ButtonSize : vector2 = vector2{X := 475.0, Y:= 80.0}

    # 'ButtonStyle' key: {button_loud = 1, button_quiet = 2, button_regular = 3}
    @editable var ButtonStyle : buttonchoice_int = 3

    @editable var RemovesUIOnClick : logic = true

    # Saves the state the button is in even after exiting. Example: Hiding a button, exiting the ui, and then opening the ui will still have that button hidden
    @editable SaveButtonState : logic = false
    
    @editable ButtonSignal_Recieve : ButtonUI_UserOptions_Functions = ButtonUI_UserOptions_Functions{}
    @editable ButtonSignal_Send : ButtonUI_UserOptions_Events = ButtonUI_UserOptions_Events{}

    var SizeToContent : logic = false

    var ButtonWidgetPerPlayer : [agent]widget = map{}

    var EnabledStateMap : [agent]logic = map{}
    var VisibleStateMap : [agent]logic = map{}

    EnableWidget(Agent : ?agent):void=
        if(ValidAgent := Agent?, ThisButtonWidget := ButtonWidgetPerPlayer[ValidAgent]):
            ThisButtonWidget.SetEnabled(true)
            if(SaveButtonState?):
                ValidAgent.SaveEnabledState(true)

    # Fades the button ui
    DisableWidget(Agent : ?agent):void=
        if(ValidAgent := Agent?, ThisButtonWidget := ButtonWidgetPerPlayer[ValidAgent]):
            ThisButtonWidget.SetEnabled(false)
            if(SaveButtonState?):
                ValidAgent.SaveEnabledState(false)

    # Saves the enabled state the button is currently in
    (Agent : agent).SaveEnabledState(State : logic):void=
        if(CurrentState := EnabledStateMap[Agent]):
            if(set EnabledStateMap[Agent] = State){}
        else:
            set EnabledStateMap = ConcatenateMaps(EnabledStateMap, map{Agent => State})

    ShowWidget(Agent : ?agent):void=
        if(ValidAgent := Agent?, ThisButtonWidget := ButtonWidgetPerPlayer[ValidAgent]):
            ThisButtonWidget.SetVisibility(widget_visibility.Visible)
            if(SaveButtonState?):
                ValidAgent.SaveVisibleState(true)

    # Completely removes the button ui
    HideWidget(Agent : ?agent):void=
        if(ValidAgent := Agent?, ThisButtonWidget := ButtonWidgetPerPlayer[ValidAgent]):
            ThisButtonWidget.SetVisibility(widget_visibility.Collapsed)
            if(SaveButtonState?):
                ValidAgent.SaveVisibleState(false)

    # Saves the visible state the button is currently in
    (Agent : agent).SaveVisibleState(State : logic):void=
        if(CurrentState := VisibleStateMap[Agent]):
            if(set VisibleStateMap[Agent] = State){}
        else:
            set VisibleStateMap = ConcatenateMaps(VisibleStateMap, map{Agent => State})

ButtonUI_UserOptions_Functions := class<concrete>():

    # The trigger that will enable this ui button, making it 'un-fade' and clickable
    @editable EnableButtonOnTrigger : trigger_device = trigger_device{}

    # The trigger that will disable this ui button, making it 'fade' and unclickable
    @editable DisableButtonOnTrigger : trigger_device = trigger_device{}

    # The trigger that will show this ui button, making it visible and clickable
    @editable ShowButtonOnTrigger : trigger_device = trigger_device{}

    # The trigger that will hide this ui button, making it not visible at all and therefore unclickable
    @editable HideButtonOnTrigger : trigger_device = trigger_device{}

ButtonUI_UserOptions_Events := class<concrete>():

    # The trigger/destination to activate
    @editable TriggerToActivate : trigger_device  = trigger_device{}

    # Do we send the instigator of the UI button?
    @editable SendInstigator : logic = true

お疲れさまでした🍵

コツコツがんばっていきましょ。

 僕のTwitterアカウントです。
 Next→
【UEFN】UIボタンの作り方(2ページ目)│Verse解説の続き
【UEFN】UIボタンの作り方(2ページ目)│Verse解説の続き

続きを見る

【UEFN】UIボタンの作り方(3ページ目)│Verse解説の続き
【UEFN】UIボタンの作り方(3ページ目)│Verse解説の続き

続きを見る

【UEFN】UIボタンの作り方(4ページ目)│Verse解説の続き
【UEFN】UIボタンの作り方(4ページ目)│Verse解説の続き

続きを見る

【UEFN】UIボタンの作り方(5ページ目)│Verse解説の続き
【UEFN】UIボタンの作り方(5ページ目)│Verse解説の続き

続きを見る

  • この記事を書いた人
  • 最新記事

あすか(Asuka)

自己紹介:UEFNクリエイター
Verse言語独学中 | UE・プログラミング・3Dモデリング 6月開始 | 備忘録として学習記録をブログに残しています。
■好きな言葉
徳は弧ならず必ず隣あり.
■ひとこと
まだまだ未熟者ですが、
よろしくお願いします。

-UEFN, Verse言語, メタバース
-, ,