Guide to Event Subscribing with Additional Parameters (Handler Functions)

It seems like a common problem creators run into while learning Verse is trying to pass in additional parameters while subscribing events to functions. For example, if you have multiple buttons that you want to print a different color based on a string parameter, you might try subscribing the InteractedWithEvent to a function like PrintColor(Color : string). This would cause an error, because (generally) by default all events can only be subscribed to a function with an agent parameter. The solution to this is a handler function.

Handler functions are essentially a way of passing additional parameters by creating a class object and calling a function using the class attributes. Here is a copy-paste format for how to use them:

Paste this at the very top of your code (or above your main class):

Handler1 := class() {
    Device : main   <#YOU MUST CHANGE "main" TO YOUR VERSE DEVICE NAME#>
    Parameter1 : int  <#Change "int" to your parameter type, ex: float, string#>

    HandlerFunction(Agent : agent) : void = {
        Device.MainFunction(Parameter1)
    }
}

Paste this into your OnBegin. You will need to change the Button.InteractedWithEvent to whatever event you are subscribing with. You will also need to change Value to whatever value you want passed through the parameter:

Button.InteractedWithEvent.Subscribe(Handler1{Device:=Self, Parameter1:=Value}.HandlerFunction)

Finally, create your main function (inside main class but outside of OnBegin):

MainFunction(Parameter1 : int) : void = {
        Print("{Parameter1}") #Function code here
    }

The uses of handlers may not seem immediately obvious, but they can be very useful when you have multiple devices with the same functionality but different inputs, like if you had an array of buttons each designated to increase the score of a different score manager. I hope this was helpful and good luck!

31 Likes

This indeed helped me a lot when I knew about this. Thanks for the tutorial, I’m sure it’s gonna help a lot of people!

2 Likes

Thank you! I’ll be using this for sure.

2 Likes

Thanks for posting @BrendannnD.

We also use this pattern in the Tagged Lights Puzzle Tutorial.
For example, the button handler looks like:

 button_event_handler := class():
 # Positions used to access the lights this button controls.
 Indices : []int

 # tagged_lights_puzzle that created this button_event_handler so we can call functions on it.
 PuzzleDevice : tagged_lights_puzzle
    
 OnButtonPressed(Player : agent) : void =
     # Tell the PuzzleDevice to toggle the lights at the positions this button controls.
     PuzzleDevice.ToggleLights(Indices)
3 Likes

Hi, @BrendannnD

What about adding the effect suspends ?

I need to use .await inside the “MainFunction”. Because I need to use a Countdown. Followed the tutorial for this

So I added but I end up with an error, that idk how to resolve. In line:

Zone.AreaIsScoredEvent.Subscribe(HandlerAreaIsScoredEvent{Device:=Self, Zone:=I}.HandlerFunction)

My code


var CountdownTimer : countdown_timer = countdown_timer{}
InitialCountdownTime : float = 30.0  

HandlerAreaIsScoredEvent:= class() {
    Device : Conquest   
    Zone : int  

    HandlerFunction(Agent : agent)<suspends> : void = {
        Device.MainFunction(Agent, Zone)
    }
}

OnBegin<override>()<suspends>:void=
  for( I->Zone: Zones):
    Zone.AreaIsScoredEvent.Subscribe(HandlerAreaIsScoredEvent{Device:=Self, Zone:=I}.HandlerFunction)

MainFunction(Agent: agent, Zone : int)<suspends> : void = {
  StartCountdown()

StartCountdown()<suspends>: void =
  Print("Starting countdown")
        
  AllPlayers:[]player = GetPlayspace().GetPlayers()
    for(Player:AllPlayers):
      if(PlayerUI := GetPlayerUI[Player]):
        set CountdownTimer = countdown_timer{MaybePlayerUI := option{PlayerUI}, RemainingTime := InitialCountdownTime}
        CountdownTimer.StartCountdown()
        CountdownTimer.CountdownEndedEvent.Await()

Hi, here you would want to do in the HandlerFunction:

spawn{Device.MainFunction(Agent, Zone)}
1 Like

I posted a slightly different approach to this problem here: Wrapping Subscribe() to pass additional data to listeners | Uefn Code Snippet

My approach is that I’m creating the handler in an extension method, so you can just say MyButton.InteractedWithEvent.SubscribeAgent(MyFunc, "some extra data")

6 Likes

Great approach! Might have to try this instead, I like the simpler syntax.