New Input System in DOTS without generating C# class

Hello everyone. I am learning DOTS at the moment and I want to understand how to implement a New Input System in Unity DOTS. I have found some examples of it in the Internet, but all of them are using “Generate C# class” function. And that is the problem, because, as I know, that solution wont allow to create a rebinding system. At least in a convinient and beautiful way. So how to implement a New Input System in the Unity DOTS? The main problem for me there is that I need somehow pass the PlayerInput classic Component (not IComponentData) to different Systems (as I understand only to a SystemBase, because I wont be able to use PlayerInput in ISystem as it is a managed type of data), so I could subscribe my functions to started/perfomed/canceled events.
I tried to create an Entity with Singleton IComponentData class (contains only PlayerInput field), but that try was ruined by the fact, that GetSingleton cannot work with managed components (error says that all fields of the component and component itself must not be able to equal null)

Hi!

Your approach seems good, and there is a managed version of the system api, to access managed components. See end of this page : SystemAPI overview | Entities | 1.0.16

Yeah that helped, thanks
But frustrating moments are still there :frowning:

  1. For some reason if I close the Subscene (which is a usual case for a build for example), my reference of PlayerInput is nulled. It doesn`t matter if I get this reference by GetComponent or by setting the public value in editor. But if I open the Subscene everything is okay
  2. I tried to test this thing with open Subscene and there are still problems. As I understand PlayerInput activates the DefaultActionMap by default. But if I just start subscribing to started/perfomed/canceled events I will get no effect. When I checked the value of PlayerInput.currentActionMap it was null. I said okay and tried to PlayerInput.SwitchCurrentActionMap(PlayerInput.defaultActionMap). But now it showed an error “Cannot switch to actions ‘Game’; input is not enabled”. And even if I put the PlayerInput.ActivateInput() before it shows the same error, but now 2 times (so it also says, that I cant "switch to actions..." by ActivateInput() because input is not activated. At least it sounds genius, but I might not understand something). Moreover when I checked PlayerInput.enabled value, it was True. So I dont know what is the root of that problem
    Here are my code:
public class InputActionsAuthoring : MonoBehaviour
{
    public PlayerInput playerInput;

    public class Baker : Baker<InputActionsAuthoring>
    {
        public override void Bake(InputActionsAuthoring authoring)
        {
            Entity entity = GetEntity(TransformUsageFlags.None);
            AddComponentObject(entity, new InputActionsSingleton { playerInput = authoring.playerInput });
        }
    }
}

public class InputActionsSingleton : IComponentData
{
    public PlayerInput playerInput;

    public InputActionsSingleton()
    {

    }
}
public partial class ControlsSystem : SystemBase
{
    private InputActionsSingleton inputSingleton;
    protected override void OnCreate()
    {
        RequireForUpdate<InputActionsSingleton>();

    }

    protected override void OnStartRunning()
    {
        inputSingleton = SystemAPI.ManagedAPI.GetSingleton<InputActionsSingleton>();
        Debug.Log(inputSingleton.playerInput.enabled);
        Debug.Log(inputSingleton.playerInput.currentActionMap);
        inputSingleton.playerInput.ActivateInput();
        inputSingleton.playerInput.SwitchCurrentActionMap(inputSingleton.playerInput.defaultActionMap);


        inputSingleton.playerInput.actions["Select"].canceled += SelectSingleUnit;
        inputSingleton.playerInput.actions["Select"].started += StartSelectingUnitWithBox;
        inputSingleton.playerInput.actions["Select"].performed += EndSelectingUnitWithBox;
    }

    protected override void OnUpdate()
    {

    }

    private void SelectSingleUnit(InputAction.CallbackContext context)
    {
        Debug.Log("Selected single unit");
    }

    private void StartSelectingUnitWithBox(InputAction.CallbackContext context)
    {
        Debug.Log("Started selecting multiple units");
    }

    private void EndSelectingUnitWithBox(InputAction.CallbackContext context)
    {
        Debug.Log("Selected multiple units");
    }
}

And here is the Log:
9229056--1289037--upload_2023-8-17_13-10-57.png

Also pinned a unitypackage file of my Scene, if it would be easier to understand my problem this way. If you use it, not forget to import Entities, Entities Graphics, Unity Physics and Input System packages, as they are in my project (and in a subscene as well, if I understand it right). Also the project was made from 3D URP template, so it also may be important detail

9229056–1289049–PlayerInputProblem.unitypackage (132 KB)

Hi, and thanks for the pinned package.

So, the problem lies in the fact that all non ecs component in a subscene are ditched when closing. And because you can not reference something outside the subscene, you are stuck. I don’t think you can keep it easely in a subscene (without some weird hacks), but you can actually author it the same way you authored the terrain : in the Awake of a monobehaviour.

That being said, I realize now that this approach (Player Input class one) could make things a bit harder. Normally, you don’t want mono code accessing entity world. I guess that you want to manipulate entities components in the events instead of Debug.Log, and I’m not sure if this is optimal (EDIT : check the message just below this one, there is a way). I’m more fan of the “Generate C# class method”, things seems to be more clean that way, but I have not tested extensively the PlayerInput class way.

You are saying that rebinding is easier with PlayerInput class, so I’m curious of what make it easier. You have probably work more than I have in the new Input system (I never had to make a rebind system, for instance).
But while searching, I found this to rebind InputActions. Seems not that bad, but I might be wrong.
Class InputActionRebindingExtensions | Input System | 1.6.3 (unity3d.com)

I recommend you test both, see what is causing the most headache for you, between the PlayerInput class being not really ecs friendly, and the generated class not able to be rebound easily.

I have just read a post by xVergilx ( Discussion - Combining DOTS and Input System Package - Unity Forum ), that made me realize the event way should be fine if you don’t manipulate components in them.

Just update an unmanaged Input component in a OnUpdate with the values you gather from the events :roll_eyes: (I don’t know how I could not think about that). So your approach should be fine.

1 Like

Thanks for your help, again it fixed most of problems :slight_smile:
The only problems left is understanding
Firstly, as I understand, for operating with input I will need to do only two actions:

  1. Read some value from the input and make some changes in ComponentData-s or Entities.
  2. Do something in react for some input (for example player presses F and programm need to create a new unit, or give him a new command, or quit the game - any action in general).
    Or combined action of two previous. If I miss something more, please tell me - don`t want to face these kind of problems when me and my team will be working on our future project, so I want to learn and understand as much as I can for now.
    So if I am right then there are pretty convenient way to solve both of these tasks. For the second one I can just use Events, and for reading and changing data in ComponentData-s or Entities I can use Entities.ForEach and EntityCommundBuffer. At least for that moment it seems for me as a solution
    But you say that:

I suppose you mean Events which are from the MonoBehaviour world; so it is not good for them to access functions from the SystemBase. Or is it bad that these functions access some ComponentData-s and Entities?. If I understood you right, then please can you also explain, why is it bad? I have some guesses but nothing clear.
Next frustrating moment is your advice about how it may be done. I dont understand why should I create and change separate ComponentData for storing input data instead of direct changing it in needed components (for example moving controls input I could send directly to the CameraMovementComponent instead or smth like this). Also I don`t understand why is it better than my approach, which I explained in that post before.
I suppose, that I might just not fully understand your idea…

And about rebinding. I haven`t done it as well, but I have learned a little about this before. So the problem in “Generate C# class method” way is that we are generating some certain code operating with input. And it will be not so easy to change it when we are trying to rebind some InputAction. In opposite, when we are using “PlayerInput component” way, we can change InputActionAsset easily (just if we done this in the Editor), because we have a reference directly to that Asset. At least that is my way of understanding that situation. I can be wrong, but in that video
https://www.youtube.com/watch?v=m5WsmlEOFiA
(timecode 18:30) author says something similar to my idea.
And as I understood from that documentation about InputActionRebindingExtensions they will not work with the Generated C# class, because they need InputAction in their parameteres, which we cannot access without InputActionAsset reference. So thes Extensions might be for “PlayerInput component” way, if I am not mistaken

So here’s the thing.

Events are OOP generic name of “here’s a specific data and a potential change - do whatever with it”.
No restrictions => no control over => undefined order => undefined behaviour.
Plus, an anti-pattern when it comes to data oriented design due to random access nature.

This is where new Input System is a step back actually for the ECS.
Because what required is identical to basic polling of inputs, like with UnityEngine.Input.

You could make it as a structural change, but in reality - you don’t need to.
Flow looks like this:
0. Make a system, insert it at the beginning of the frame.

  1. Create an Entity that always persists with “InputData”. In it you can store any kind of value or flag application requires.
  2. Subscribe to events in OnCreate / dispose them in OnDestroy;
  3. In system, you want a temporary copy of InputData. Events modify that copy instead of writing directly. This doesn’t trigger any job completion. And doesn’t result in any main thread stalls as a result.
  4. In OnUpdate - you swap temporary copy of data, and write it to the entity. Next temp data is wiped to be prepared for the next frame respectively.

This allows the following:

  • You can add any number of systems that read from that singleton InputData.
    This is the biggest pro to creating separate entity with data on it. Near infinite extendability, no job stalls if you only read from it.
  • Bonus point - testing is easier since you’d need to only generate one component with specific data set.
  • MonoBehaviours do not affect behaviour of systems this way as well. Single direction interactions.
  • All input polling logic is constrained to the single system. Single responsibility. If you spread input polling logic around in multiple systems - it will be hard to refactor later on.
  • Which means system that require inputs are not coupled with other systems that may require same inputs. Decoupling.
  • No use of structural changes - no extra main thread stalls. If you need to add some extra events later on - you can read from InputData in a separate system and generate specific ones. Or, perform direct queries based on the inputs state without the need of events.

Rebinding does not change input related data. It changes the bindings for the Input System.
Data flow doesn’t change at all, so the rest of the logic still works in the same way.
For runtime, you’d want something like this:
https://docs.unity3d.com/Packages/com.unity.inputsystem@1.7/api/UnityEngine.InputSystem.InputActionRebindingExtensions.html

3 Likes

I understand that my answers can be frustrating, and that you are eager for more in depth explanations to understand fully the whys in unity dots, and not just a basic answer to a problem (which is all to your credit). I’ll try to remember that and go for more detailed explanation, when I know the actual detailed answer : sometimes my recommendations come from more advanced people post that I’ve read (from users like tertle, DreamingImLatios, xVergilx and many more that have been pioneering entities for years), and although I fully trust them in their expertise, and had plenty of occasion of discovering why sometimes, things are better one way or the other, I still have a long journey to fully understand the whys of many aspects of unity ecs.

I have just seen that xVergilx answered with a detailled explanation while writing this:smile:. It certainly is better than what I was preparing to provide to you. Glad you got an in depth explanation.

2 Likes

Big gratitude for your detailed answer, xVergilx! Now I got the idea and the reasons of that concept
But can you please clarify a few things:

  1. You says that I can subscribe to Events in OnCreate, but in reality I just get an error, that the singletone of PlayerInput entity was not created yet. As I understand, Systems are initialized before the Awake of MonoBehaviours or initialization of Entities from the scene (as it was the same result, when I was baking PlayerInput entity), right? If so, then I should set a RequireForUpdate and subscribe to Events in OnStartRunning, right?
  2. Is it okay to use SystemBase class as a place to store temporary InputData? I have read that it is not good to store any data in Systems, but I dont fully understand why, apart from clean code organization. Maybe instead I should use a new component added to Systems entity? Or component added to some separate entity?
  3. The idea of using events was to react on some inputs. So for example if player presses right mouse button the unit gets the new target for moving. But in current concept, as I got it, the most efficient and the easiest way to react on inputs is something like: Player presses right mouse button => Event writes a true value in the bool in the TemporaryInputData => OnUpdate copys that data to InputData => MovementSystem in OnUpdate always checkes the bool value in th InputData and if it is true, System calls the function of changing the target for moving. Am I right? It just looks a little bit complicated (and not so efficient as a result), so I am not sure that it is the best way to do such things. If so, please correct me.

If InputSystem is not initialized at point of “InputPollingSystem”, you might want to just manually initialize it.
Either via custom bootstrap, or by other means.

As for the data on the entity - create entity upon system creation, destroy OnDestroy respectively.
If system exists - singleton entity exists.

As of right now, there is no “clean” solution to the system related data.
There is “system state” entities, but those are still entities that reside inside the chunk. Its completely fine to store data inside system as long as other systems do not read / write from it directly (e.g. require a fetch via World.GetOrCreateManagedSystem()).

Reason for that is - you don’t want to access managed systems from the unmanaged systems OnUpdate, as that will prevent OnUpdate from burst compiling and couple systems together as well. Plus, managed references no longer can be stored inside ISystems, which makes process of accessing system related data significantly slower.

Since other systems does not access InputPollingSystem - it does not matter at all.
All required data is “abstracted” away and stored separately from the system.

Ideally you’d want something like:

public struct InputData : IComponentData {
     public bool RightActionHeld; // Or it could be a float + property check
     // Or an actual computed value required to be consumed by other systems
}

Add “abstract” data as inputs. Shouldn’t matter if its a button, or a trigger, or a touch.
Data copying is extremely fast, if you use two variables of the same type (inside system & attached to the entity).

Systems that require those inputs pick them up and use however they are needed based on current game state.
While it may look complicated, in reality you add action once and forget about it.

Consider another case, what happens if you want to add more logic to the same button?
Extra events aren’t free. Restrictions they apply aren’t free either.

As for the reading values in other systems - its extremely cheap when its an ISystem + burst compiled.
If events are used - unmanaged system OnUpdate burst compilation is blocked.
If structural changes are used - performance lost is more than just a simple check.

My general advice - don’t use tags / ECBs unless you REALLY need it and are certain that data change happens rarely. Data change frequency is the key here.

Writing directly even with random access is way cheaper because it happens off main thread.
Reading & checking in managed system is still faster for visual layer, alike UI changes.

10k Update optimization case no longer applies to the Entities.
OnUpdates are fast, they don’t have native overhead.
Can be faster for sure, but even for mobile its not that required to be honest with 1.x + ISystems.

1 Like

I’ve been using a singleton component to store input data.

SystemBase

using Unity.Entities;
using UnityEngine;
using UnityEngine.InputSystem;

public partial class GameInputSystem : SystemBase, InputActions.IPlayerActions
{
    private InputActions inputActions;
  
    private Vector2 move;
  
    private EntityQuery playerInputQuery;
  
    protected override void OnCreate()
    {
        inputActions = new InputActions();
        inputActions.Player.SetCallbacks(this);

        playerInputQuery = GetEntityQuery(typeof(PlayerInput));
    }

    protected override void OnStartRunning() => inputActions.Enable();
    protected override void OnStopRunning() => inputActions.Disable();
  
    protected override void OnUpdate()
    {
        if (playerInputQuery.CalculateEntityCount() == 0)
            EntityManager.CreateEntity(typeof(PlayerInput));
      
        playerInputQuery.SetSingleton(new PlayerInput
        {
            Move = move
        });
    }

    void InputActions.IPlayerActions.OnMove(InputAction.CallbackContext ctx) => move = ctx.ReadValue<Vector2>();
}

Component

using Unity.Entities;
using Unity.Mathematics;

public struct PlayerInput : IComponentData
{
    public float2 Move;
}

Accessing

public partial struct ExampleSystem : ISystem
{
    public void OnCreate(ref SystemState state)
    {
        state.RequireForUpdate<PlayerInput>();
    }

    public void OnUpdate(ref SystemState state)
    {
        var input = SystemAPI.GetSingleton<PlayerInput>();

        // do stuff
    }
}
2 Likes

Thanks xVergilx, I suppose I got most of your speech.
But I just can`t understand the part with the solution of OnCreate problem:

So I have a ComponentData with PlayerInput class reference in it:

public class PlayerInputAuthoring : MonoBehaviour
{
    private PlayerInput playerInput;
    private void Awake()
    {
        playerInput = GetComponent<PlayerInput>();
        EntityManager entityManager = World.DefaultGameObjectInjectionWorld.EntityManager;
        Entity entity = entityManager.CreateEntity();
        entityManager.AddComponent<InputActionsSingleton>(entity);
        entityManager.SetComponentData(entity, new InputActionsSingleton { playerInput = playerInput });
    }
}

public class InputActionsSingleton : IComponentData
{
    public PlayerInput playerInput;

    public InputActionsSingleton()
    {

    }
}

Then I have a ControlsSystem (“InputPollingSystem”), which subscribes to events and update InputData:

[UpdateInGroup(typeof(InitializationSystemGroup), OrderFirst = true)]
public partial class ControlsSystem : SystemBase
{
    private InputActionsSingleton inputSingleton;

    private InputData inputDataSingleton;


    private float3 cameraPos;

    private bool neededSelectAction;
    private quaternion cameraRotation;

    private bool neededTargetingAction;
    private float3 cursorCurrentDirection;

    protected override void OnCreate()
    {
        RequireForUpdate<InputActionsSingleton>();

        EntityManager.AddComponent<InputData>(SystemHandle);
        inputDataSingleton = EntityManager.GetComponentData<InputData>(SystemHandle);
    }

    protected override void OnStartRunning()
    {
        inputSingleton = SystemAPI.ManagedAPI.GetSingleton<InputActionsSingleton>();


        inputSingleton.playerInput.actions["SelectUnit"].started += StartSelectingUnit;
        inputSingleton.playerInput.actions["SelectUnit"].canceled += EndSelectingUnit;
        inputSingleton.playerInput.actions["TargetUnit"].started += TargetUnit;
    }

    protected override void OnUpdate()
    {
        inputDataSingleton.cameraPos = cameraPos;
        inputDataSingleton.neededSelectAction = neededSelectAction;
        inputDataSingleton.cameraRotation = cameraRotation;
        inputDataSingleton.neededTargetingAction = neededTargetingAction;
        inputDataSingleton.cursorCurrentDirection = cursorCurrentDirection;
    }

    private void StartSelectingUnit(InputAction.CallbackContext context)
    {
        Debug.Log("Selected single unit");
    }

    private void EndSelectingUnit(InputAction.CallbackContext context)
    {
        Debug.Log("Selected multiple units");
    }

    private void TargetUnit(InputAction.CallbackContext context)
    {
        var ray = Camera.main.ScreenPointToRay(Mouse.current.position.value);
        cursorCurrentDirection = ray.direction;
        cameraPos = Camera.main.transform.position;
        neededTargetingAction = true;
    }
}

public struct InputData : IComponentData
{
    public float3 cameraPos;

    public bool neededSelectAction;
    public quaternion cameraRotation;

    public bool neededTargetingAction;
    public float3 cursorCurrentDirection;
}

And here I cant call GetSingleton<InputActionsSingleton>() in OnCreate, because to that moment Entity which contains that component is not created yet. Moreover I cant get InputData component from other systems in OnCreate (for some reason in OnStartRunning it doesnt work as well). So how can I solve that problem? You said about custom bootstraping, but as I know that is quite complicated thing, where I will have to reinvent the whole PlayerLoop. Isnt it too much just for one system?

And additional question: from this thread and from others it seems to me that you know very well how Unity DOTS work from inside. Is it just an enormous amont of tests and experience? Or is there a way for me (as a new one there) to reference somewhere, when I need to understand some underhood details of Unity DOTS? I know that there is a ScriptAPI, but for many things there are no or almost no commentaries. So sometimes I just can`t find any info on some general principles of Unity DOTS work, or some methods, or some structs, or something else. So maybe there is something I am mising?

1 Like

You can create entity & component directly from the system’s OnCreate.
You don’t need the MonoBehaviour for this or managed IComponentData.

E.g.
https://discussions.unity.com/t/926207/11

Or here’s what I use with old Input System:

[UpdateInGroup(typeof(BeginFrameGroup))]
   public partial struct InputPollingSystem : ISystem {
      #region [Fields]

      private Entity _inputEntity;
      private ComponentLookup<InputData> _inputs;

      #endregion
 
      public void OnCreate(ref SystemState state) {
         _inputs = state.GetComponentLookup<InputData>();
         EntityManager em = state.EntityManager;

         if (!SystemAPI.TryGetSingletonEntity<InputData>(out _inputEntity)) {
            EntityArchetype arch = em.CreateArchetype(Queries.InputDataArch);

            _inputEntity = em.CreateEntity(arch);
#if UNITY_EDITOR
            em.SetName(_inputEntity, "Input Data");
#endif
         }
      }
 
      public void OnUpdate(ref SystemState state) {
         InputData data = default;

         if (InputManager.IsInitialized) {
            ReadGenericInputs(ref data);
            ReadWeaponInputs(ref data);
            ReadCCInputs(ref data);
            ReadInteractions(ref data);
            ReadMouseInputs(ref data);
         }

         state.CompleteDependency();
 
         _inputs.Update(ref state);
         _inputs[_inputEntity] = data;
      }
      ...
   }

Difference with newer InputSystem would be:

  • managed system instead of ISystem (because events);
  • extra field InputData inside the system to write data into from these events;

You’ve almost figured it out by yourself.
Just drop access from MonoBehaviour side and managed component.
The rest would be much easier to work with.

If you need the access from MonoBehaviour side to the inputs for some reason there are options such as:

  • Querying component object inside SystemBase (via Entities.ForEach).
    MonoBehaviour can be added via EntityManager.AddComponentObject to the entity.
    Which supports UnityEngine.Object.
    InputData can be then accessed as singleton inside of OnUpdate.
    – or –
  • Access system via world + call into some method => perform SystemAPI calls from the system.

Querying is preferrable, due to full control on where system / respective logic is executed.

That’s what would be required if InputSystem wasn’t initialized. But that’s not the case.
Input actions can be obtained just fine from OnCreate.

In any case, creating custom bootstrap is not that hard.
Not everything has to be re-defined, just takes a bit of time.

Yes. Trial & error mostly.
I’ve been working on multiple hybrid projects since 0.17, so its just how life goes.

Unfortunately (if I am not mistaken) I cannot do that. Your example and the example of Ryiah will not work in my case as I can`t create PlayerInput instance from the file, because I need a reference for the Input Action Asset. In your examples it is used a generated C# class which can be easily created in SystemBase in opposite to my situation, where I want to use PlayerInput Unity Component (which lies in the GameObject) just for future rebinding system (as I understand it is not so easy to do it in the “Generate C# class” way). So I need a GameObject to store that PlayerInput Unity Component. Just to be sure here is a screenshot of the component I am talking about:
9233364--1289967--upload_2023-8-18_23-48-49.png

And also there is one more problem, which is not fully connected with the above one. I add to the ControlsSystem a component of InputData in the OnCreate, but I still can`t have access to it in another system. Even in OnStartRunning with RequireForUpdate. Maybe I am just wrong in accessing InputData singleton? (Here is my code in OnCreate variant)

public void OnCreate (ref SystemState state)
{
    state.RequireForUpdate<MovementComponent>();
    inputData = SystemAPI.GetSingleton<InputData>();
    collisionWorld = SystemAPI.GetSingleton<PhysicsWorldSingleton>().CollisionWorld;
}

Wow, that is amazing! Big respect to you for sharing your enormous experience with such newbies as me :slight_smile:

Yeah, higher level wrapper is really overengineered one. Some of the review commentaries inside it is pretty hilarious.
Its one of those Unity things that usually don’t age well.

Still, if rebinding is all you want - its pretty straight forward to implement.
All it takes an Input Action and a key for the binding.
Input Actions are in JSON format, so saving / loading them in runtime isn’t that hard.

But, if you really want to keep PlayerInput - add component directly to the entity.
That way you’ll get rid of managed component.
As for the how to author them without loading directly from resources, that’s a bit more tricky.
To bypass PlayerInput, you could technically store it as a blob asset during baking process, since its just a string.
Then convert default JSON into Input Asset in runtime (or load from file from persistent storage).
From there its the same as example above.

Edit:
Generated C# InputActions mapping already exist as JSON directly inside the code.
So you don’t really need anything extra, just create an object with specified class (and dispose it in OnDestroy). Rebinding works the same as you would do rebinding with any InputAction in runtime. I’ve completely forgot they’ve used this clever hack.

This might come in handy:
https://docs.unity3d.com/Packages/com.unity.inputsystem@1.7/api/UnityEngine.InputSystem.InputActionAsset.html

1 Like

InputData should be read in OnUpdate.
Its a struct, if you copy it in OnCreate - you’ll get a momentary copy of it.
There’s no point in getting it in OnCreate.

As for the why - it may not exist (especially with delayed MonoBehaviour initialization).
System creation happens before any scene loads.

On topic of polling new input system Input Actions:
https://docs.unity3d.com/Packages/com.unity.inputsystem@1.7/manual/Actions.html#polling-actions

Instead of using events, you can directly read values from the InputAction.
And well, generated C# InputActions should just work for rebinding. Just like existing asset.

1 Like

I’ve never heard of it being difficult but I also don’t visit the official forum section as the threads there are often not answered or out of date. I don’t have a test project set up at the moment to verify but you should be able to just do something like this:

var rebind = inputActions.Player.Jump.PerformInteractiveRebinding()
    .WithControlsExcluding("Mouse")
    .Start();

Loading:

var rebinds = inputActions.SaveBindingOverridesAsJson();
PlayerPrefs.SetString("rebinds", rebinds);

Saving:

var rebinds = PlayerPrefs.GetString("rebinds");
inputActions.LoadBindingOverridesFromJson(rebinds);

https://docs.unity3d.com/Packages/com.unity.inputsystem@1.7/api/UnityEngine.InputSystem.InputActionRebindingExtensions.html

2 Likes

So much thanks to both of you! You really helped me to understand InputSystem as much as I can for that moment!
But there is just two more things which I want to clarify.

  1. About accessing Singletons and Components from a System.
    a) So firstly, I understand now that GetSingleton or GetComponent return just a copy of that data. But for Components (for some reason for Singletons there is no such thing) there is also an option of RefRO<>, which seems pretty similar in use, but in fact there is a reference with readonly access instead of a copy. Is there some difference in usage of these things? Maybe I should use them in different occasions, or there are different restrictions and abilities for them, or something else?
    b) I tried to get the RefRW in OnCreate, so there will be no need in getting the RefRW of it on each Update. But it has given to me the error:

    I just do not understand what structural changes they are talking about? I am not doing anything with System`s entity after getting that reference… Also if it is a real problem, not my mistake in some minor thing, then I should get the RefRW in OnUpdate, am I right?
    Here is the code of that thing for better understanding what am I talking about:
private ControlsAsset controlsAssetClass;

private RefRW<InputData> inputDataSingleton;

private float3 cameraPos;
private bool neededTargetingAction;
private float3 cursorCurrentDirection;

protected override void OnCreate()
{
    controlsAssetClass = new ControlsAsset();

    controlsAssetClass.Game.SelectUnit.started += StartSelectingUnit;
    controlsAssetClass.Game.SelectUnit.canceled += EndSelectingUnit;
    controlsAssetClass.Game.TargetUnit.started += TargetUnit;

    EntityManager.AddComponent<InputData>(SystemHandle);
    inputDataSingleton = SystemAPI.GetComponentRW<InputData>(SystemHandle);
}


protected override void OnUpdate()
{
    inputDataSingleton.ValueRW.cameraPos = cameraPos;
    inputDataSingleton.ValueRW.neededSelectAction = neededSelectAction;
    inputDataSingleton.ValueRW.cameraRotation = cameraRotation;
    inputDataSingleton.ValueRW.neededTargetingAction = neededTargetingAction;
    inputDataSingleton.ValueRW.cursorCurrentDirection = cursorCurrentDirection;

    neededSelectAction = false;
    neededTargetingAction = false;
}

c) How fast is GetSingleton? I just worry a little as I need to GetSingleton of not only the InputData Component. For example even in my easy and small code I already need to GetSingleton for RayCasting. And if I got it right I need to GetSingleton of it in each OnUpdate, if I need collisions in each OnUpdate. Won`t it be too heavy to 2-4x (I suppose in the future there can be a necessity for more than 2x) GetSinglton for each frame operation?

  1. And here is a short question. In SystemAPI, EntityManager and SystemState there are some very similar methods. I know that SystemState differs by automatic creation of Dependencies for the Systems. But what is the difference between EntityManager and SystemAPI if I can use all of them in the same piece of code?

RW references cannot be safely cached. Its a temporary pointer to the data that resides in the chunk.
So if anything else changes data layout - it cannot safely assume pointer is still pointing to the same memory location.

Basically, if you want to get singleton - you have to get a fresh one in OnUpdate each time.
If in short:
RW singleton - is a direct reference to the data inside the chunk without ANY safety.
Meaning you also have to combine job handles if you write to it in multiple jobs (off main thread).

RO singleton - is a direct reference to the data inside the chunk, still no safety, but you can’t write back results.
Read the summary for the method. Its the best explanation.

RW singleton is a hack for other main thread systems to read e.g. native collections directly from IComponentData to pass around NativeCollections into jobs. This is not an issue because:

  1. Usually component never changes inside the job;
  2. NativeCollections have their own safety to ensure they’re read / written correctly.

This hack may change in the future. And probably will include automatic dependency handling / safety when actual proper collection access will be provided. But that may not happen any time soon™.

BurstCompiled OnUpdate won’t evel feel it. Managed systems has more overhead due to them being managed.
Pretty much never a problem with 1.x.

You can always grab (or store) Entity + ComponentLookup instead of directly reading component data on the main thread.
This way you can read / write data directly from the job. It depends on the case. But its a balancing act, whether its worth to add up previous job to the component chain or not. Usually its not, especially after changes in 1.x.

If you really bothered, you can always add Profiler.Begin[End]Sample and check the profiler for specific code paths.

SystemAPI is a codegen entry point that allows to source generators to generate respective code.
So basically, if you use e.g. SystemAPI.GetComponent it will generate code based on the context where it is called.
Note that SystemAPI cannot be called outside of systems as of right now.

SystemState is a bunch of unmanaged pointers to the ISystem state.
This allows it be passed around & Burst compiled.

EntityManager is an EntityManager.
Its a pointer-wrapper struct for the unmanaged data storage that communicates changes required to be done.
Basically its a native collection, but much, much, much more complex. It can be passed around by like by “ref”.
And can be used respectively outside of systems.

If you want specifics, some IDE’s allow you to inspect generated code just by clicking header of the system.
Like Jetbrains Rider does. As for VS, I’m not sure, might be a bit more tricky. But in the end all SystemAPI calls get codegened into something. Like ComponentDataTypeHandle access.

Note that you can avoid SystemAPI completely and use direct approach methods.
Depends whether you want or not to write more code. Or when something specific is required.

Thank you so much. Your help was enormous!

1 Like