Unity Tips & Tricks: Multiple Event Systems in a Single Scene (Unity 5.1)

| September 7, 2015 | 5 Replies

Yup, that’s correct, having multiple event systems in a single scene is possible. It requires a bit of trickery, but in the end, the job gets done.
Here’s some background. I’ve been making a 2D shoot ’em up game in Unity. The primary goal is to learn how to use the different Unity APIs and subsystems. One of the features I liked a lot initially was the new GUI framework. However, this quickly turned into frustration when I decided to add a local coop mode for my game. Here’s the problem: I have a character selection screen before loading the main level. Everything was fine and dandy when only one player had to choose a character, but adding a second player to the equation proved difficult.
You see, the main problem with the GUI framework is that only one EventSystem is allowed per scene. Now, I’m not going to go into too much detail, but I feel I have to do a little explanation. The EventSystem is responsible for choosing an appropriate InputModule and keeping track of the currently selected GUI GameObject. There are two problems with this setup:

  1. I can’t set separate axes for the second player
  2. An EventSystem can only track a single selected object. Moreover, the Unity UI code for all Selectable behaviours relies on the static variable EventSystem.current. EventSystem.current is set by the EventSystem itself inside OnEnable(). If the variable has already been set, the EventSystem does not work at all!

So if I want the second player to have separate controls for the UI and to have a separate selected GameObject, it seems I need another EventSystem dedicated to the second player.

My solution

After delving into the source code of the UI framework I was able to determine how to do just that.
First, I extended EventSystem so that it doesn’t require to be the single EventSystem in the scene.

public class MyEventSystem : EventSystem
{
    protected override void OnEnable()
    {
        // do not assign EventSystem.current
    }

    protected override void Update()
    {
        EventSystem originalCurrent = EventSystem.current;
        current = this; // in order to avoid reimplementing half of the EventSystem class, just temporarily assign this EventSystem to be the globally current one
        base.Update();
        current = originalCurrent;
    }
}

OnEnable() has been overriden in order to avoid assigning the extended EventSystem to EventSystem.current. EventSystem.current must be the original EventSystem which will continue to reside in the scene for compatibility reasons.
The original Update() is quite complex, calling multiple private methods. However, the first thing that the original Update() method does, is to check whether this EventSystem is EventSystem.current. If the assertion fails, Update() immediately returns and nothing gets done. Because of that, I had to temporarily assign EventSystem.current to be this EventSystem. This operation is safe, because Unity executes these methods in a single thread and a concurrency issue seems impossible.

After all that, I extended Button and Slider. The code for both classes is absolutely the same, because methods of the Selectable class have to be overriden. Selectable is parent to both Button and Slider. Here’s the Button code:

public class MyButton : Button
{
	public EventSystem eventSystem;

	public override void OnPointerDown(PointerEventData eventData)
	{
		if (eventData.button != PointerEventData.InputButton.Left)
			return;

		// Selection tracking
		if (IsInteractable() && navigation.mode != Navigation.Mode.None)
			eventSystem.SetSelectedGameObject(gameObject, eventData);

		base.OnPointerDown(eventData);
	}

	public override void Select()
	{
		if (eventSystem.alreadySelecting)
			return;

		eventSystem.SetSelectedGameObject(gameObject);
	}
}

Basically, these are the two methods that have to be overriden in order to use the EventSystem dedicated to a player. The Select() method is an exact copy of the base method, however EventSystem.current has been replaced by the member variable eventSystem. OnPointerDown does almost the same, but also calls the base method in the end. The base method calls a bunch of private methods.

Setting everything up

The first step in the setup is to leave the original vanilla Unity EventSystem in the scene. However, it must not be able to do anything. That’s why all InputModule scripts in the EventSystem game object have to be disabled or removed. The “First Selected Game Object” property has to be left empty.
Then, add two empty game objects which will host the new event systems. Add a MyEventSystem component to both objects. Also, add a StandaloneInputModule and TouchInputModule (if needed) to both objects. Configure each StandaloneInputModule with the axes for the corresponding player.
After that, for every button you add (or have already added) to the scene, remove the Button component from the game object and instead add a MyButton component. The editor gui will be as if using the original Button script. Unfortunately, this means that there isn’t a box to select the EventSystem. My quick & dirty solution to this was to just create a simple script like this one:

public class EventSystemProvider : MonoBehaviour
{
	public EventSystem eventSystem;
}

and add it as a component to the game object hosting the button. It is used solely for the input box. In order to use this component, the MyButton class has to be modified a little:

// other code ommitted for clarity
public class MyButton : Button
{
	public EventSystem eventSystem;

	protected override void Awake()
	{
		base.Awake();
		eventSystem = GetComponent<EventSystemProvider>().eventSystem;
	}
}

With that done, just drag the desired event system object into the box to tell the button to use it.
The methodology for Slider and other Selectable widgets is analogous.

This solution to the problem might not be the best one, but it works and requires little effort. I hope that the Unity team will provide first class support for multiple player input in the future.

Tags: , , , ,

Category: Development

About the Author ()

Comments (5)

Trackback URL | Comments RSS Feed

  1. Tibi says:

    Hi,
    Check out this uGUI fork
    https://bitbucket.org/TobiasPott/ui-5.x/src/b2a12d91a471a0f7967bc03542e80e9dd2027ad5?at=5.0

    I tried it and it works. It lets you add as many Event Systems as you wish and give them an unique int id. Then you can add an EventSystemGroup component to every game object you wish to be handled by a specific event system (it’s children will oblige too).

  2. GamedevBear says:

    Thanks for this! Worked like a charm. There’s already a pull request for adding multiplayer support to Unity UI, so hopefully it is added in 5.2

  3. AL says:

    This seems pretty genius!

    I will give it a shot soon.

    Have you encountered any problems with this approach that I should take in to count?

    Thanks in advance. This really made my day!

Leave a Reply