Search Unity

Simulating input via code?

Discussion in 'Input System' started by Jingle-Fett, Apr 14, 2016.

  1. Jingle-Fett

    Jingle-Fett

    Joined:
    Oct 18, 2009
    Posts:
    614
    I've played around a little bit with the Experimental build and it's pretty badass! It's sort of similar to the system I built for my own game, but way better. There's something I'm wondering about though, the ability to simulate inputs via code.

    I attached a simple diagram so you can see the structure of my custom system. The general idea is very similar to what you've implemented, where in-game avatars, vehicles, etc. take the PlayerInput script as a variable (in my system it's called "VirtualPlayer"). The difference though is that VirtualPlayer receives its input from either a human controlled input device (like mouse+keyboard or gamepad) or simulated inputs generated in an AI script. In NIS, it'd be the equivalent of the AI script activating actions in the Action Map and then that action map being sent to the PlayerInput script.

    My question is can this be done with the current build? And if not, is it something that could be added as a feature?
    InputSystem Diagram.jpg
     
    odaimoko and ModLunar like this.
  2. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    You can simulate input of devices, but we don't have a system for simulating actions directly, circumventing the concept of where it comes from entirely. However, we've had multiple requests for that now and I think it makes a lot of sense.

    If you could choose freely, what kind of API would you like to use for this? Can you write some imaginary example code of how it would be used? If not, that's ok too; we'll come up with something. Just looking for input and ideas if you should have any.
     
  3. Jingle-Fett

    Jingle-Fett

    Joined:
    Oct 18, 2009
    Posts:
    614
    Oh yeah absolutely!

    My thought is something along the lines of creating an instance of the Action Map through code that behaves like the mecanim Animation Override Controller, which we can control at runtime.

    In my custom system I have an abstract class called VirtualButton that holds the variables for each Action (like booleans for OnButtonDown, OnButton, OnButtonUp, etc) and then I hold all of these in a second class that has an array of VirtualButton called VirtualController.

    So then in my HumanInput script (which I attach to a singleton object) I hold VirtualController class as a variable and I do a conversion. So the HumanInput script has stuff like this:

    In NIS it seems like I'd no longer have to manually do that though since it's handled by the Action Map (which is great!). So then in the case of an AI bot in my system it's currently something like this:
    From there I send the instance of VirtualController to the VirtualPlayer script and from there to whatever avatar script that needs it.

    So for NIS I was thinking what if we had something similar to the mecanim Animation Override Controller for manually controlling the variables. With the mecanim override controller, the FSM is identical to the original one, but you're able to replace the animation clips with new ones.

    So in my AI bot script with the hypothetical API, I'm thinking it might look something like this:
    And then this ActionMap override can be sent to the NIS PlayerInput script just like a regular one.
     
    ben-rasooli and bsterling250DI like this.
  4. ben-rasooli

    ben-rasooli

    Joined:
    May 1, 2014
    Posts:
    40
    @runevision I would like to have a API like these:

    public static void Input.simulatePointerDown( Vector2 pointerPsition );

    public static void Input.simulatePointerUp( Vector2 pointerPsition );

    public static void Input.simulatePointerMove( Vector2 pointerPsition, Vector2 deltaPosition);
    deltaPosition adds to the pointerPosition to get a new position. To simulate a drag action, user can keep calling this method in the Update method and each time just pass in a deltaPosition.

    public static void Input.triggerTouchEvent( TouchPhase touchPhase, Vector2 touchPosition = Vector2.zero );
     
    Last edited: May 27, 2016
    ModLunar likes this.
  5. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    Thanks for the suggestion. You can simulate these things already by creating events and queuing them. See this example code:
    Code (CSharp):
    1.  
    2. var inputEvent = InputSystem.CreateEvent<PointerMoveEvent>();
    3. inputEvent.deviceType = typeof(Mouse);
    4. inputEvent.deviceIndex = 0;
    5. inputEvent.delta = myMouseDelta;
    6. inputEvent.position = myMousePosition;
    7. InputSystem.QueueEvent(inputEvent);
    8.  
    9. var inputEvent = InputSystem.CreateEvent<GenericControlEvent>();
    10. inputEvent.deviceType = typeof(Mouse);
    11. inputEvent.deviceIndex = 0;
    12. inputEvent.controlIndex = 0;
    13. inputEvent.value = clicked ? 1.0f : 0.0f;
    14. InputSystem.QueueEvent(inputEvent);
    We are probably not going to do a more specialized mouse simulation API like you suggest since we want all the input system APIs to work for arbitrary devices and not be hard-coded for specific device types such as mice.
     
    EZaca, ModLunar and ben-rasooli like this.
  6. ben-rasooli

    ben-rasooli

    Joined:
    May 1, 2014
    Posts:
    40
    I was actually thinking of a touch input pointer when I was writing those imaginary codes :)
    I think in many cases user needs somehow to point at a point in the game world before she can execute her commands. Having a 'Input.simulatePointer(position)' is pretty generic regardless of what kind of input device is being used. I think we have only two options when a pointing attempt is detected. The detection can be either a boolean or a rang. For example, in a touch input we can either say a TouchDown detected or a TouchPressure detected.
    An API like this can handle many input devices:
    public void static Input.simulatePointerDetection(Vector3 position, float engagementLevel = 1.0f, Vector3 deltaPosition = Vector3.zero);
    public void static Input.simulatePointerDisengaged(Vector3 position);
    I'm using Vector3 for position in case an input device can provide a position in 3D space.
    If deltaPosition is provided, then it should be added the position to simulate a pointer move functionality.
     
  7. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    Well, it doesn't matter. We don't want the core of the Input API to know about mouse or pointer or anything like that. It's meant to be a generic framework.

    There could definitely be a different class with this functionality, that's not part of the core module of the input system. For example PointerSimulation.simulatePointerDetection. However, it's not our priority to work on these since simulating events is not the biggest use cases of the input system, and for those who do need to do it it can be done already, and writing simpler wrappers around it shouldn't be too hard.
     
    jaredhuff85 likes this.
  8. ben-rasooli

    ben-rasooli

    Joined:
    May 1, 2014
    Posts:
    40
    I know this is not your priority to work on these, but you asked for it on your first post "If you could choose freely, what kind of API would you like to use for this?" and I was just giving my opinion. I really don't care if you wanna do it or not.
     
  9. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    Sorry if I was being unclear. I was asking what API someone might want for simulating actions directly, independent of the devices it's coming from. What you talked about is not simulating actions directly but rather simulating device input (that an ActionMap might then convert into actions).
     
    ben-rasooli likes this.
  10. Jingle-Fett

    Jingle-Fett

    Joined:
    Oct 18, 2009
    Posts:
    614
    @runevision If I may ask, what's the current state of the new input system right now, have there been any new developments or updates? I'd be very interested in using NIS for my upcoming game but would need the ability to simulate the the input actions in order to do so
     
  11. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    We're currently working on rewriting the prototype into a proper system integrated with Unity (in the same way e.g. the UI system is).

    We don't have a solution yet for simulating actions, but I think we'll come up with something sensible.
     
    usenkod and Jingle-Fett like this.
  12. Deleted User

    Deleted User

    Guest

    Hi,

    Can you please provide an update on this?
     
  13. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    You can make up events for any existing device or create your own devices and feed them input.

    Code (CSharp):
    1. // Update position of current mouse.
    2. InputSystem.QueueDeltaStateEvent(Mouse.current.position, new Vector2(123, 234));
    3.  
    4. // Create a fake mouse and change its position.
    5. var myMouse = InputSystem.AddDevice<Mouse>();
    6. myMouse.QueueDeltaStateEvent(myMouse.position, new Vector2(234, 345);
    7.  
    8. // Update the entire state of the mouse.
    9. InputSystem.QueueStateEvent(myMouse, new MouseState { ... });
    10.  
    11. // Grab some arbitrary device of whatever type and update its "firstButton" and "dpad/up" to be pressed.
    12. var device = InputSystem.devices[1];
    13. InputEventPtr eventPtr;
    14. using (StateEvent.From(device, out eventPtr))
    15. {
    16.     ((ButtonControl) device["firstButton"]).WriteValueIntoEvent(1, eventPtr);
    17.     ((ButtonControl) device["dpad/up"]).WriteValueIntoEvent(1, eventPtr);
    18.     InputSystem.QueueEvent(eventPtr);
    19. }
    Going back to the original post, you could use this to, for example, create a gamepad, assign it to a player, and then make up input to control the player.
     
    MikeGDev, Nevey, NotaNaN and 3 others like this.
  14. usenkod

    usenkod

    Joined:
    Jul 29, 2017
    Posts:
    2
    Hey Guys,

    I wonder if you had any progress since 2016 (https://github.com/Unity-Technologi...ster/FakePrototypeStuff/MouseInputToEvents.cs) or shall I expect such features in a new Input System which is still work in progress?

    My aim is to simulate input on any OS, device causing input simulation does not matter. The easiest and best option I see is to call smth like was suggested before "SetKeyDown(KeyCode.Whatever)" or in case of other inputs like gamepads methods of type "SetButtonDown/SetAxis("m_name")" using Unity Input Manager axes or/and "SetButtonDown/SetAxis("joystick 0 button 0")" using gamepad and button indexes.

    Edit: ofc it makes sense only if those events affect Input.GetKey/Button/Axis. In my case it would be enough to simply override Input.GetKey/Button/Axis result by sending such events (so it is platform and device independent)

    Thank you for your time.
     
    Last edited: Jun 25, 2019
  15. ControlNet

    ControlNet

    Joined:
    Dec 3, 2019
    Posts:
    1
    I do not know why, but for some reason, the line

    Code (CSharp):
    1. ((ButtonControl) device["firstButton"]).WriteValueIntoEvent(1, eventPtr);
    produces compile error.
    The code that actualy simulates pressing the left mouse button needs to pass a float value:

    Code (CSharp):
    1. ((ButtonControl)device["leftButton"]).WriteValueIntoEvent(1f, eventPtr);
    although in the mouse profile I can see that the left button is the 0-bit in a 24bit integer, so I would also expect to pass an int as a value.
    In my mouse profile the left button is named "leftButton" instead of the "firstButton", but that depends on the profile.
    Tested on Unity 2019.3.9f1.
     
  16. sbethge

    sbethge

    Joined:
    Mar 20, 2019
    Posts:
    16
  17. ben-rasooli

    ben-rasooli

    Joined:
    May 1, 2014
    Posts:
    40
    I guess those methods are part of Unity.InputSystem.TestFramework assembly. I don't think I would want to include that in my production build.
     
  18. sbethge

    sbethge

    Joined:
    Mar 20, 2019
    Posts:
    16
    That would hold true for anything test related. Your own test assembly (with this as dependency) should not be part of the build which I think is achieved using the UNITY_INCLUDE_TESTS Constraint set in the test assembly.
     
    Last edited: Jun 30, 2020
  19. ModLunar

    ModLunar

    Joined:
    Oct 16, 2016
    Posts:
    374
    Wow.

    The ability to create devices and simulate input like this is extremely powerful.
    I'm using this to simulate a client's input on my server (to have it go through server-authoritative validation).

    Thanks so much!
     
    NotaNaN likes this.
  20. huulong

    huulong

    Joined:
    Jul 1, 2013
    Posts:
    224
    I noticed the same thing. However I tried WriteValueIntoEvent<int>(1, eventPtr) and although it compiles, it sends a runtime error "System.ArgumentException : Expecting control of type 'Int32' but got 'KeyControl'"

    The reason is that KeyControl < ButtonControl < AxisControl < InputControl<float> and therefore they still expect a float.
    I'm not sure about the use case (pressure-sensitive buttons?), but that's how input values seem to be stored anyway.
     
  21. huulong

    huulong

    Joined:
    Jul 1, 2013
    Posts:
    224
    I managed to integrate this to Play Mode testing to do an automated run of the game.

    However I have some troubles with exact input timing:
    - I need to release my key after pressing it so I can press it again (also I need to cleanup any sticky keys at the end of the test, else the next test of Enter Play will still have that sticky key and the next key press will fail!)
    - I need to wait at least 1 frame before sending the release (WriteValueIntoEvent(0f, eventPtr)) event, else it just cancels the press entirely
    - waiting frames with yield return null is not reliable enough. Multiple yield return null seem to merge into a single one (at least in test IEnumerator), while WaitForFixedUpdate can be stacked, but sometimes 1 frame is not enough to release the button properly after pressing it. Maybe QueueEvent is somewhat late? Or sends the events in the wrong order? I end up with a sticky key in the middle of the test and one of the key press is not handled properly. Or the key press event is overwritten by the key release event and nothing happens.

    For now I must WaitForFixedUpdate enough times (3+) or use WaitForSeconds with a time long enough to make sure my test is working.

    Example of helper methods I'm using:

    Code (CSharp):
    1.         private static void SimulateSetInputValue(InputDevice inputDevice, string inputPath, float inputValue)
    2.         {
    3.             InputEventPtr eventPtr;
    4.             using (StateEvent.From(inputDevice, out eventPtr))
    5.             {
    6.                 float currentInputValue = ((InputControl<float>) inputDevice[inputPath]).ReadValue();
    7.                 if (currentInputValue != inputValue)
    8.                 {
    9.                     inputDevice[inputPath].WriteValueIntoEvent(inputValue, eventPtr);
    10.                     InputSystem.QueueEvent(eventPtr);
    11.                 }
    12.                 else
    13.                 {
    14.                     Debug.LogWarningFormat("Trying to set input value of {0} to {1}, but it is already so.",
    15.                         inputPath, inputValue);
    16.                 }
    17.             }
    18.         }
    19.      
    20.         private static void SimulatePressInput(InputDevice inputDevice, string inputPath)
    21.         {
    22.             SimulateSetInputValue(inputDevice, inputPath, 1f);
    23.         }
    24.  
    25.         private static void SimulateReleaseInput(InputDevice inputDevice, string inputPath)
    26.         {
    27.             SimulateSetInputValue(inputDevice, inputPath, 0f);
    28.         }
    29.      
    30.         private static IEnumerator SimulateShortPressInput(InputDevice inputDevice, string inputPath)
    31.         {
    32.             SimulatePressInput(inputDevice, inputPath);
    33.          
    34.             // WaitForFixedUpdate better than null, but you may need several!
    35.             for (int i = 0; i < 3; i++)
    36.             {
    37.                 yield return new WaitForFixedUpdate();
    38.              
    39.             }
    40. //            yield return null;
    41.             SimulateReleaseInput(inputDevice, inputPath);
    42.             yield return new WaitForFixedUpdate();
    43. //            yield return null;
    44.         }
     
    BlocCole likes this.
  22. NotaNaN

    NotaNaN

    Joined:
    Dec 14, 2018
    Posts:
    325
    Hmm. This is really useful stuff. (I wish there was something in the documentation about being able to do this).

    Question though —
    Is there a way to simulate inputs at the InputAction level?

    For instance, is it possible to maybe do something like:
    Code (CSharp):
    1. var action = new InputAction();
    2.  
    3. action.WriteValue<float>(0.5f);
    or similar?

    Having to use an Input Device is kinda annoying — in most cases I simply want to trigger / set the value of InputActions directly and not have to worry about the device and how it interacts with the InputAction in question.

    My usecase, specifically, is simulating inputs for Artificial intelligence. (An AI does not care if it is using a Keyboard / Mouse or a Gamepad. All it wants to do is write values to its own InputActionAsset).
     
  23. ModLunar

    ModLunar

    Joined:
    Oct 16, 2016
    Posts:
    374
    @GliderGuy I agree.

    I'd like to know too -- this would be very powerful in letting us re-use code, regardless of "who's" providing the input (a real player, or an AI-controlled character), with the flexibility of InputActions rather than coupling to the nitty gritty input details.
     
    kta25298 and NotaNaN like this.
  24. pitchblende

    pitchblende

    Joined:
    Jan 9, 2020
    Posts:
    71
    The ability to "mock" player inputs via code would be useful to me too - perhaps it's possible to write a input pseudo-device with an API that can be driven by code? Did anyone figure out how to do this?
     
  25. Stevens-R-Miller

    Stevens-R-Miller

    Joined:
    Oct 20, 2017
    Posts:
    676
    Quite. Rene's code from Jan 9, 2019 has no effect on my system:

    Code (CSharp):
    1. InputSystem.QueueDeltaStateEvent(Mouse.current.position, new Vector2(123, 234));
    Does this still work for anyone else?
     
  26. Stevens-R-Miller

    Stevens-R-Miller

    Joined:
    Oct 20, 2017
    Posts:
    676
    Well, it doesn't work when called from an event handler, but it DOES work when called from the Update method.

    This does NOT work:
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.InputSystem;
    3. using UnityEngine.InputSystem.LowLevel;
    4.  
    5. public class SimGamepad : MonoBehaviour
    6. {
    7.     public InputAction mouseAction;
    8.     public InputAction keyAction;
    9.  
    10.     void Start()
    11.     {
    12.         mouseAction.Enable();
    13.         mouseAction.performed += (x) => Debug.Log("Mouse Action Performed");
    14.  
    15.         keyAction.Enable();
    16.         keyAction.performed += KeyDown;
    17.  
    18.         mouseAction.AddBinding(Mouse.current.position);
    19.         keyAction.AddBinding(Keyboard.current.pKey);
    20.     }
    21.  
    22.     private void KeyDown(InputAction.CallbackContext ctx)
    23.     {
    24.         using (StateEvent.From(Mouse.current, out var eventPtr))
    25.         {
    26.             Mouse.current.position.WriteValueIntoEvent(new Vector2(123, 234), eventPtr);
    27.             InputSystem.QueueEvent(eventPtr);
    28.         }
    29.     }
    30. }

    This DOES work:
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.InputSystem;
    3. using UnityEngine.InputSystem.LowLevel;
    4.  
    5. public class SimGamepad : MonoBehaviour
    6. {
    7.     public InputAction mouseAction;
    8.     public InputAction keyAction;
    9.  
    10.     bool isPending;
    11.  
    12.     void Start()
    13.     {
    14.         mouseAction.Enable();
    15.         mouseAction.performed += (x) => Debug.Log("Mouse Action Performed");
    16.  
    17.         keyAction.Enable();
    18.         keyAction.performed += (x) => isPending = true;
    19.  
    20.         mouseAction.AddBinding(Mouse.current.position);
    21.         keyAction.AddBinding(Keyboard.current.pKey);
    22.     }
    23.  
    24.     private void Update()
    25.     {
    26.         if (isPending)
    27.         {
    28.             using (StateEvent.From(Mouse.current, out var eventPtr))
    29.             {
    30.                 Mouse.current.position.WriteValueIntoEvent(new Vector2(123, 234), eventPtr);
    31.                 InputSystem.QueueEvent(eventPtr);
    32.             }
    33.  
    34.             isPending = false;
    35.         }
    36.     }
    37. }
    38.  
    Both are supposed to "fake" a mouse movement when you press the "P" key on the keyboard. But only the second snippet does this. (Note that, if you try it, you have to move the mouse between successive pressings of the "P" key, or else the Input System thinks the mouse has stayed at (123, 234), so it doesn't call your listener.)
     
  27. Stevens-R-Miller

    Stevens-R-Miller

    Joined:
    Oct 20, 2017
    Posts:
    676
    Further to the above, I sure hope someone can help me with this one.

    The second code snippet in my previous post does seem to work. I modified it to simulate the movement of a head-mounted device. Here's that version:
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.InputSystem;
    3. using UnityEngine.InputSystem.XR;
    4. using UnityEngine.InputSystem.LowLevel;
    5.  
    6. public class SimGamepad : MonoBehaviour
    7. {
    8.     public InputAction moveAction;
    9.     public InputAction turnAction;
    10.  
    11.     public InputAction keyAction;
    12.  
    13.     bool isPending;
    14.  
    15.     XRHMD hmd;
    16.  
    17.     float x = 1;
    18.     float y = 2;
    19.     float z = 3;
    20.  
    21.     void Start()
    22.     {
    23.         moveAction.Enable();
    24.         moveAction.performed += (ctx) => Debug.Log($"pos {ctx.ReadValue <Vector3>()}");
    25.  
    26.         keyAction.Enable();
    27.         keyAction.performed += (x) => isPending = true;
    28.  
    29.         hmd = InputSystem.AddDevice<XRHMD>();
    30.     }
    31.  
    32.     private void Update()
    33.     {
    34.         if (isPending)
    35.         {
    36.             using (StateEvent.From(hmd, out var eventPtr))
    37.             {
    38.                 hmd.devicePosition.WriteValueIntoEvent(new Vector3(x, y, z++), eventPtr);
    39.                 InputSystem.QueueEvent(eventPtr);
    40.             }
    41.  
    42.             isPending = false;
    43.         }
    44.     }
    45. }
    When I run it, and press "p" three times, I get this output:

    upload_2021-12-3_20-29-3.png

    That's what I expect to get. But when I change the code at Lines 24 and 38, so that it simulates a rotation rather than a position, like this:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.InputSystem;
    3. using UnityEngine.InputSystem.XR;
    4. using UnityEngine.InputSystem.LowLevel;
    5.  
    6. public class SimGamepad : MonoBehaviour
    7. {
    8.     public InputAction moveAction;
    9.     public InputAction turnAction;
    10.  
    11.     public InputAction keyAction;
    12.  
    13.     bool isPending;
    14.  
    15.     XRHMD hmd;
    16.  
    17.     float x = 1;
    18.     float y = 2;
    19.     float z = 3;
    20.  
    21.     void Start()
    22.     {
    23.         moveAction.Enable();
    24.         moveAction.performed += (ctx) => Debug.Log($"rot {ctx.ReadValue <Quaternion>()}");
    25.  
    26.         keyAction.Enable();
    27.         keyAction.performed += (x) => isPending = true;
    28.  
    29.         hmd = InputSystem.AddDevice<XRHMD>();
    30.     }
    31.  
    32.     private void Update()
    33.     {
    34.         if (isPending)
    35.         {
    36.             using (StateEvent.From(hmd, out var eventPtr))
    37.             {
    38.                 hmd.deviceRotation.WriteValueIntoEvent(Quaternion.Euler(x, y, z++), eventPtr);
    39.                 InputSystem.QueueEvent(eventPtr);
    40.             }
    41.  
    42.             isPending = false;
    43.         }
    44.     }
    45. }
    I only get one line of output:
    upload_2021-12-3_20-32-44.png

    The performed event handler is only called the first time I queue an event. After that, it ignores any later events. (Of course, I have remapped the InputAction from HMD.devicePosition to HMD.deviceRotation in the Inspector.)

    It's baffling. It works fine when simulating a position, but only fires the event handler once when simulating a rotation.

    I'm stumped. Anyone see what I'm doing wrong?
     
  28. FeastSC2

    FeastSC2

    Joined:
    Sep 30, 2016
    Posts:
    978
    @runevision Is there a way to simulate actions with Unity's new input system?

    Something along those lines?

    Code (CSharp):
    1.         ActionMap.Actions.GetAction("Attack").SimulateContextStarted();
    2.         ActionMap.Actions.GetAction("Attack").SimulateContextPerformed();
    3.         ActionMap.Actions.GetAction("Attack").SimulateContextCanceled();
     
    Last edited: Apr 12, 2022
  29. FeastSC2

    FeastSC2

    Joined:
    Sep 30, 2016
    Posts:
    978
    @Rene-Damm
    @runevision

    What would be even better is if you could pass an InputAction.CallbackContext to an action.
    Code (CSharp):
    1.  
    2. var myContext = new InputAction.CallbackContext();
    3. myContext.started = true;
    4. ActionMap.Actions.GetAction("Attack").SimulateContext(myContext);
    5.  
    The problem right now is that InputAction.CallbackContext cannot be edited by the user. This would need to be allowed.

    As a sidenote: this would even allow the users to call functions that must be implemented when a class inherits from the IActions interfaces.

    Is this something you would consider adding in future versions of the Input System?
    Great work with the Input System by the way!
     
    Last edited: Apr 12, 2022
  30. nathanjams

    nathanjams

    Joined:
    Jul 27, 2016
    Posts:
    304
    So at the moment there is no way to simulate a button press through code? is this correct?
     
  31. NotaNaN

    NotaNaN

    Joined:
    Dec 14, 2018
    Posts:
    325
    Incorrect.
    You can simulate inputs via code but it can only be done at the Input Device Level (which is extremely unwieldy).
    See this post for more information on how to do that.


    I like this implementation concept. You get my +1!
     
  32. nathanjams

    nathanjams

    Joined:
    Jul 27, 2016
    Posts:
    304
    Thanks for the response, @NotaNaN

    I'm new to the new input system so I hope you don't mind a follow up quetsion. I have this code
    Code (CSharp):
    1.  public void CanPressButtonOnGamepad()
    2.     {
    3.         var device = InputSystem.devices[0];
    4.         Debug.Log(device);
    5.         InputEventPtr eventPtr;
    6.         using (StateEvent.From(device, out eventPtr))
    7.         {
    8.             ((ButtonControl)device["PlayerUI/InventoryToggle"]).WriteValueIntoEvent(1f, eventPtr);
    9.             InputSystem.QueueEvent(eventPtr);
    10.         }
    11.  
    12.     }
    but it triggers this error when I activate it:
    Code (CSharp):
    1. KeyNotFoundException: Cannot find control 'PlayerUI/InventoryToggle' as child of 'Keyboard:/Keyboard'
    2. UnityEngine.InputSystem.InputControl.get_Item (System.String path) (at Library/PackageCache/com.unity.inputsystem@1.3.0/InputSystem/Controls/InputControl.cs:419)
    3. VSTriggerInputButton.CanPressButtonOnGamepad () (at Assets/VSTriggerInputButton.cs:18)
    4. UnityEngine.Events.InvokableCall.Invoke () (at <3be1a7ff939c43f181c0a10b5a0189ac>:0)
    5. UnityEngine.Events.UnityEvent`1[T0].Invoke (T0 arg0) (at <3be1a7ff939c43f181c0a10b5a0189ac>:0)
    Where "PlayerUI/InventoryToggle" an Action in the input system. Can you tell me what I'm doing wrong here?

    Thanks in advance,
    Nathan
     
  33. NotaNaN

    NotaNaN

    Joined:
    Dec 14, 2018
    Posts:
    325
    Anytime. ;)

    Alright, I think I see your problem.

    You actually said the issue right here:
    Take a look at this line in your code:
    Code (CSharp):
    1. ((ButtonControl)device["PlayerUI/InventoryToggle"]).WriteValueIntoEvent(1f, eventPtr);
    In this line you just tried to write a value to the button "PlayerUI/InventoryToggle" on a Keyboard InputDevice. "PlayerUI/InventoryToggle" is not a key on a keyboard, it is the name of an InputAction in an InputActionMap.

    This is the reason we get the KeyNotFoundException. It cannot find the key you want to write the value to!

    Let's take a look at a snippet of @Rene-Damm's example:
    Code (CSharp):
    1. ((ButtonControl) device["dpad/up"]).WriteValueIntoEvent(1, eventPtr);
    In this snippet he is specifically writing a value to the "dpad/up" key on the Input Device.

    If you want your code to work you need to be writing to a valid keyboard key. Like this:
    Code (CSharp):
    1. ((ButtonControl)device["a"]).WriteValueIntoEvent(1f, eventPtr);
    Note the little "A" key slapped in there (which is a valid keyboard key).


    Of course, It is likely at this time that you find simulating inputs via the InputDevice extremely painful and wish you could simulate inputs at the InputAction level.

    Join the club. :D
     
  34. nathanjams

    nathanjams

    Joined:
    Jul 27, 2016
    Posts:
    304
    Wow!

    @NotaNaN

    Thank you so much for the detailed response that so clearly explains the whole everything about my question. Can confirm that this works.

    I'll just make this a single generic script that takes the button value as a string and then I'll be able set the button value in the editor and use this whenever I need this function.



    much thanks,
    Nathan
     
    NotaNaN likes this.
  35. NotaNaN

    NotaNaN

    Joined:
    Dec 14, 2018
    Posts:
    325
    No problem! I'm glad I could answer your question so thoroughly. ;)

    That's great because I totally didn't test any of the code.
    I mean, of course it works. :p
     
    nathanjams likes this.
  36. Stevens-R-Miller

    Stevens-R-Miller

    Joined:
    Oct 20, 2017
    Posts:
    676
    Until the next version of Unity ;)
     
  37. pistoleta

    pistoleta

    Joined:
    Sep 14, 2017
    Posts:
    539
    The only way to simulate a user's input is this one? I expected to find an asset that would help me with the task but for what I see here you have been doing it all by yourselves... by the way.. is it possible to simulate touch commands like swipe , pinch, rotate etc... ?
     
  38. NotaNaN

    NotaNaN

    Joined:
    Dec 14, 2018
    Posts:
    325
    Yep. This is the only way. (As far as I am aware)

    I don't know of many assets that actually extend unity's new input system — most of them just provide their own input-solution entirely.

    You can simulate inputs on touchscreens, yes. (See the docs for an example of how to do that, specifically).
    However, as far as the 'swipe', 'pinch', and 'rotate' commands?
    Unfortunately there's nothing out-of-the-box to do that.
     
  39. MikeGDev

    MikeGDev

    Joined:
    Dec 19, 2017
    Posts:
    53
    This approach of simulating input works when using a keyboard or a gamepad button. However, when trying to write into a stick on the gamepad the value doesnt get applied. Is this the correct approach when dealing with a composite input action?

    Code (CSharp):
    1. ((ButtonControl)device["leftStick/right"]).WriteValueIntoEvent(1.0f, eventPtr);
     
    Last edited: Oct 4, 2022
    ModLunar likes this.
  40. swingingtom

    swingingtom

    Joined:
    Feb 15, 2018
    Posts:
    10
    @MikeGDev the control type of "sticks" is not button

    Code (CSharp):
    1.  
    2. // retrieve the Gamepad
    3. Gamepad gamepad = InputSystem.GetDevice<Gamepad>();
    4. // or create one
    5. // Gamepad gamepad = InputSystem.AddDevice<Gamepad>();
    6. // A lot of more dedicated (less generic) controller are available : DualShockGamepad, XInputController, ...
    7. // You can always analyse their source code to find which kind of controls are behind a key
    8. // @see : https://docs.unity3d.com/Packages/com.unity.inputsystem@1.0/api/UnityEngine.InputSystem.Gamepad.html#UnityEngine_InputSystem_Gamepad_rightStick
    9.          
    10. InputEventPtr eventPtr;
    11. using (StateEvent.From(gamepad, out eventPtr))
    12. {
    13.     ((StickControl) gamepad["leftStick"] ).WriteValueIntoEvent( new Vector2(.5f,.5f), eventPtr);
    14.     InputSystem.QueueEvent(eventPtr);
    15. }
     
    ModLunar and MikeGDev like this.
  41. MikeGDev

    MikeGDev

    Joined:
    Dec 19, 2017
    Posts:
    53
    Makes sense, thanks for the help! :)
     
  42. achimmihca

    achimmihca

    Joined:
    Feb 13, 2016
    Posts:
    280
    The following does not work for me in Unity 2022.2.3

    Code (CSharp):
    1. private void Update()
    2.     {
    3.         if (Keyboard.current.leftCtrlKey.IsPressed(0.5f))
    4.         {
    5.             Debug.Log("leftCtrlKey pressed");
    6.             using (StateEvent.From(Mouse.current, out InputEventPtr eventPtr))
    7.             {
    8.                 Mouse.current.position.WriteValueIntoEvent(new Vector2(123, 234), eventPtr);
    9.                 InputSystem.QueueEvent(eventPtr);
    10.             }
    11.         }
    12.     }
    I see the debug log line, but the mouse pointer does not change.
    Also tried it with Mouse.current.delta, makes no difference.

    Is it broken or am I doing something wrong?
     
  43. rdjadu

    rdjadu

    Joined:
    May 9, 2022
    Posts:
    115
    If
    Mouse.current
    is the system mouse, your input will conflict with input coming from the actual backend controlling the mouse (have to use cursor warping in that case). Your events in
    Update
    will come before any events fed by the platform for the next frame and thus be overwritten by them.

    Add a new mouse with
    InputSystem.AddDevice<Mouse>()
    to avoid that. That one won't receive any input and you can queue whatever on it.
     
    achimmihca likes this.
  44. achimmihca

    achimmihca

    Joined:
    Feb 13, 2016
    Posts:
    280
    > Add a new mouse with InputSystem.AddDevice<Mouse>() to avoid that. That one won't receive any input and you can queue whatever on it.

    I tried this, but this does not affect the system mouse cursor. And I think it makes sense because it only sets the value of a virtual mouse.
    My goal is to move the system mouse cursor as well. Thus, I think I have to write the event to Mouse.current.

    > your input will conflict with input coming from the actual backend controlling the mouse

    This makes sense. Is there a way to write my custom event AFTER the actual backend controlling the mouse? I also tried LateUpdate, but it does not make a difference.
     
  45. achimmihca

    achimmihca

    Joined:
    Feb 13, 2016
    Posts:
    280
    Found the anwser: one can set the system mouse position using
    Mouse.current.WarpCursorPosition
    . No eventPtr needed in this case.

    The following works for me:
    Code (CSharp):
    1. private void Update()
    2.     {
    3.         if (Keyboard.current.leftCtrlKey.IsPressed(0.5f))
    4.         {
    5.             Vector2 customMouseDelta = new Vector2(-1, 0);
    6.             Mouse.current.WarpCursorPosition(Mouse.current.position.ReadValue() + customMouseDelta);
    7.         }
    8.     }
    Nonetheless, adding virtual input devices is still useful for me to trigger input actions from code without conflicts of actual input devices. Thanks for the hint!
     
  46. Xtro

    Xtro

    Joined:
    Apr 17, 2013
    Posts:
    608
    Just in case anyone needs this, this is how I simulated keyboard events for my tests in Test Runner:

    Code (CSharp):
    1.  
    2.         var Keyboard = InputSystem.GetDevice<Keyboard>();
    3.         var KeyboardState = new KeyboardState();
    4.         KeyboardState.Press(Key.Space);
    5.         InputSystem.QueueStateEvent(Keyboard, KeyboardState);
    6.         KeyboardState.Release(Key.Space);
    7.         InputSystem.QueueStateEvent(Keyboard, KeyboardState);
    8.  
    More info in this thread: https://forum.unity.com/threads/inp...t-simulate-inputactions.1016416/#post-9444620
     
  47. municola

    municola

    Joined:
    Jan 18, 2024
    Posts:
    1
    The code of Xtro
    awell from Rene
    both work to simulate input when the game is run in the unity editor. However, after making a build with il2cpp, it doesn't work anymore i.e.the unity input system doesn't pick up the simulated inputs.

    For context: we are testing a game and trying to programmatically simulate input for certain situations. The above code works in the unity editor (play mode) but when compiled through il2cpp and then tested in the final build, the simulated inputs don't get picked up anymore (but no other errors or output occurs).

    Any advice on why this is and how we could achieve our goal of simulating input would be highly appreciated!
     
  48. rdjadu

    rdjadu

    Joined:
    May 9, 2022
    Posts:
    115
    Usually comes down to stripping.

    If it's a device that comes with the input system package itself, there's likely some
    [Preserve]
    attributes missing and most likely some controls that have gone missing from the build. One way this can be worked around (and the theory tested) is by using a custom link.xml file to prevent stripping on the input system assembly entirely.

    If it's a custom device, it's likely missing the
    [Preserve]
    attribute on controls.
     
  49. Xtro

    Xtro

    Joined:
    Apr 17, 2013
    Posts:
    608
    I'm not testing my game on the build yet so I have no idea how it would be possible to test it in the build. Sorry :(
     
  50. SnehalVW

    SnehalVW

    Joined:
    Oct 31, 2019
    Posts:
    1
    Hello,
    I would like to mock a XR Controller to simulate controller input via code,
    Is this a correct way,


    private Gamepad rightController;

    void Start(){
    rightController = InputSystem.AddDevice<Gamepad>();
    }

    public void SimulateButtonPress<TDevice>(TDevice device, string buttonName, float value) where TDevice : InputDevice
    {
    InputEventPtr eventPtr;
    using (StateEvent.From(device, out eventPtr))
    {
    ((ButtonControl)device[buttonName]).WriteValueIntoEvent(value, eventPtr);
    InputSystem.QueueEvent(eventPtr);
    }
    }

    SimulateButtonPress(rightController, "gripPressed", 1);
    //For this input action mapping: INPUTASSET ActionTreeItem{"m_Name":"Grip Pressed (R)","m_Type":1,"m_ExpectedControlType":"Button","m_Id":"1d49c14b-53c9-4b24-9f7e-01b847117447","m_Processors":"","m_Interactions":"Press(pressPoint=0.85)","m_SingletonActionBindings":[],"m_Flags":0}BindingTreeItem{"m_Name":"","m_Id":"01e984db-5c6e-4ba1-bf63-64226108c62c","m_Path":"<XRController>{RightHand}/gripPressed","m_Interactions":"Press","m_Processors":"","m_Groups":"","m_Action":"Grip Pressed (R)","m_Flags":0}