Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Multiple processing InputModules

Discussion in 'UGUI & TextMesh Pro' started by ThermalFusion, Nov 23, 2015.

  1. ThermalFusion

    ThermalFusion

    Joined:
    May 1, 2011
    Posts:
    906
    I'm trying to port our current UI solution over to uGUI. We require being able to use multitouch and mouse/kb input simultaneously. It appears that only one InputModule cause events at a time. Skimming through the source on bitbucket all modules seem to run UpdateModule each frame, but only the current one runs Process.
    Do I need to make my own input module that combines the functionality of the StandaloneInputModule and TouchInputModule?
     
  2. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,688
    The event system does poll all input modules individually. This means you may get separate updates from each system independently.
    If you need to process events concurrently (expecting multiple input systems to affect the same objects at the same time), then you would need to roll up your own Input System.

    Personally, I've not hit any issues with the separate modules and events.
     
  3. ThermalFusion

    ThermalFusion

    Joined:
    May 1, 2011
    Posts:
    906
    Hey @SimonDarksideJ , thanks for answering. Sure, interacting with the same object with both the mouse and touch input at the same time will cause conflicts, or even just several touches on a draggable object for instance. This is a problem I'm not looking to solve.
    What I want to achieve is being able to use the mouse to click on some buttons, then without having to toggle the StandaloneInputModule off and turning on the TouchInputModule tap some buttons on the touchscreen.
    Explicitly enabling both StandaloneInputModule and TouchInputModule doesn't help because only the current one is Processed.
    Look at:
    https://bitbucket.org/Unity-Technol...leviewer=file-view-default#EventSystem.cs-276
    Only m_CurrentInputModule is processed, not all active input modules, so I can't use both.
    I've worked around the issue by activating the StandaloneInputModule whenever the user clicks with the mouse, or activating the TouchInputModule whenever there are any touches, turning the other off, causing the current inputmodule to switch.
    This works to my ends, but I'd rather let them both run.
     
  4. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,688
    m_ CurrentInputModule (this is just used internally for the current input module being evaluated) is evaluated each loop for each input module. The input modules themselves are within another for loop within the eventsystem, this is how it's always been.
    Up until recently that some of the input modules were separate and it was only recently they were merged.

    You can have as many input modules attached to the event system as you like and they will all be evaluated at runtime.
     
  5. ThermalFusion

    ThermalFusion

    Joined:
    May 1, 2011
    Posts:
    906
    I am clearly not understanding this properly. You say that I should be getting events from both standalone and touch if I have them both active and enabled? Then why am I only getting them from one?
     
  6. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,688
    yes, you should get both.
    hmm, I'll do a little test project but I've never had an issue with both. In fact both are by default connected to an eventsystem when it is first created.

    Are both definitely enabled?
     
  7. ThermalFusion

    ThermalFusion

    Joined:
    May 1, 2011
    Posts:
    906
    I am running 5.2.3f1.
    I've triple checked this now, and they are definitely all enabled.
    I even added multiple StandaloneInputModules to see if I got multiple events, but no.

    If the UI source on bitbucket is the source of what is shipped with my version of Unity then I can see where the problem is. Assuming this is the case, here's a cut and paste of EventSystem.Update:
    Code (csharp):
    1.  
    2. protected virtual void Update()
    3. {
    4.     if (current != this)
    5.         return;
    6.  
    7.     TickModules();
    8.  
    9.     bool changedModule = false;
    10.     for (var i = 0; i < m_SystemInputModules.Count; i++)
    11.     {
    12.         var module = m_SystemInputModules[i];
    13.         if (module.IsModuleSupported() && module.ShouldActivateModule())
    14.         {
    15.             if (m_CurrentInputModule != module)
    16.             {
    17.                 ChangeEventModule(module);
    18.                 changedModule = true;
    19.             }
    20.             break;
    21.         }
    22.     }
    23.  
    24.     // no event module set... set the first valid one...
    25.     if (m_CurrentInputModule == null)
    26.     {
    27.         for (var i = 0; i < m_SystemInputModules.Count; i++)
    28.         {
    29.             var module = m_SystemInputModules[i];
    30.             if (module.IsModuleSupported())
    31.             {
    32.                 ChangeEventModule(module);
    33.                 changedModule = true;
    34.                 break;
    35.             }
    36.         }
    37.     }
    38.  
    39.     if (!changedModule && m_CurrentInputModule != null)
    40.         m_CurrentInputModule.Process();
    41. }
    42.  
    On line 7 TickModules is called:
    On line 40 Process is called on m_CurrentInputModule (Just the one, no looping)
    Code (csharp):
    1.  
    2. private void TickModules()
    3. {
    4.     for (var i = 0; i < m_SystemInputModules.Count; i++)
    5.     {
    6.         if (m_SystemInputModules[i] != null)
    7.             m_SystemInputModules[i].UpdateModule();
    8.     }
    9. }
    10.  
    Ok, so UpdateModule is called on all input modules in TickModules.
    What happens in UpdateModule?
    BaseInputModule:
    Code (csharp):
    1.  
    2. public virtual void UpdateModule()
    3. {}
    4.  
    PointerInputModule does not override it.
    StandaloneInputModule and TouchInputModule look the same:
    Code (csharp):
    1.  
    2. public override void UpdateModule()
    3. {
    4.    m_LastMousePosition = m_MousePosition;
    5.    m_MousePosition = Input.mousePosition;
    6. }
    7.  
    Nothing really happens here.
    All interesting logic is inside the various implementation of BaseInputModule.Process.
    As you can see in the first bit of code, it's only ever called on one InputModule once per frame.
     
  8. phil-Unity

    phil-Unity

    Unity UI Lead Developer

    Joined:
    Nov 23, 2012
    Posts:
    1,226
    As a FYI in 5.3 the input modules (touch and standalone) have been merged into one (now just called standalone likely should have changed the name) This was to better support devices such as the windows surface which uses both input methods.
     
    ThermalFusion and SimonDarksideJ like this.
  9. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,688
    Not sure I get you as Both TickModules() and the update code use the following
    Code (CSharp):
    1. for (var i = 0; i < m_SystemInputModules.Count; i++)
    2.     {
    Which means it loops through all available input modules, unless I'm sorely mistaken.

    Also the m_SystemInputModules is kept up to date in the behaviour routine UpdateModules():
    Code (CSharp):
    1. public void UpdateModules()
    2.         {
    3.             GetComponents(m_SystemInputModules);
    4.             for (int i = m_SystemInputModules.Count - 1; i >= 0; i--)
    5.             {
    6.                 if (m_SystemInputModules[i] && m_SystemInputModules[i].IsActive())
    7.                     continue;
    8.  
    9.                 m_SystemInputModules.RemoveAt(i);
    10.             }
    11.         }
    So each frame the available input modules are collected, then in update, they are each evaluated in turn.
     
  10. ThermalFusion

    ThermalFusion

    Joined:
    May 1, 2011
    Posts:
    906
    Thanks for sharing!
    That is so, and I'm not saying the opposite. But it's what's happening inside these loops that I'm trying to explain.
    But the only lines of code that leads on to causing UI events to be sent are these (At the bottom of EventSystem.Update)
    Code (csharp):
    1.  
    2. if (!changedModule && m_CurrentInputModule != null)
    3.     m_CurrentInputModule.Process();
    4.  
    This is not inside any other loop than the Update method itself, only happening one time per frame.
    Leading to only one InputModule being usable per frame. Is this not so?

    Edit: I realize the rest of the Update method is testing if another InputModule should be switched to. But my statement stays, only one ever has calls to Process() per frame.

    Edit2: And I just saw this is all explained in plaintext in the documentation. Jumped before I looked, sorry.
     
    Last edited: Nov 26, 2015
  11. Julien-Lynge

    Julien-Lynge

    Joined:
    Nov 5, 2010
    Posts:
    142
    I've been having this issue as well with VR. We are working on VR input modules that can be added and removed based on the system you're in. So for basic VR, we have a gaze-based input system. If you're in GearVR, we add an input module that adds gesture support on the side touch pad. If you're in Vive, we add an input module that handles the Vive controllers. And of course we want to keep the Standalone Input Module active to allow hotkeys and such when giving demos or testing.

    I had run into this issue during development, and figured I hadn't set something up properly, and just worked around it / ignored it during testing. Now that things are built, I'll find a way around it, not a _huge_ deal, but it's a bit annoying. @phil-Unity: what's the reasoning behind only running one input module at a time? Is it just that you started on a relatively simple platform (PC / mobile) and didn't think about systems like VR where you have all kinds of potential inputs? Or is there a technical reason why Unity only wants to support one module at a time? Would you guys consider a system where all enabled input modules run, and to deactive a module you disable its component?
     
  12. Julien-Lynge

    Julien-Lynge

    Joined:
    Nov 5, 2010
    Posts:
    142
    Quick update: managed to get things working with multiple input modules, and as I suspected, it was a bit of a pain.

    The main pieces are:

    1) you have to create a script to do essentially event forwarding to all the input modules
    2) the script has to set itself as the active input module
    3) the script has to keep the EventSystem from automatically reverting to the first input module it finds
    4) the script has to call Process() on all the other input modules

    For point 2, you have to use reflection:
    Code (CSharp):
    1. MethodInfo changeEventModuleMethod = EventSystem.current.GetType().GetMethod("ChangeEventModule", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(BaseInputModule) }, null);
    2.   changeEventModuleMethod.Invoke(current, new object[] { this });
    3.   EventSystem.current.UpdateModules();
    For point 3, you have to make sure the script is first in the list of event modules, and again you need reflection to access that list:
    Code (CSharp):
    1. private List<BaseInputModule> GetInputModules()
    2.         {
    3.             EventSystem current = EventSystem.current;
    4.             FieldInfo m_SystemInputModules = current.GetType().GetField("m_SystemInputModules", BindingFlags.NonPublic | BindingFlags.Instance);
    5.             return m_SystemInputModules.GetValue(current) as List<BaseInputModule>;
    6.         }
    7.         private void SetInputModules(List<BaseInputModule> inputModules)
    8.         {
    9.             EventSystem current = EventSystem.current;
    10.             FieldInfo m_SystemInputModules = current.GetType().GetField("m_SystemInputModules", BindingFlags.NonPublic | BindingFlags.Instance);
    11.             m_SystemInputModules.SetValue(current, inputModules);
    12.         }
    Once you have that, the processing each frame is relatively straightforward:
    Code (CSharp):
    1.         public override void Process()
    2.         {
    3.             List<BaseInputModule> activeInputModules = GetInputModules();
    4.             foreach (BaseInputModule module in activeInputModules)
    5.             {
    6.                 if (module == this)
    7.                     continue;
    8.  
    9.                 Debug.Log("Processing " + module.GetType().Name + " module");
    10.                 module.Process();
    11.             }
    12.         }
    For @phil-Unity, it would be great to allow this functionality within the event system. It could certainly be off by default, to not break projects and not confuse new users, but with a checkbox to "Allow multiple active input modules". You could also include an attribute that would allow users to specify an order for the modules to process, or use the script execution order settings. With the way the event system is put together now, it would be really easy to add this alternative mode - happy to help out if it means I can get rid of ugly reflection :)

    I think this is going to be a growing problem - I've seen a proliferation of input modules starting to appear as people experiment with inputs for the various VR systems.
     
    ThermalFusion likes this.
  13. shawnblais

    shawnblais

    Joined:
    Oct 11, 2012
    Posts:
    324
    Yes we're having the same problem. We're using a custom VR Input Module, to allow the VR user to use UGUI using a laser pointer, but we also want to support MouseControls, for the 'spectator' on the 2nd Screen.

    Using both StandaloneInputModule and VRInputModule doesn't work, one of them is always ignored.

    I wasn't able to solve things with the script above. A couple things weren't clear:
    1. Where do you place the code from point 2? I tried, awake, start, update... nothing seems to work.
    2. There's an error in "changeEventModuleMethod.Invoke(current)", current is undefined. I assume you mean EventSystem.current, and used that instead, but it's still not working.

    Here's the VR Input module we're using:
    https://github.com/VREALITY/ViveUGUIModule
     
  14. edwon

    edwon

    Joined:
    Apr 24, 2011
    Posts:
    265
    I'm having the same issue, but I'm using the awesome VR GUI input module available here

    http://wacki.me/blog/2016/06/vr-gui-input-module-for-unity-htc-vive/

    The Process method is only working about half the time, half to restart to get it to work again.

    I'm going to try implementing the code above but as someone else mentioned this really needs to be a fix on Unity's side!
     
  15. Cind13

    Cind13

    Joined:
    Jul 4, 2017
    Posts:
    5
    I know this thread is old, but I stumbled upon it when googling this problem so... for everyone that stumbles upon it after me, here is @Julien-Lynge solution as a plug-and-play BaseInputModule:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.EventSystems;
    5. using System.Reflection;
    6.  
    7. public class DistributingInputModule : BaseInputModule {
    8.  
    9.     private List<BaseInputModule> GetInputModules() {
    10.         EventSystem current = EventSystem.current;
    11.         FieldInfo m_SystemInputModules = current.GetType().GetField("m_SystemInputModules",
    12.             BindingFlags.NonPublic | BindingFlags.Instance);
    13.         return m_SystemInputModules.GetValue(current) as List<BaseInputModule>;
    14.     }
    15.  
    16.     private void SetInputModules(List<BaseInputModule> inputModules) {
    17.         EventSystem current = EventSystem.current;
    18.         FieldInfo m_SystemInputModules = current.GetType().GetField("m_SystemInputModules",
    19.             BindingFlags.NonPublic | BindingFlags.Instance);
    20.         m_SystemInputModules.SetValue(current, inputModules);
    21.     }
    22.  
    23.     public override void UpdateModule()    {
    24.         MethodInfo changeEventModuleMethod =
    25.             EventSystem.current.GetType().GetMethod("ChangeEventModule",
    26.             BindingFlags.NonPublic | BindingFlags.Instance, null,
    27.             new[] { typeof(BaseInputModule) }, null);
    28.             changeEventModuleMethod.Invoke(EventSystem.current, new object[] { this });
    29.         EventSystem.current.UpdateModules();
    30.         List<BaseInputModule> activeInputModules = GetInputModules();
    31.         activeInputModules.Remove(this);
    32.         activeInputModules.Insert(0, this);
    33.         SetInputModules(activeInputModules);
    34.     }
    35.  
    36.     public override void Process() {
    37.         List<BaseInputModule> activeInputModules = GetInputModules();
    38.         foreach (BaseInputModule module in activeInputModules)
    39.         {
    40.             if (module == this)
    41.                 continue;
    42.  
    43.             Debug.Log("Processing " + module.GetType().Name + " module");
    44.             module.Process();
    45.         }
    46.     }
    47. }
    Keep in mind, that this is dark magic and basically a hack. Once the Names of the reflected methods change or the way the EventSystem manages InputModules changes too much, this will be broken.
     
    Jeph_Diel, budukratok and ab_salazar like this.
  16. Andy3D

    Andy3D

    Joined:
    Jan 31, 2015
    Posts:
    42
    Hey Cind13,
    Thats great, thanks! Should this still work in Unity2019? I need something similar with my project as it needs to use both VR input and Touch / Mouse inputs. Ive created the above as a script - just wondering where I attach this now?
    cheers
     
  17. sh0v0r

    sh0v0r

    Joined:
    Nov 29, 2010
    Posts:
    325

    Awesome thanks guys, I just ran into this same issue with Rewired which I used to be able to rebind over 50 inputs in Lunar Flight and the OVRInput Module for Gaze based input.
     
  18. sh0v0r

    sh0v0r

    Joined:
    Nov 29, 2010
    Posts:
    325
    Place it on the same GameObject that has the InputModules on it, move it up the component order so it is before the Input Modules.
     
  19. guavaman

    guavaman

    Joined:
    Nov 20, 2009
    Posts:
    5,608
    The Rewired Standalone Input Module is based on Unity's Standalone Input Module. If anything changed, it was due to changes Unity made to the Standalone Input Module which were copied to the Rewired Standalone Input Module due to some functionality or other not working exactly the same was as the Unity Standalone Input Module pointed out by some user. Rewired's Standalone Input Module is not a complete rethink of the Unity Standalone Input Module. It is primarily designed to give you exactly the same features but allow you to get input from a Rewired Player instead of UnityEngine.Input. It has to stay in sync with Unity's module wherever possible.
     
  20. chrpetry

    chrpetry

    Joined:
    Mar 7, 2018
    Posts:
    65
    @Cind13:
    In addition to the DistributingInputModule code:
    Here an toString() suggestion to show current status in the preview window in the editor
    Code (CSharp):
    1.  
    2. ...
    3. public override string ToString()
    4.         {
    5.             var moduleStringList = new List<string>();
    6.             foreach (var module in GetInputModules())
    7.             {
    8.                 if (module == this)
    9.                     continue;
    10.  
    11.                 moduleStringList.Add(module.ToString());
    12.             }
    13.             return string.Join("\n\n", moduleStringList);
    14.         }
    15. ...
    16.  
     
  21. JoshValjosh

    JoshValjosh

    Joined:
    Dec 5, 2018
    Posts:
    3
    @Cind13 I had to modify your example a little because my input modules are on different GameObjects, but otherwise your drop-in solution saved me hours of work. Thank you.