Search Unity

Unity UI [SOLVED] Multiple Event System

Discussion in 'UGUI & TextMesh Pro' started by eron82, Jan 13, 2018.

  1. eron82

    eron82

    Joined:
    Mar 10, 2017
    Posts:
    83
    How can i have 2 Event Systems in the scene so 2 players can select different UI buttons to choose their character? (like tekken for example) Thanks
     
    Fenikkel likes this.
  2. eron82

    eron82

    Joined:
    Mar 10, 2017
    Posts:
    83
    Create 2 empty objects and add this script and a Standalone Input Model on both:
    Code (csharp):
    1. using UnityEngine.EventSystems;
    2. using UnityEngine;
    3.  
    4. public class MyEventSystem : EventSystem
    5. {
    6.  
    7. protected override void OnEnable(){
    8.         base.OnEnable ();
    9. }
    10.  
    11. protected override void Update(){
    12.     EventSystem originalCurrent = EventSystem.current;
    13.     current = this;
    14.     base.Update();
    15.     current = originalCurrent;
    16. }
    17. }
    Then add these two scripts on each button selecting one event systems:
    Code (csharp):
    1. using UnityEngine.EventSystems;
    2. using UnityEngine.UI;
    3.  
    4. public class MyButton : Button
    5. {
    6.     public EventSystem eventSystem;
    7.  
    8.     protected override void Awake()
    9.     {
    10.         base.Awake ();
    11.         eventSystem = GetComponent<MyEventSystemProvider> ().eventSystem;
    12.     }
    13.  
    14.     public override void OnPointerDown(PointerEventData eventData)
    15.     {
    16.         if (eventData.button != PointerEventData.InputButton.Left)
    17.             return;
    18.  
    19.         // Selection tracking
    20.         if (IsInteractable() && navigation.mode != Navigation.Mode.None)
    21.             eventSystem.SetSelectedGameObject(gameObject, eventData);
    22.  
    23.         base.OnPointerDown(eventData);
    24.     }
    25.  
    26.     public override void Select()
    27.     {
    28.         if (eventSystem.alreadySelecting)
    29.             return;
    30.  
    31.         eventSystem.SetSelectedGameObject(gameObject);
    32.     }
    33. }
    Code (csharp):
    1. using UnityEngine.EventSystems;
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class MyEventSystemProvider : MonoBehaviour
    6. {
    7.     public EventSystem eventSystem;
    8. }
     
    DigitalDesignDude likes this.
  3. AshMcConnell

    AshMcConnell

    Joined:
    Aug 10, 2011
    Posts:
    11
    Hi @eron82 ,

    Does this still work for you in recent versions of Unity? I'm having trouble getting it to work for my splitscreen game

    Thanks for your help!
     
    dthoma6403 likes this.
  4. dthoma6403

    dthoma6403

    Joined:
    Nov 15, 2017
    Posts:
    2
    Hey @AshMcConnell In the next few days I plan on giving this a shot if you still need help
     
  5. AshMcConnell

    AshMcConnell

    Joined:
    Aug 10, 2011
    Posts:
    11
    Only seeing this now. How did you get on? I made a very basic custom solution that allows me to move between controls on a page (only buttons and a car selector for the moment), but a "proper" solution would be great :)
     
  6. davdeo

    davdeo

    Joined:
    Aug 11, 2019
    Posts:
    4
    Hey @dthoma6403 did you already solve the problem?
    I am having the same problem as described above but i need to support 4 Players at the same time (local multiplayer character selection)
    Thank you in advance :)
     
  7. mr_blahblah

    mr_blahblah

    Joined:
    Jan 15, 2016
    Posts:
    38
    Anyone else still having this problem? I am, and the solution above doesn't work for me, unless you need to replicate this for each individual button...
     
  8. DigitalDesignDude

    DigitalDesignDude

    Joined:
    Mar 25, 2020
    Posts:
    3
    It's working alright for my project :).

    Don't know if using unity 2017 affects this or not but here are some additional instructions that might help.


    Create 2 empty objects and add this script and a Standalone Input Model on both. these will act as event systems for each player. Delete any other event systems in your scene to prevent issues.

    Code (CSharp):
    1. using UnityEngine.EventSystems;
    2. using UnityEngine;
    3. public class MyEventSystem : EventSystem
    4. {
    5. protected override void OnEnable(){
    6.         base.OnEnable ();
    7. }
    8. protected override void Update(){
    9.     EventSystem originalCurrent = EventSystem.current;
    10.     current = this;
    11.     base.Update();
    12.     current = originalCurrent;
    13. }
    14. }
    In the standalone input components, assign the horizontal and vertical axis, submit and cancel buttons, to the unique inputs your players are using. They shouldn't be using the same inputs for this to work.


    For each button that player 1 controls, add the following scripts then assign player 1's event system object as the event system in the "My Event System Provider Script".

    You'll also have to remove the old button script sadly and customize your buttons' colours again.

    Repeat this for all of player's 2 buttons except you'll use player 2's event system obj instead.

    Code (CSharp):
    1. using UnityEngine.EventSystems;
    2. using UnityEngine;
    3. using System.Collections;
    4. public class MyEventSystemProvider : MonoBehaviour
    5. {
    6.     public EventSystem eventSystem;
    7. }

    Code (CSharp):
    1. using UnityEngine.EventSystems;
    2. using UnityEngine.UI;
    3. public class MyButton : Button
    4. {
    5.     public EventSystem eventSystem;
    6.     protected override void Awake()
    7.     {
    8.         base.Awake ();
    9.         eventSystem = GetComponent<MyEventSystemProvider> ().eventSystem;
    10.     }
    11.     public override void OnPointerDown(PointerEventData eventData)
    12.     {
    13.         if (eventData.button != PointerEventData.InputButton.Left)
    14.             return;
    15.         // Selection tracking
    16.         if (IsInteractable() && navigation.mode != Navigation.Mode.None)
    17.             eventSystem.SetSelectedGameObject(gameObject, eventData);
    18.         base.OnPointerDown(eventData);
    19.     }
    20.     public override void Select()
    21.     {
    22.         if (eventSystem.alreadySelecting)
    23.             return;
    24.         eventSystem.SetSelectedGameObject(gameObject);
    25.     }
    26. }

    Finally set all of your button's navigation in the "My Button (Script)" component to "Explicit" navigation so that you control the order in which buttons are selected. Otherwise, player 2 may be able to select player 1's buttons by accident due to unity's odd automatic button navigation.
     
    Fenikkel and bubika like this.
  9. AwesomePossum

    AwesomePossum

    Joined:
    Feb 15, 2014
    Posts:
    6
    I'm joining the party a bit late, trying to use the new Input System to be able to use a variable number of players... So not sure if the above solution will work for me as the buttons and player objects won't be around until the player joins, but hopefully...
     
  10. Eden_

    Eden_

    Joined:
    Jan 4, 2019
    Posts:
    2
    I know this is a bit an old topic but I would rather make my own solution when using multiple controllers. This is using the old input system, probably the new one has its own implementation. But, you could just remove any StandAloneInputModule and leave an EventsSystem. Then the idea is to implement an interface for any UI element that contains selectables. i.e. ISelectableUI. There you could define the starting UI element that will be selected among other things that you might come up useful. Then, create an input manager (i.e. PlayerInputManager) and a class where you will handle this ISelectableUI.

    This would be how this class where you handle each UIElement that has a ISelectableUI looks like:

    Code (CSharp):
    1. public class UserInputEventsHandler
    2.         {
    3.             Selectable currentSelected = null;
    4.             ISelectableUI currentUI = null;
    5.             InputSchema userInputSchema;
    6.  
    7.             public UserInputEventsHandler(InputSchema _userInputSchema)
    8.             {
    9.                 userInputSchema = _userInputSchema;
    10.             }
    11.  
    12.             public void Update()
    13.             {
    14.                 float verticalAxisValue = GameManager.PlayerInput.GetAxisValue(PlayerInput.GetAxis(userInputSchema, InputType.NavigationVerticalAxis));
    15.                 float horizontalAxisValue = GameManager.PlayerInput.GetAxisValue(PlayerInput.GetAxis(userInputSchema, InputType.NavigationHorizontalAxis));
    16.                 if (verticalAxisValue > 0.15f && Mathf.Abs(horizontalAxisValue) < 0.05f) SelectOnUp();
    17.                 else if (verticalAxisValue < -0.15f && Mathf.Abs(horizontalAxisValue) < 0.05f) SelectOnDown();
    18.                 else if (Mathf.Abs(verticalAxisValue) < 0.05f && horizontalAxisValue > 0.15f) SelectOnRight();
    19.                 else if (Mathf.Abs(verticalAxisValue) < 0.05f && horizontalAxisValue < -0.15f) SelectOnLeft();
    20.             }
    21.  
    22.             public void Subscribe(ISelectableUI selectableUI)
    23.             {
    24.                 currentUI = selectableUI;
    25.                 Select(selectableMenu.SetInitialSelectable());
    26.  
    27.                 if (userInputSchema == InputSchema.KeyboardAndMouse)
    28.                 {
    29.                     KeyCode confirmKey = PlayerInput.GetKeyCode(userInputSchema, InputType.Confirm);
    30.                     GameManager.PlayerInput.SubscribeToOnKeyDown(confirmKey, Confirm);
    31.                 }
    32.                 else if (userInputSchema == InputSchema.Gamepad)
    33.                 {
    34.                     string confirmButtonName = PlayerInput.GetButtonName(userInputSchema, InputType.Confirm);
    35.                     GameManager.PlayerInput.SubscribeToOnButtonDown(confirmButtonName, Confirm);
    36.                 }
    37.             }
    38.             public void UnSubscribe()
    39.             {
    40.                 currentSelected = null;
    41.                 currentMenu = null;
    42.  
    43.                 if (userInputSchema == InputSchema.KeyboardAndMouse)
    44.                 {
    45.                     KeyCode confirmKey = PlayerInput.GetKeyCode(userInputSchema, InputType.Confirm);
    46.                     GameManager.PlayerInput.UnSubscribeToOnKeyDown(confirmKey, Confirm);
    47.                 }
    48.                 else if (userInputSchema == InputSchema.Gamepad)
    49.                 {
    50.                     string confirmButtonName = PlayerInput.GetButtonName(userInputSchema, InputType.Confirm);
    51.                     GameManager.PlayerInput.UnSubscribeToOnButtonDown(confirmButtonName, Confirm);
    52.                 }
    53.             }
    54.  
    55.             public ISelectableUI GetCurrentUI() => currentUI;
    56.             public Selectable GetCurrentSelected() => currentSelected;
    57.  
    58.             void Confirm()
    59.             {
    60.                 if (currentSelected == null) return;
    61.                 ExecuteEvents.Execute(currentSelected.gameObject, new BaseEventData(EventSystem.current), ExecuteEvents.submitHandler);
    62.             }
    63.             void Select(Selectable newSelectable)
    64.             {
    65.                 if (newSelectable == null) return;
    66.                 if (currentSelected == newSelectable) return;
    67.                 if (currentSelected != null)
    68.                     ExecuteEvents.Execute(currentSelected.gameObject, new BaseEventData(EventSystem.current), ExecuteEvents.deselectHandler);
    69.                 currentSelected = newSelectable;
    70.                 ExecuteEvents.Execute(currentSelected.gameObject, new BaseEventData(EventSystem.current), ExecuteEvents.selectHandler);
    71.             }
    72.  
    73.             public void SelectOnRight()
    74.             {
    75.                 if (currentSelected == null) return;
    76.                 Select(currentSelected.FindSelectableOnRight());              
    77.             }
    78.             public void SelectOnLeft()
    79.             {
    80.                 if (currentSelected == null) return;
    81.                 Select(currentSelected.FindSelectableOnLeft());
    82.             }
    83.             public void SelectOnUp()
    84.             {
    85.                 if (currentSelected == null) return;
    86.                 Select(currentSelected.FindSelectableOnUp());
    87.             }
    88.             public void SelectOnDown()
    89.             {
    90.                 if (currentSelected == null) return;
    91.                 Select(currentSelected.FindSelectableOnDown());
    92.             }
    93.         }
    Leaving a part some parts of the code that are not explained, the idea is that you use the ExecuteEvents.Execute to Select, Deselect and Use any Selectable, but you are not using the EventsSystem itself to handle which Selectable is selected. This allows you to have two UIElements at the same time handled by two different controllers. Note that you would have as many instances of this class as inputSchemas you have. In my case I have two and I use a dictionary with the InputSchema as the key to look for this.
    Also note that I have a PlayerInput static class where I keep the reference of the keys/buttons that I can look at from the inputSchema that the player is using.