Customisable Security Keypad With SFX
Module — 2 files
These files compile together (same module folder).
ez_ui_keypad_v2.verse
<#
A customisable security keypad connected to a barrier device but
could be rewritten to affect anything you want such as chests, doors
or even NPCs. These placeholder visuals are freely available from my
Instagram Threads page at this link: https://www.threads.net/@kryyative/post/CzXCZ_ENBc3
#>
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_KeypadManager := class ()
{
EZ_LinkedCreativeDevice<public> : ez_ui_keypad_v2 # 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_RequiredPIN_000to999<public> : string # the valid PIN
EZ_BarrierDevice<public> : barrier_device # the device controlled by the keypad
EZ_AccessGrantedSFX<public> : audio_player_device # correct PIN sfx
EZ_AccessDeniedSFX<public> : audio_player_device # incorrect PIN sfx
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 InputCount : int = 0
var InputPINArray : []?int = array{false,false,false} #input PIN values
EZUI_Position_Horizonal<public> : type{_X:float where 0.000000 <= _X, _X <= 1.000000} = 0.5
EZUI_Position_Vertical<public> : type{_X:float where 0.000000 <= _X, _X <= 1.000000} = 0.5
#Interactive UI components
WidgetUIButton1:button_quiet=button_quiet{}
WidgetUIButton2:button_quiet=button_quiet{}
WidgetUIButton3:button_quiet=button_quiet{}
WidgetUIButton4:button_quiet=button_quiet{}
WidgetUIButton5:button_quiet=button_quiet{}
WidgetUIButton6:button_quiet=button_quiet{}
WidgetUIButton7:button_quiet=button_quiet{}
WidgetUIButton8:button_quiet=button_quiet{}
WidgetUIButton9:button_quiet=button_quiet{}
WidgetUIButtonCancel:button_quiet=button_quiet{}
WidgetUIButton0:button_quiet=button_quiet{}
WidgetUIButtonAccept:button_quiet=button_quiet{}
#Custom Visual UI components
<#
Design your visible UI widgets here which use imported custom visuals
This is only used if you enable the "UseCustomArt" tickbox on the properties but you will
need to define the imported assets where by their path andfilename under the DefaultImage
and set the DesiredSize to match the image dimensions.
refer to https://dev.epicgames.com/documentation/en-us/uefn/exposing-assets-to-verse-in-unreal-editor-for-fortnite
for info on how to import custom textures required for texture_blocks.
#>
# graphics layer which contains visual elements of the UI
GfxLayerWidget<private> : canvas = canvas{Slots := array{}}
# the default keypad gfx widget
Intial_KeyPadGfxWidget<private> : texture_block = texture_block
{
DefaultImage := Textures.KeypadTemplatePNG
DefaultDesiredSize := vector2{X := 250.0, Y := 400.0}
}
# 1st PIN entered gfx
FirstPIN_KeyPadGfxWidget<private> : texture_block = texture_block
{
DefaultImage := Textures.KeypadTemplate_PIN1PNG
DefaultDesiredSize := vector2{X := 250.0, Y := 400.0}
}
# 2nd PIN entered gfx
SecondPIN_KeyPadGfxWidget<private> : texture_block = texture_block
{
DefaultImage := Textures.KeypadTemplate_PIN2PNG
DefaultDesiredSize := vector2{X := 250.0, Y := 400.0}
}
# 3rd PIN entered gfx
ThirdPIN_KeyPadGfxWidget<private> : texture_block = texture_block
{
DefaultImage := Textures.KeypadTemplate_PIN3PNG
DefaultDesiredSize := vector2{X := 250.0, Y := 400.0}
}
# PIN accepted gfx
Accepted_KeyPadGfxWidget<private> : texture_block = texture_block
{
DefaultImage := Textures.KeypadTemplate_AcceptedPNG
DefaultDesiredSize := vector2{X := 250.0, Y := 400.0}
}
# PIN denied gfx
Denied_KeyPadGfxWidget<private> : texture_block = texture_block
{
DefaultImage := Textures.KeypadTemplate_DeniedPNG
DefaultDesiredSize := vector2{X := 250.0, Y := 400.0}
}
# debugging color block
DebugBlock<private> : color_block = color_block
{
DefaultColor := color{R := 1.0, G := 0.0, B := 0.0}
DefaultOpacity := 0.75
}
# some common UI presets
DefaultKeyPadding : margin = margin{ Top := 10.0, Left := 10.0, Bottom := 10.0, Right := 10.0 }
NoPadding : margin = margin{ Top := 0.0, Left := 0.0, Bottom := 0.0, Right := 0.0 }
CentreAlignment : vector2 = vector2{X := 0.5, Y := 0.5}
# do all the things
Ini<public>() : void =
if (IsInitialized?):
return
if(EZ_UI_ID = 1): # just a hold-over from multi-UI style support
# subscribe keypad
WidgetUIButton1.OnClick().Subscribe(OnUIButtonClick)
WidgetUIButton2.OnClick().Subscribe(OnUIButtonClick)
WidgetUIButton3.OnClick().Subscribe(OnUIButtonClick)
WidgetUIButton4.OnClick().Subscribe(OnUIButtonClick)
WidgetUIButton5.OnClick().Subscribe(OnUIButtonClick)
WidgetUIButton6.OnClick().Subscribe(OnUIButtonClick)
WidgetUIButton7.OnClick().Subscribe(OnUIButtonClick)
WidgetUIButton8.OnClick().Subscribe(OnUIButtonClick)
WidgetUIButton9.OnClick().Subscribe(OnUIButtonClick)
WidgetUIButtonCancel.OnClick().Subscribe(OnUIButtonClick)
WidgetUIButton0.OnClick().Subscribe(OnUIButtonClick)
WidgetUIButtonAccept.OnClick().Subscribe(OnUIButtonClick)
KeyPad_1() # builds the main canvas
# adds the initial keypad gfx to the gfx layer
GfxLayerWidget.AddWidget
(
canvas_slot:
Anchors := anchors{Minimum := vector2{X := EZUI_Position_Horizonal , Y := EZUI_Position_Vertical}, Maximum := vector2{X := EZUI_Position_Horizonal, Y := EZUI_Position_Vertical}}
Offsets := margin{Top := 0.0, Left := 0.0, Right := 0.0, Bottom := -120.0}
Alignment := CentreAlignment
ZOrder := 0
SizeToContent := true
# keypad gfx
Widget := overlay:
Slots := array:
overlay_slot:
HorizontalAlignment := horizontal_alignment.Center
VerticalAlignment := vertical_alignment.Center
Widget := Intial_KeyPadGfxWidget
)
#.........................
# registers the main canvas to the Linked player
AddUI<public>() : void =
if (PlayerUI := GetPlayerUI[EZ_LinkedPlayer]):
PlayerUI.AddWidget(GetMainCanvas(), player_ui_slot{InputMode := ui_input_mode.All}) #all canvas and content interactions active
#.........................
# Exposes the class Canvas to public
GetMainCanvas<public>() : canvas = MainCanvas
#.........................
# main canvas with the interaction layer added
KeyPad_1 <public>() : void =
set MainCanvas = canvas
{
Slots := array
{
# keys 1 - 3
Keys1to3Slot := canvas_slot:
Anchors := anchors{Minimum := vector2{X := EZUI_Position_Horizonal , Y := EZUI_Position_Vertical}, Maximum := vector2{X := EZUI_Position_Horizonal, Y := EZUI_Position_Vertical}}
Offsets := margin{Top := -70.0, Left := 0.0, Right := 0.0, Bottom := -70.0}
Alignment := CentreAlignment
ZOrder := 1
SizeToContent := true
Widget := overlay:
Slots := array:
overlay_slot:
Padding:= NoPadding
Widget := stack_box:
Orientation := orientation.Horizontal
Slots := array:
stack_box_slot:
Padding:= DefaultKeyPadding
Widget := WidgetUIButton1
stack_box_slot:
Padding:= DefaultKeyPadding
Widget := WidgetUIButton2
stack_box_slot:
Padding:= DefaultKeyPadding
Widget := WidgetUIButton3
# keys 4 - 6
Keys4to6Slot := canvas_slot:
Anchors := anchors{Minimum := vector2{X := EZUI_Position_Horizonal , Y := EZUI_Position_Vertical}, Maximum := vector2{X := EZUI_Position_Horizonal, Y := EZUI_Position_Vertical}}
Offsets := margin{Top := 0.0, Left := 0.0, Right := 0.0, Bottom := 0.0}
Alignment := CentreAlignment
ZOrder := 1
SizeToContent := true
Widget := overlay:
Slots := array:
overlay_slot:
Padding:= NoPadding
Widget := stack_box:
Orientation := orientation.Horizontal
Slots := array:
stack_box_slot:
Padding:= DefaultKeyPadding
Widget := WidgetUIButton4
stack_box_slot:
Padding:= DefaultKeyPadding
Widget := WidgetUIButton5
stack_box_slot:
Padding:= DefaultKeyPadding
Widget := WidgetUIButton6
# keys 7 - 9
Keys7to9Slot := canvas_slot:
Anchors := anchors{Minimum := vector2{X := EZUI_Position_Horizonal , Y := EZUI_Position_Vertical}, Maximum := vector2{X := EZUI_Position_Horizonal, Y := EZUI_Position_Vertical}}
Offsets := margin{Top := 70.0, Left := 0.0, Right := 0.0, Bottom := 70.0}
Alignment := CentreAlignment
ZOrder := 1
SizeToContent := true
Widget := overlay:
Slots := array:
overlay_slot:
Padding:= NoPadding
Widget := stack_box:
Orientation := orientation.Horizontal
Slots := array:
stack_box_slot:
Padding:= DefaultKeyPadding
Widget := WidgetUIButton7
stack_box_slot:
Padding:= DefaultKeyPadding
Widget := WidgetUIButton8
stack_box_slot:
Padding:= DefaultKeyPadding
Widget := WidgetUIButton9
# key cancel,0, accept
KeysCancelZeroAcceptSlot := canvas_slot:
Anchors := anchors{Minimum := vector2{X := EZUI_Position_Horizonal , Y := EZUI_Position_Vertical}, Maximum := vector2{X := EZUI_Position_Horizonal, Y := EZUI_Position_Vertical}}
Offsets := margin{Top := 140.0, Left := 0.0, Right := 0.0, Bottom := 140.0}
Alignment := CentreAlignment
ZOrder := 1
SizeToContent := true
Widget := overlay:
Slots := array:
overlay_slot:
Padding:= NoPadding
Widget := stack_box:
Orientation := orientation.Horizontal
Slots := array:
stack_box_slot:
Padding:= DefaultKeyPadding
Widget := WidgetUIButtonCancel
stack_box_slot:
Padding:= DefaultKeyPadding
Widget := WidgetUIButton0
stack_box_slot:
Padding:= DefaultKeyPadding
Widget := WidgetUIButtonAccept
# gfx layer
GfxLayer := canvas_slot:
Anchors := anchors{Minimum := vector2{X := EZUI_Position_Horizonal , Y := EZUI_Position_Vertical}, Maximum := vector2{X := EZUI_Position_Horizonal, Y := EZUI_Position_Vertical}}
Offsets := margin{Top := 0.0, Left := 0.0, Right := 0.0, Bottom := 0.0}
Alignment := CentreAlignment
ZOrder := 0
SizeToContent := false
Widget := GfxLayerWidget
}
}
# initial gfx
Intial_KeyPadGfxWidget.SetDesiredSize(vector2{X := 250.0, Y := 400.0})
GfxLayerWidget.AddWidget(canvas_slot
{
Offsets := margin{Top := 0.0, Left := 0.0, Right := 0.0, Bottom := -120.0}
Alignment := CentreAlignment
ZOrder := 0
# keypad gfx
Widget := Intial_KeyPadGfxWidget
} )
#Print("[EZUI] RefreshGFXSlot: Added Widget")
set IsInitialized = true # Lock the inialization state to prevent from running this function again
#.........................
# maps the button widgets to values
OnUIButtonClick (InputEvent : widget_message) : void=
InteractingPlayer := InputEvent.Player
KeyWidget := InputEvent.Source
var InputValue : int = 0
# key value bindings
if (KeyWidget = WidgetUIButton0):
set InputValue = 0
if (KeyWidget = WidgetUIButton1):
set InputValue = 1
if (KeyWidget = WidgetUIButton2):
set InputValue = 2
if (KeyWidget = WidgetUIButton3):
set InputValue = 3
if (KeyWidget = WidgetUIButton4):
set InputValue = 4
if (KeyWidget = WidgetUIButton5):
set InputValue = 5
if (KeyWidget = WidgetUIButton6):
set InputValue = 6
if (KeyWidget = WidgetUIButton7):
set InputValue = 7
if (KeyWidget = WidgetUIButton8):
set InputValue = 8
if (KeyWidget = WidgetUIButton9):
set InputValue = 9
if (KeyWidget = WidgetUIButtonCancel):
set InputValue = 10
if (KeyWidget = WidgetUIButtonAccept):
set InputValue = 11
UpdatePIN(InputValue,EZ_LinkedPlayer)
#.........................
# keeps track of and handles the numbers input
UpdatePIN( KeyInput : int, InteractingPlayer : player) : void=
if (KeyInput = 10):
spawn{PINResult(0)} # close without PIN check
set InputCount = 0
return
if (KeyInput = 11):
spawn{PINResult(1)} #close and check PIN
set InputCount = 0
return
if (InputCount = 3):
return
if (InputCount = 0):
InsertIntoPINArray(KeyInput)
RefreshGFXSlot(Intial_KeyPadGfxWidget,FirstPIN_KeyPadGfxWidget)
set InputCount = 1
#Print("Input Count = {InputCount}")
return
else if (InputCount = 1):
InsertIntoPINArray(KeyInput)
RefreshGFXSlot(FirstPIN_KeyPadGfxWidget,SecondPIN_KeyPadGfxWidget)
set InputCount = 2
#Print("Input Count = {InputCount}")
return
else:
if (InputCount = 2):
InsertIntoPINArray(KeyInput)
RefreshGFXSlot(SecondPIN_KeyPadGfxWidget,ThirdPIN_KeyPadGfxWidget)
set InputCount = 3
#Print("Input Count = {InputCount}")
#.........................
# clear UI
ClearAllUI <public>(): void =
{
if (PlayerUI := GetPlayerUI[EZ_LinkedPlayer]) {
PlayerUI.RemoveWidget(GetMainCanvas())
}
}
#.........................
# key inpits are inserted into an array
InsertIntoPINArray(KeyValue : int) : void=
for (i := 0..InputPINArray.Length -1 ): #iterate
# find the first empty index
if (InputPINArray[i] = false):
#Print("Found Empty Slot @ Index " + "{i}")
# fill the emoty index
OptKeyValue := option{KeyValue}
if(set InputPINArray[i] = OptKeyValue) {}
#Print("Added {KeyValue} @ Index " + "{i}")
return
#.........................
# converts the contents of the PINArray into a string to be compared with RequiredPIN_000to999
PINArrayToString() : string =
{
var i0 : int = 0
var i1 : int = 0
var i2 : int = 0
if (NewPIN0 := InputPINArray[0]?) { set i0 = NewPIN0 }
if (NewPIN1 := InputPINArray[1]?) { set i0 = NewPIN1 }
if (NewPIN2 := InputPINArray[2]?) { set i0 = NewPIN2 }
return "{i0}{i1}{i2}"
}
#.........................
# the function used to update the UI visuals
RefreshGFXSlot(CurrentGfxWidget : texture_block, NewGfxWidget : texture_block) : void =
GfxLayerWidget.RemoveWidget(CurrentGfxWidget)
#Print("[EZUI] RefreshGFXSlot: Removed Widget")
NewGfxWidget.SetDesiredSize(vector2{X := 250.0, Y := 400.0})
GfxLayerWidget.AddWidget(canvas_slot
{
Offsets := margin{Top := 0.0, Left := 0.0, Right := 0.0, Bottom := -120.0}
Alignment := CentreAlignment
ZOrder := 0
# keypad gfx
Widget := NewGfxWidget
})
#Print("[EZUI] RefreshGFXSlot: Added Widget")
#.........................
# handles all of sfx and visuals for a PIN Result then closes the UI
PINResult(CloseType : int)<suspends>:void=
# close type defines exiting the UI or attempting to enter the PIN
if (CloseType = 1):
EnteredPIN := PINArrayToString()
#Print("DEBUG: PIN Entered is {EnteredPIN}")
if (EnteredPIN = EZ_RequiredPIN_000to999):
EZ_AccessGrantedSFX.Play()
EZ_BarrierDevice.Disable()
RefreshGFXSlot(ThirdPIN_KeyPadGfxWidget,Accepted_KeyPadGfxWidget)
#Print("DEBUG: Correct PIN")
Sleep(1.0) # wait a moment to see the PIN result
RefreshGFXSlot(Accepted_KeyPadGfxWidget,Intial_KeyPadGfxWidget) # reset gfx
else:
EZ_AccessDeniedSFX.Play()
EZ_BarrierDevice.Enable()
RefreshGFXSlot(ThirdPIN_KeyPadGfxWidget,Denied_KeyPadGfxWidget)
#Print("DEBUG : Wrong PIN")
Sleep(1.0) # wait a moment to see the PIN result
RefreshGFXSlot(Denied_KeyPadGfxWidget,Intial_KeyPadGfxWidget) # reset gfx
#clear PIN inputs
for (i := 0..InputPINArray.Length - 1):
if (set InputPINArray[i] = false) {}
ClearAllUI()
#.........................
} # END EZ_KeypadManager ------------------------------------------------------------------------------------------------------------------------------------
ez_ui_keypad_v2 := class(creative_device):
@editable ShowUITrigger : trigger_device = trigger_device {}
@editable RequiredPIN_000to999 : string = "009" # between 000 and 999 only
@editable BarrierDevice : barrier_device = barrier_device{}
@editable AccessGrantedSFX : audio_player_device = audio_player_device{}
@editable AccessDeniedSFX : audio_player_device = audio_player_device{}
@editable var UI_Position_Horizonal : type{_X:float where 0.000000 <= _X, _X <= 1.000000} = 0.5
@editable var UI_Position_Vertical : type{_X:float where 0.000000 <= _X, _X <= 1.000000} = 0.5
# UI regisitration
var PlayerToUIMap : [player]EZ_KeypadManager = 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
#.........................
# pop-up templates selected by a UI ID number
var UI_ID : int = 1 #just one texture based UI
#.........................
if(PlayerAgent := MaybePlayer?): #set the typing to agent from ?agent
# Verify if an Linked pop-up manager already exists and uses it, if not, create a new one
if ( InteractingPlayer := player[PlayerAgent] )
{
var OptPlayerUI :?EZ_KeypadManager = false
if (ThisPlayerUI := PlayerToUIMap[InteractingPlayer])
{ set OptPlayerUI = option{ThisPlayerUI} }
else if (ThisPlayerUI := set PlayerToUIMap[InteractingPlayer] = EZ_KeypadManager
#everything that is passed to the EZ manager
{
EZ_LinkedCreativeDevice := Self,
EZ_LinkedPlayer := InteractingPlayer,
EZ_UI_ID := UI_ID,
EZ_RequiredPIN_000to999 := RequiredPIN_000to999,
EZ_BarrierDevice := BarrierDevice,
EZ_AccessGrantedSFX := AccessGrantedSFX,
EZ_AccessDeniedSFX := AccessDeniedSFX,
EZUI_Position_Horizonal := UI_Position_Horizonal,
EZUI_Position_Vertical := UI_Position_Vertical
}
)
{ ThisPlayerUI.Ini(), set OptPlayerUI = option{ThisPlayerUI} }
# Try to open the Interface for the player
if (PlayerUI := OptPlayerUI?):
PlayerUI.AddUI()
else
{ Print("Error when getting saved or creating ThisPlayerUI") }
}
#.........................
}
<#
a special thamks to:
Mayapple
SprinterMax
Daigorō
CUDL_DreamyFox
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.