With all the great options for a user to change and add multiple bindings I cannot find any guidance for how and where the actions and bindings are stored. Also I am a bit surprised there is not even a basic UI for this built in. Is there a sample somewhere I have missed?
There's no API yet to assist directly in persisting bindings. It's something we'd like to take a look at after 1.0. An overall sample for a rebinding screen has landed and should be in the next package. For persisting binding customizations, you can traverse of the set of bindings in each action map using InputActionMap.bindings and for each one that has an overridePath set, save it along with the ID of the binding. When loading, you can look up the bindings by ID and put the override back in place. Code (CSharp): // Saving. var overrides = new Dictionary<Guid, string>(); foreach (var map in asset.actionMaps) foreach (var binding in map.bindings) { if (!string.IsNullOrEmpty(binding.overridePath)) overrides[binding.id] = binding.overridePath; } // Loading. foreach (var map in asset.actionMaps) { var bindings = map.bindings; for (var i = 0; i < bindings.Count; ++i) { if (overrides.TryGetValue(bindings[i].id, out var overridePath) map.ApplyBindingOverride(i, new InputBinding { overridePath = overridePath }); } } There'll be a more elegant API for this later on. What I think would be great to have would be a simple API that just returns a string in JSON format that you can store anywhere and that you can then hand back to the API and it restores all overrides for you.
I took the suggestion and made it work with json and the PlayerPrefs system. Thanks for the start that was indeed a big big help! I am calling LoadControlOverrides on Start. I am also using the rebinding UI elements with a little rework. For them I added a Start method to call UpdateBindingDisplay so they show the correct override. Code (CSharp): public InputActionAsset control; /// <summary> /// Private wrapper class for json serialization of the overrides /// </summary> [System.Serializable] class BindingWrapperClass { public List<BindingSerializable> bindingList = new List<BindingSerializable> (); } /// <summary> /// internal struct to store an id overridepath pair for a list /// </summary> [System.Serializable] private struct BindingSerializable { public string id; public string path; public BindingSerializable(string bindingId, string bindingPath ) { id = bindingId; path = bindingPath; } } /// <summary> /// stores the active control overrides to player prefs /// </summary> public void StoreControlOverrides () { //saving BindingWrapperClass bindingList = new BindingWrapperClass (); foreach ( var map in control.actionMaps ) { foreach ( var binding in map.bindings ) { if ( !string.IsNullOrEmpty ( binding.overridePath ) ) { bindingList.bindingList.Add ( new BindingSerializable ( binding.id.ToString (), binding.overridePath ) ); } } } PlayerPrefs.SetString( "ControlOverrides", JsonUtility.ToJson ( bindingList ) ); PlayerPrefs.Save (); } /// <summary> /// Loads control overrides from playerprefs /// </summary> public void LoadControlOverrides () { if (PlayerPrefs.HasKey( "ControlOverrides") ) { BindingWrapperClass bindingList = JsonUtility.FromJson ( PlayerPrefs.GetString ( "ControlOverrides" ), typeof ( BindingWrapperClass ) ) as BindingWrapperClass; //create a dictionary to easier check for existing overrides Dictionary<System.Guid, string> overrides = new Dictionary<System.Guid, string> (); foreach ( var item in bindingList.bindingList) { overrides.Add ( new System.Guid ( item.id ), item.path ); } //walk through action maps check dictionary for overrides foreach ( var map in control.actionMaps ) { var bindings = map.bindings; for ( var i = 0; i < bindings.Count; ++i ) { if ( overrides.TryGetValue ( bindings[i].id, out string overridePath ) ) { //if there is an override apply it map.ApplyBindingOverride ( i, new InputBinding { overridePath = overridePath } ); } } } } }
Thanks a lot @Rene-Damm and @Wiechering, I tried the code of @Wiechering and it worked without any fixes. I can use this with a few modifications, so it fits into my system. Also a method to reset: Code (CSharp): public void ResetKeybindings(InputActionAsset control) { foreach (var map in control.actionMaps) { map.RemoveAllBindingOverrides(); } PlayerPrefs.DeleteKey("ControlOverrides"); }
@Wiechering @Schneddi Hey, did the new key bindings work in trigger their action? I am getting the correct override path in logs but no action is triggered with it, still the old keys...
@abdo400 same behavior here, I can log that the override is stored but it never triggers the action. Has anyone figured out a way around this?
@Rene-Damm I'm getting the same issue as the previous two users posted. I'm using the code from the Rebinding UI sample and the overrides are definitely stored as JSON in PlayerPrefs and they even get loaded correctly into the UI after a restart. The issue comes after I switch scenes and begin playing my game. I attempt to load the overrides using a reference to the same InputActionAsset, but the callbacks are still triggered by the original keybinds. I made sure to register to the callbacks (Start) after loading the overrides (Awake). I tried to look for the documentation for ApplyBindingOverride but wasn't able to find anything super helpful here. I'm using 1.0.0 version of Input System and Unity 2019.3.6f1. I attached the code I'm using to save and load. Please let me know if I can include any other info.
That would be great. Right now I LOVE the inputsystem and the example, but it lacks a possibility to store the keybinds.
1.1 is in preview and has functionality to save / load all the input binding overrides as a json string, looking at the changelog.
Anyone googling for this the GitHub for the InputSystem is now at 1.1 preview 3, so you can already use the saving to disk feature if you are OK with using preview packages -> https://github.com/Unity-Technologies/InputSystem
Now: Code (CSharp): void SaveUserRebinds(PlayerInput player) { var rebinds = player.actions.SaveBindingOverridesAsJson(); PlayerPrefs.SetString("rebinds", rebinds); } void LoadUserRebinds(PlayerInput player) { var rebinds = PlayerPrefs.GetString("rebinds"); player.actions.LoadBindingOverridesFromJson(rebinds); }
Has anyone run into issues with Unity saving the JSON but not loading it back into the actions with 1.1 preview 3? I have confirmed that my PlayerPrefs are saving the JSON as a string but when I load the overrides nothing seems to happen. This is my code for loading: Code (CSharp): playerActionsAsset.LoadBindingOverridesFromJson(Options.PlayerRebinds); _playerInput.actions.LoadBindingOverridesFromJson(Options.UIRebinds); I'm trying two different methods of loading the overrides JSON into the InputActions but neither approach is working. In the first line, playerActionsAsset is a publicly defined InputActionAsset that I assign in the inspector. I'm also attempting to get a reference via PlayerInput.actions, but that's not working either. I don't see any errors or warnings in the console, but when I open the input debugger for PlayerInput I still see the default button mappings, not the saved overrides. This is the JSON that is being generated when I save the overrides (I have two separate strings for the two InputActions I'm using): Code (CSharp): {"bindings":[{"action":"DefaultActions/Trick3","id":"67d6dcd5-cafc-4781-b58f-91c13efc4197","path":"<Gamepad>/leftShoulder","interactions":"","processors":""},{"action":"DefaultActions/Trick3","id":"9093fff7-3dfe-4ac4-b49a-f31f1700eb17","path":"<Gamepad>/leftShoulder","interactions":"","processors":""}]} {"bindings":[{"action":"UI/Rename","id":"96a3b044-76ee-4d2b-9358-dbec8350d728","path":"<Gamepad>/rightShoulder","interactions":"","processors":""},{"action":"UI/Rename","id":"2e8d8c27-e64f-402c-a747-a2ffe4db43a9","path":"<Gamepad>/rightShoulder","interactions":"","processors":""}]} Does anyone have any idea why that wouldn't be loading? Am I missing a step or am I just doing something wrong? Any help would be super appreciated. Thanks! EDIT: I forgot to mention, but I'm running this in 2019.4.23f1.
I was able to figure out what my problem was, and for the sake of anyone who finds this later I'll document the problem and how I got around it. As I mentioned in my previous post, I had been using a mix of the Unity class PlayerInput and manually instantiating my own InputActionAsset classes. This was the fundamental cause behind LoadBindingOverridesFromJson not working, as creating a new instance of my Input Actions was overwriting my loading with the defaults. I need to make use of the events from different phases of InputAction (.started, .performed, and .canceled) and as near as I can tell, there's no way to get that data directly from the messages and events that PlayerInput broadcasts. So instead of creating a new instance and adding events like this: Code (CSharp): _playerInputs = new PlayerInputActions(); _playerInputs.DefaultActions.Movement.performed += ctx => moveVector = ctx.ReadValue<Vector2>(); _playerInputs.DefaultActions.Jump.started += ctx => ButtonStarted(InputType.Jump); I subscribe to the event directly from PlayerInput like this: Code (CSharp): _playerInput.actions["Movement"].performed -= ctx => moveVector = ctx.ReadValue<Vector2>(); _playerInput.actions["Jump"].started -= ctx => ButtonStarted(InputType.Jump); I don't love this approach because it relies on matching the string to the Action name and the compiler won't catch this, but it gets the job done. I can listen for specific phase events and saving / loading my remapped inputs works now.
What version are you using because LoadBindingOverridesFromJson isn't available for me in playerinput?
According to my package lock, "com.unity.inputsystem" is using version "1.1.0-preview.3". Hope that helps!
This is most likely my problem as well... been searching for a fix what seems 20 hours now... I'm using version 1.1.1 of the input system, got a save/load working perfectly, but the changes are only "applied" when I restart the game... In my case however, I use the following code : Code (CSharp): using UnityEngine.InputSystem; PlayerInput playerInput; private void Awake() { playerInput = GetComponent<PlayerInput>(); } //then later on in Update and another function I use the following lines to read values... Vector2 plMove = playerInput.actions["Move"].ReadValue<Vector2>(); //... Ray camRay = nightcam.ScreenPointToRay(playerInput.actions["MouseLoc"].ReadValue<Vector2>()); //... if (playerInput.actions["Sprint"].ReadValue<float>() == 1) I have absolutely no idea how I would go to change all of my codes .ReadValue to actually work with the rebindsOverrides... cause right now having a rebinding menu that you need to restart the game isnt quite optimal... Any suggestion would be GREATLY appreciated EDIT : its been more than a week without a single reply... guess i'll just do like everyone else and buy "Rewired"... At least I know that will work properly straight away and i'll get support if needed....
Needs to be applied to the InputActionAsset instance used by a specific player. For single player, can be done at any point and just applied to the given source asset as is. For multiple players, needs to be applied after the player's OnEnable. When using PlayerInputManager, listening for player joins and doing it there is one way. But many others. E.g. putting this right next to the PlayerInput component on the same GO. Code (CSharp): public class SaveAndRestoreBindingOverrides : MonoBehaviour { private void OnEnable() { var player = GetComponent<PlayerInput>(); // Fetch overrides. Not very robust to do it by playerIndex but // serves as an illustration. var rebinds = PlayerPrefs.GetString("Rebinds" + player.playerIndex); // Apply. player.actions.LoadBindingOverridesFromJson(rebinds); } private void OnDisable() { // Save rebinds. var player = GetComponent<PlayerInput>(); var rebinds = player.actions.SaveBindingOverridesAsJson(); PlayerPrefs.SetString("Rebinds" + player.playerIndex, rebinds); } }
for me the SaveBindingOverridesAsJson() and LoadBindingOverridesFromJson() just dont exist for me on my playerinput component, i have a "ToJson()" and "LoadFromJson()" method though
NVM! I understand now. Overrides are runtime values, I was expecting what I had set in editor (just path) to show up.