Skip to main content

Event System

Events are a crucial part of any game, this includes the Massive Loop. The majority of events can be traced back to player interactions. However, the scripts also can raise events based on some criteria.

The Lua Event System in Massive Loop is a versatile tool used to manage and communicate not only with other parts of the game but it can also be used to synchronize the game between all players over the network.

It's all about the handlers

Handlers: functions that will get executed when an event triggers.

In Massive Loop, the only way to create an Event is to attach handlers to it.

Attaching handlers: Specifying the function that will execute when an event triggers.

When attaching a handler to an Event, the system keeps a record of the specified event name and the functions to execute. Adding more handlers to that event just adds them to that record.

warning

Raising an event is pointless if there are no handlers to handle the event.

Adding Handlers:

To add a handler to an event, use the following method (from API)

EventToken MLEvents.AddHandler(string eventName, Action<object[]> handler);

The eventName is a string that defines the name of the event.

handler is a function that will be executed when the event triggers.

Example
using ML.SDK;
using UnityEngine;

public class Script : MonoBehaviour
{

private void Handler(object[] arguments)
{
// code to run
}

void Start()
{
MLEvents.AddHandler("AnEventName", Handler);
}

}

Note that both in Lua and C#, like many other programming languages, you can pass the reference to a function by typing its name without parenthesis.

You can add many handlers to same event:

using ML.SDK;
using UnityEngine;

public class Script : MonoBehaviour
{

private void Handler1(object[] arguments)
{
// code to run for handler 1
}

private void Handler2(object[] arguments)
{
// code to run for handler 2
}

void Start()
{
MLEvents.AddHandler("AnEventName", Handler1);
MLEvents.AddHandler("AnEventName", Handler2);
}

}
Tip

Keep handlers lightweight in terms of computation. If there are 1000 handlers attached to an event, all those handlers will get executed in one single frame when the event triggers. This may cause performance issues.

It is recommended to keep the number of handlers per event as low as possible.

If a handler requires you to initiate a costly computation, you can have a bool value in your script in which the handler will set to true. Then, in update(), check for that variable and if it's true, start a coroutine to perform the calculation.

using ML.SDK;
using System.Collections;
using UnityEngine;

public class Script : MonoBehaviour
{

private void Handler(object[] arguments)
{
StartCoroutine(CoroutineFunction());
}

private IEnumerator CoroutineFunction()
{
// perform heavy computations here
// don't forget to call yield

yield return null;
}

void Start()
{
MLEvents.AddHandler("AnEventName", Handler);
}

}

Raising the events

An event can be raised in different ways.

One way to raise an event is by its name. You can call the Invoke method (From any script) to raise an event. For example:

MLEvents.Invoke("anEventName");

You can pass optional parameters in the invoke function which will get passed to a handler function.

For example, you can define the following handler:

using ML.SDK;
using UnityEngine;

public class Script : MonoBehaviour
{
private void Handler(object[] arguments)
{
Debug.Log($"I received the message {arguments[0]}");
}

void Start()
{
MLEvents.AddHandler("AnEventName", Handler);
}

}

And invoke it like this:

MLEvents.Invoke("AnEventName", "Hello there!");

You can raise an event over the network as well.

Every handler that is attached to an event can be raised over the network. You don't need anything else.

It is important to remember that all of the clients (players who joined the room) will have access to all Lua scripts.

Caution

It's important that the handler must be registered before raising an event.

To raise an event over the network, you should call InvokeNetwork of these methods instead of plain invoke().

For more detail about networking events, please check out this tutorial on multiplayer and networking.

Removing handlers

To remove a handler, you can call the LuaEvents.remove() function. All registered handlers can be traced by their LuaEventId's. The LuaEventId is a unique identifier for a handler that is attached to a specific function.

The Add function returns a token for the attached handler which can be used to remove it later.

using ML.SDK;
using UnityEngine;

public class Script : MonoBehaviour
{

EventToken eventToken;

private void Handler(object[] arguments)
{
Debug.Log($"I received the message {arguments[0]}");
}

void Start()
{
eventToken = MLEvents.AddHandler("AnEventName", Handler);
}

private void OnDisable()
{
MLEvents.RemoveHandler(eventToken);
}

}
Tip

Although small, all registered handlers occupy a space in memory. Remove unnecessary handlers.

warning

For the Lua scripts, try to add all your event handlers in the start function.

Local events

A Local event only defined with in the script and the game object. So, for example lets imagine a local event called testEvent within a Lua script test.lua attached to game object A. The scope of this event is only the combination of the object A and test.lua. Same event name in same script attached to a different object will be different from this event. Or a local event with same name in object A with in a different script would in a separate scope.

Local Events over the network!

Ironically, the local events can be raised over the network! In this case, the scope of the local event is within the same game object + Lua script combination that existed in the scene during the build time, or the instantiated prefab instance with MLSynchronizer.

Handling UI Events (Lua Only)

The Lua Event System can be used to register events from UI. The concept of registering a handler for UI events is exactly the same. In any script, you can register a handler for the events.

For example, consider following sample UI Canvas with a panel and a Button.

Registering UI event handlers

To add the event handler, like previous scenarios, we add the event handler first (in any script)

do -- UI 
local UIscript = LUA.script;

function onButtonClick()
Debug.Log("Button clicked");
end

function UIscript.Start()
LuaEvents.Add("onMyButtonClick", onButtonClick);
end
end

Again, so far it is the exact process.

Raising UI events

We need to connect the Canvas element to the Massive Loop Lua Event system. In order for the UI to register the user interactions in VR, we must add the SDKUI component to our canvas.

To do this, Select your canvas from the scene objects hierarchy, and in the inspector click on Add Component then select MassiveLoop > UI > SDKUI.

As you may notice, adding SDKUI component also adds a LuaUIEvent component automatically. This component can be used to raise UI events.

In our example, we want to register the click from the button, and raise the event we named "onMyButtonClick".

In order to do that, first, choose the Button from the scene hierarchy and find the On Click () box in the inspector.

Click on the + .

Here, we need to drag the Canvas object (Which has the LuaUIEvent component on it) and drop it to the empty field.

Now, from the function drop down, select LuaUIEvents > CallLuaEvent(string) .

Now on the empty field that opened, we can write the name of the event we want this button to trigger.

Done! Now whenever a user clicks this button, all the handlers registered under name of "onMyButtonClick" will be executed.

A word on event names

As we saw in previous sections, the event names are strings, which technically allows the event names have a little more flexibility. Although it is allowed to go wild on naming the event names, we recommend selecting a naming convention similar to camelCase, snake_case or, etc. that suits your project.

info

Under the hood, the event names are stored in a hash table. There is no string comparison used.