Took me a while to figure out the composite rebinding issue. The issue lies with what is being passed into the "recursive" DoRebind. The original binding index was accidentally passed instead of the firstPartIndex and the check should be for isPartOfComposite and not the actual composite Currently on Github: if (firstPartIndex < action.bindings.Count && action.bindings[firstPartIndex].isComposite) DoRebind(action, bindingIndex, statusText, true, excludeMouse); works for me: if (firstPartIndex < action.bindings.Count && action.bindings[firstPartIndex].isPartOfComposite) DoRebind(action, firstPartIndex , statusText, true, excludeMouse);
Thank you for this. I spent all day failing to wrap my mind around key rebinding with the new Input System, until I came across this video. It's both comprehensive and easy to follow. You're a great communicator.
Holy Molly this works. I’ve been stuck with rebinding for half a month. Thank you so sooo much! You can’t imagine how overwhelming and difficult such ‘simple’ things are for a newcomer like me. Finally I can continue working on my game.
That's because the new Input System is catastrophically bad for newcomers to Unity. It is really, REALLY bad. I don't know what they were thinking, really.
Such an amazing tutorial, I found some others but they didn't address the issue with using the scriptable object versus the c# generated script. Such a life saver!
I had to watch this few times in order to get my head around things but overall well done on a topic that hasn't been documented very well. Awesome job.
Thank you for this tutorial, very helpful. If anyone runs into the problems I had here are some changes I had to make: - My InputManager is set up as a singleton, so I had to change all the Null checks in LoadBindingOverride(), GetBindingName() etc. accordingly: if (Instance.inputActions == null) Instance.inputActions = new InputActions(); - I was also getting Null reference exceptions due to actionName not being references, I assume this is happening because of script execution order. I "fixed" it by adding: if (actionName == null) return; to LoadBindingOverride() - Since the Instance of my InputManager is handled in Awake, I had to move all the OnEnable() stuff from the ReBindUI script to Start, so the instance already exists in the scene before anything gets called from there. Except for the two lines of UpdateUI event subscription. - I also had to change OnValidate because the Instance doesn't exist when this is first called. But only when running the game, otherwise the changes made in the Inspector (like changing the keybind) wouldn't be reflected: if (Application.isPlaying) if (InputManager.Instance == null) return;
Hello, currently I encounter the same problem within build. Injecting code works but not for the first part of your tip (Instance.inputActions == null...). Unity said to me that "instance.inputActions" is an inaccessible reference in the context. Remove the "static" parameter to the reference seems to clear the problem but it's occur errors on other sides of functions. How did you override this problem ? Thanks,
Thanks so much for this video, it has been really useful! I thought the route of using generated code would end up being painful, but you've explained it all.
This is an immense tutorial! Thank you. It has been so helpful, I never would have managed this on my own. As others have pointed out there were a few errors but solutions have also been provided, so that's great. I played about with getting the composite individual key names to appear during binding, but I think I'll go the route of having each key, W A S D on their own, so that it is much clear which key you are currently rebinding. Also I'll add in some extra functionality to alert the player if they are overwriting a key which has already been bound. When playing with the composites I bound Up, Down, Left, and Right all to the same key :D So I'll have to find a way to prevent that.
Just finished implementing all this stuff, works great, thanks (tested on build, worked either). It's pain in the ass that we can't add multiple Cancel Rebind Buttons, but right now (I dunno if you could do it in August) it's better to add InputActionReference to WithCancelingThrough function rather than hardcode a string. So in RebindManager we can add serialized reference to some InputActionReference and then use it like this: >rebind.WithCancelingThrough(cancelRebindButton.action.controls[bindingIndex]); I personally made a separate key for canceling with 2 bindings - first one for keyboard and second one for gamepad (how you choose what to use is up to you).
Returning here to say that rebinding saves are actually acting kinda strange in the actual build. So, when you are changing bindings, they are saved and stuff. But when you close the game and run it again they are not working (even though my rebinding UI panel is showing me the new inputs). The coolest part is that when I'm making an absolute the same build new bindings are working again until I change some of them. In that case everything is reset to default once I restart the game.
I have two questions: 1) Did you try out the composite rebinding? I'm having trouble with it. It seems the sub-bindings for lack of a better term return FALSE for isComposite- only the first one (the WASD one) returns true for that. The subsequent ones return true for isPartOfComposite, but if I just sub those out as -seems- appropriate, I get an InvalidOperationException: Cannot perform rebinding on composite binding 'Move:Dpad' 2) Is there a particular reason you didn't just make the InputManager just use a singleton for the InputActions reference? Seems like a decent use case for a singleton, and would resolve the issue of forgetting to put the InputManager script on an object in the scene.
Ok, figured out the first problem. Using the line numbers in your linked project on github: Line 34 is: if (firstPartIndex < action.bindings.Count && action.bindings[firstPartIndex].isComposite) But needs to be: if (firstPartIndex < action.bindings.Count && action.bindings[firstPartIndex].isPartOfComposite) (change .isComposite to .isPartOfComposite) Line 35 is: DoRebind(action, bindingIndex, statusText, true, excludeMouse); But needs to be DoRebind(action, firstPartIndex, statusText, true, excludeMouse); (Need to pass firstPartIndex, not bindingIndex. If you pass bindingIndex, you get that invalid operation exception) Line 85 is: if (nextBindingIndex < actionToRebind.bindings.Count && actionToRebind.bindings[nextBindingIndex].isComposite) But needs to be if (nextBindingIndex < actionToRebind.bindings.Count && actionToRebind.bindings[nextBindingIndex].isPartOfComposite) (Same issue as the first correction) There is still some wonkiness with this approach, since as-presented, the order that the user needs to press their new bindings in isn't terribly clear. Despite the display reading W/A/S/D, the order the buttons actually need to be pressed in (in my case, at least) is, Up, Down, Left, Right. I'm going to work on adding some extra feedback to the status text to help make this more clear to the user.
@@OneWheelStudio On the subject of point #1, implementing the InputManager as a singleton (instead of extending monobehaviour) worked quite well for me. This way, I don't need to worry about having the InputManager attached to some object in the scene. I also included logic to apply custom keybind overrides in this section, such that they'll get applied as soon as ANY part of the project attempts to access the InputManagaer instance. I did it like this (RiverGameInputActions is the name of my c# generated InputActions class): private static RiverGameInputActions _inputActions; public static RiverGameInputActions inputActions { get { if(_inputActions == null) { SetupInputActionsInstance(); } return _inputActions; } } private static void SetupInputActionsInstance() { _inputActions = new RiverGameInputActions(); var maps = _inputActions.asset.actionMaps; for(int i = 0; i < maps.Count; i++) { //Debug.Log("MAP: " + maps[i].name); var actions = maps[i].actions; for(int j = 0; j < actions.Count; j++) { //Debug.Log("---ACTION: " + actions[j].name); //Load the custom bindings LoadBindingOverride(actions[j].name); var bindings = actions[j].bindings; for(int k = 0; k < bindings.Count; k++) { //Debug.Log($"___---Binding path: {bindings[k].effectivePath}"); } } } }
@@Frenotx Following your advice here, I was able to also fix the composite input issue. I am trying to show also extra feedback to the status text. So far I have this: // If it's a part binding, show the name of the part in the UI. if (actionToRebind.bindings[bindingIndex].isPartOfComposite) { statusText.text = $"Binding '{actionToRebind.bindings[bindingIndex].name}'. "; } else statusText.text = "Press a " + actionToRebind.expectedControlType; But it only works on the first biding and not the rest. Have you manage to do this? Thank you! :D
The reset code has a bug where you can't reset composite bindings. To fix, remove "&& action.bindings[i].isComposite" in the for loop. You don't need to check if each binding isComposite. You only need to know if the main binding is. Fixed code for 48:47: if (action.bindings[bindingIndex].isComposite) { for (int i = bindingIndex; i < action.bindings.Count; i++) { action.RemoveBindingOverride(i); } }
Original video and your answer both have errors, actually you have to check for isPartOfComposiste in a loop, according to Unity documentation. Checking for action.bindings.Count means you will rebind from first composite till the end of all bindings.
Hey, thanks a lot for the video, huge time saver. Otherwise i'd have to completely dig into unity code samples One small note: it should be if (nextBindingIndex < action.bindings.Count && action.bindings[nextBindingIndex].isPartOfComposite) isntead of if (nextBindingIndex < action.bindings.Count && action.bindings[nextBindingIndex].isComposite) Otherwise rebinding of composite actions wouldn't work properly
Regards your race condition: OnValidate runs before Awake and OnEnable runs after Awake Therefore your trying to access the InputActions in Validate to rebind the UI. Which beats the Awake of the InputManager. Switching InputManager to OnValidate does work, in my case, but still runs a race against who runs first.
Can I ask you one thing about saving string to the next scene when I load second scene my Inputs only saves when i have canvas in with same keybinds but if canvas is disabled or there is no keybindings on canvas, game just using default keys not saved ones from previous scene... its really strange is not global saved them keybindings any idea how to approach this issue?
Hmm. I haven't played with that. If I had to guess, it would be that the key binding is saved with the instance of the Input Action Asset. So, maybe, one work around would be to make sure the object that has a reference to the input action asset is not destroyed between scenes?
Hi! This tutorial really is so helpful and works like a charm with the C# generated class. While using it I'm just encountering an issue, if you might be able to help or anyone, please contact :( the problem comes if you try to rebind the UI asset (let's say Submit or Cancel actions), since the Event System, or in this case, the InputSystemUIModule is using the scriptable object as the actions asset, when you rebind the control, it doesnt update since you are updating the C# class instead of the scriptable object itself, therefore you are not able to rebind UI actions. My workaround was simply when doing the rebind, update the UIModule to use the instance created during the rebind, it kind of work, the rebinded control is properly set, but you lose the references of the other actions (such as Point, Navigate, etc)
Hi, can You instead of making a Input Rebinding in script which must be scripted to be saved and loaded, make some universal script for rebind using global persist way?
Very helpful, unfortunately I have been really struggling trying to fix the composite input side of things. Using the great fixes provided in the comments I have been able to change the composite inputs and have them show in the text box. However, the reset functionality seems to elude me. Anyone had any luck there?
Normally put my comments under each other to save space. BUT its gone. (prob pending approval) Anyways I ran ahead and realized in the OnComplete; I can set my text to the new binding. HOWEVER your right in wanting Actions to call other functionality. It also make for cleaner code to update the UI in one place, rather than two. Handling it in the RebindUI, rather than the InputManager
so i have a biiiiig problem please help !! i followed your steps and make it work but wen i build my game it doesn't work at all it does work very good on Unity but wen build my game nothing happens please help !!😕😕
Hmm. This does occasionally happen. Interesting. What doesn't work? Does any part of the system work? It may be worth trying an older more stable build of Unity?
@@OneWheelStudio i have no idea the result is that the system doesn't work at all wen i build the game wen i press the button it should start rebinding but it doesn't do that i tried debugging nothing seems to work but it works perfectly on Unity 🤷🏻♂️🤷🏻♂️
Thanks for the tutorial, it works fine but i have one small problem. How would i handle 2 action in different categories that have the same name? Example: PlayerInput.Player1.Left and PlayerInput.Player2.Left will both change when rebinding PlayerInput.Player1.Left even though they should rebind separately
It's an amazing tutorial and learned a lot about the inner intricacies of working with the new Input System. There's something that I'm trying to figure out though. I want to generate my rebinding UI dynamically by iterating through the Actions of a given InputActionAsset. In the RebindUI script, we are using InputActionReference however I cannot seem to find a way to get the InputActionReference from the asset or just InputAction. Any hints or pointers would be much appreciated! Thanks. Edit: The issue can be avoided altogether by creating a static UI and manually assigning the InputActionReference for each binding. I am still hoping if there's some other way to achieve this instead of manually going through that.
This works amazing in the editor but whenever I build the game this doesn't work. I can rebind but when I load a new scene or reload the game nothing saves. Any help appreciated
I dealt with this in my latest project. You’ll have to save the data to player prefs or some other file - then reload it when needed. Feel free to jump on the OWS discord and I can share some of my code. It may not perfectly fit your needs but might get you started.
@@OneWheelStudio Thanks for the reply, I actually fixed the issue. It was basically the input manager was reading its own input actions and my existing UserInput script which dealt with all the player inputs was reading a different one so I tied my exiting script into the InputManager and it saved all good!
Regarding "Exclude Devices", is it possible to exclude certain keys? I tried adding ".WithControlsExcluding("/escape")", but whenever I try to rebind Escape, instead of doing nothing, it rebinds the input to "Any Key". Is there a way to either prevent it from rebinding the key and switch on a condition (so I can add instructions if this key is pressed: "Please Select a Valid Key") if an excluded key is pressed?
Is there an easy way to detect what controller the player is using and then switch out graphics for that controller? So if the player is on an xbox controller the south button is A, on Playstation it is O, on switch it is B. The triggers look different etc.
There is! I haven’t looked into it, but Unity does have a sample project that does exactly that. Should be available in the package manager - you may have to experiment to figure out which sample exactly.
I've spent the last couple evenings putting together a keybinding system using yours as a reference (which has been super useful by the way) and I was wondering if you knew a way that lets you press a key to unbind while performing interactive rebinding, much like you can cancel rebinding by pressing a key?
Glad it’s been useful. By unbind do you mean have so key bound to that action? I would imagine you might need a UI button to do that. To be honest I haven’t looked into it.
@@OneWheelStudio Yeah so for example you would press backspace and it would clear the binding completely. I can probably figure out a workaround if no such feature is available. Could use the onstarted event to check when the binding is being edited and store that in a bool if the action matches. You then press delete and it completes the interactive binding, then using the oncomplete event check if the bool is true and if delete was pressed, then we can clear the binding and set the bool to false. Theoretically should work, only worry would be if delete would still be pressed on that frame or not. Unfortunately I've just gotten into bed so I can't test it out right now as much as I want to lol
Ok. I can’t remember all the inner workings but seems like you would need to grab the inputs before the rebinding and not let certain values like backspace and delete go through.
@@OneWheelStudio I think my previous comment got removed because I included code, however I came up with a solution to this problem! To clear keybinds, first decide what key you want to use to clear them. Next, at the top of rebind.OnComplete add in a check for the current bindings overridePath and see if it's equal to your chosen keys binding string. For example, the delete key is "/delete". If true, use ApplyBindingOverride on your actionToRebind and set it to an empty string. Clearing will now work, however saving and loading will be broken because the string is empty. To fix this, in the SaveBindingOverride you'll need to check if the current bindings overridePath is null or empty, if it is SetString to something like "empty", else SetString with the overridePath. In LoadBindingOverride, you'll need to GetString and store it in a local variable, then check if it is equal to "empty". If it is, ApplyBindingOverride with an empty string, else ApplyBindingOverride with the stored string. And that's it! It only works out to a few lines of code, but it looks like a monster here because of how I've had to explain it so it doesn't get deleted. I hope this helps anyone that needs this functionality!
Thanks a lot for the tutorial! Is there a way to start the scene with the new keybindings already in place? Keybidings always get reset until I open the RebindUI panel.
I have a generated C# class for my input action, But now I cannot drag and drop the input action asset into RebindUI script in the inspector.... Can anyone help me with that?
is it possible to use this code for rebinding wasd aswell ? if there's multiple input I want to rebind where should I reference it ? or if set up array for InputActionReference will do the trick ?
Great video, but still need a bit of help. How/where would I add an overlay like you were talking about at 31:32, and how could I update the text specifically for each keybind? I have tried adding a gameobject panel but I can reference it in the InputManager because everything is static, and in the RebindUI there is nothing that I can see that I can hook into to turn it off and on.
That's a good question. Hmm. I'm not sure off the top of my head. If you find a solution I'd love to see it and it might be useful for folks in the future.
@@ThatRazzy No sorry, haven't dug into this one. I'm not sure if there is any built in functionality for this or if you just need to loop through all the bindings.
@@OneWheelStudio Thank you for the reply! I've found solutions in other tutorials but unfortunately they are all a bit buggy and don't account for things like interactions and composites. Or if they do there have been bugs :/ Anyways, I think this is good for now. Thanks for the detailed tutorial!
Great job, I dont think I'd be able to do it in less than a week so thank you a lot. But if I may ask this, might there be a problem with "withCancelThrough" because the button I assigned there DOESN'T cancel the operation, and I made sure repeatedly that I am not writing the wrong string when writing the button's path.. however "withtimeout" triggers the on cancel part...
Sorry was traveling. I looked through old comments and someone else had the same issue. The original question was asked by "Game Play" and it was answered by "Videogamemaker" You need to do: Swap the GetBindingInfo call with the InputManager.LoadBindingOverride(actionName) in the OnEnable function in RebindUI. Hope that helps.
First i really want to thank you for this guide. It helped me alot on my study project. ' and 'rebind.OnCancel(operation =>' seen in minute 45:49, in order to make the rebinding work correctly again, after implementing that 'ToggleActionMaps'-Method'. (The problem was, that after pressing the keyboard-buttons to finish the rebind, my mouse disappeared and the player could not move, after closing the menu. Probably those '.Enable();' lines caused that problem.) Did i fix that "bug" by luck, or could those lines be needed in the future and i created another problem, i did not stumble on until now? Greetings and big thanks again.
I got stuck while trying to apply the texts and button into the inspector of rebind UI script. It won't recognize the textor button and doesn't allow me to add. Could you help me with that?
Thank you very much for such a deep explanation, this tutorial is very useful for me. But I have a question. I want to stop the rebinding process if a new key is already used in the Input System and notify observers. Could you please give an idea how it is possible to do?
I'm not entirely sure how this would be done, but it does appear that you can iterate over the bindings in a given input action asset --> InputActionAsset.bindings From there you can iterate through the bindings and check the path assigned to each binding. I'm not sure this is the best way, but it's what I might try first.
@@OneWheelStudio Thanks for the answer! Yes, that's exactly what I've already implemented. By the way, I found that the mouse wheel cannot be rebound separately for each direction. For example, you can't separately rebind a key instead of scrolling up (or down) to, say, zoom in (zoom out). But I found information on the forum that they promise to introduce a new DeltaControl type in the new 1.4 version, which will make it possible.
@@Sentenello Sure, no problem. Each key rebinding loop goes through an intermediate layer that contains an array of all (re)bound buttons. Each array element is a structure as following: publci struct BindingKey { public string Path {get; set;} public string OverridePath {get; set;} public int BindingIndex {get;set;} public string ActionName { get; set; }
... others } When we rebind a key, the corresponding array element will have the relevant OverridePath and will also be null if the rebinding was removed. It means that this array will always reflect up-to-date data. Thus, in order to understand whether there are duplicates, it is necessary to run through the array after each rebinding and compare whether there are keys with the same OverridePath, or Path if OverridePath is null. If there are, then these duplicates are found, then they can be marked if necessary and reflected in the UI This is a very superficial description, I hope it helps you.
@@dmitriisafronov8178 Hey, thank you for the explanation you provided. I'm just wondering if you could go into more detail on the actual code as I'm having difficulty implementing this. Thanks in advance!
Try this: czcams.com/video/KBlmJ5FrXUw/video.html It's designed around saving levels and less around saving general game state, but might give you a head start. While not perfect, I'm a big fan of Easy Save.
so you have a key binding for the new input system as well this indresting however the new input system don't support therd patty controller my question is do you have have eny key binding that use the old input system and have controller support ? if you do make the option to set up the buttons for the controllers and not only the 3 mouse buttons and keyboard binding i know how to make the axes button and i thing i know how to change the joystick number on my own
I don't have any system to do key binding with the old input system and game controllers. There are paid assets out there that do it - but this was one of the big pros with the new input system.
@@OneWheelStudio you maly not remember but you have create a system that call click bind if not mestaiken that supposed all the keyboard binding I don't know how this system works, but it's if know how this system i will add the rest of the off the button by my self but (the controller buttons) but unfortunately I have 0 idea how you manage to make those button to setup anyway it's 5 years old video
Yeah I do remember the tool. I used it myself. The old input system was a pain with controller. I never really bothered to figure it out. With the new input system controllers are easy but rebinding is harder. Sorry wish I had another solution for you.
@@OneWheelStudio okay thanks don't wory I'm sure I will find some way And the fact that you make binding for keyboard that has 100+ key's is more than enough help for my Now I only have to how to make the rest of the system to work and I'm sure I will find the selution don't wory
that is good and all but what if you have more than one player on the same device and you want to Rebinding the key each off thos players it is possible with this method ? for example in my game i have up to 4 players and i will be amazing if eny off thos players able to Rebinding thos keys eny way if you don't know the answer don't worry about it im sure i will find the solution im looking on in fact a already find a way to Rebinding keys with the old Input method that supports all therd party controllers but it's in progress
Thanks for the tutorial! Everything works fine except for the "rebind.WithCancelingThrough(">/backspace"); " option. I thought it worked because the UI shows the rebinding was canceled, but when I type any key after that, it rebinds to that button. ^^ Did I miss something? Do you have any idea what's going wrong? Thanks! :)
This problem has come up a handful of times in recent weeks. I'm wondering if it's a version issue - as in Unity changed or broke something. I wish I could be more helpful. You can dive into Unity's example - maybe that's been updated too? I stole a lot of my code directly from the project.
@@linzenmeyer input system with old ui system ... you need unity 2019.1 minimum for the new input system so why not using ui toolkit ... both input allow you to manage all existing input so the only difference is in performance. ui toolkit is faster than ugui so keeping using old ui with new input system is similar to using an 3090 video card with any 25 years old computer. anyway it's only a question of time before the ui is deprecated.
No blog post for this video, but here is the code from the video: github.com/onewheelstudio/Adventures-in-C-Sharp/tree/main/Key%20Rebind
thx!!
Thanks again for the tutorial but composite rebinding do not work at all...
I wonder if something changed. You aren’t the first to say that - check the comments IIRC someone posted a fix.
Took me a while to figure out the composite rebinding issue. The issue lies with what is being passed into the "recursive" DoRebind. The original binding index was accidentally passed instead of the firstPartIndex and the check should be for isPartOfComposite and not the actual composite
Currently on Github:
if (firstPartIndex < action.bindings.Count && action.bindings[firstPartIndex].isComposite)
DoRebind(action, bindingIndex, statusText, true, excludeMouse);
works for me:
if (firstPartIndex < action.bindings.Count && action.bindings[firstPartIndex].isPartOfComposite)
DoRebind(action, firstPartIndex , statusText, true, excludeMouse);
Thank you for this. I spent all day failing to wrap my mind around key rebinding with the new Input System, until I came across this video. It's both comprehensive and easy to follow. You're a great communicator.
If you're having actionName be null in build, but work fine in the editor.
if (actionName == null)
return;
Do that in LoadBindingOverride
thanks!
The issue with this is its not loading the saved data,
Holy Molly this works. I’ve been stuck with rebinding for half a month. Thank you so sooo much!
You can’t imagine how overwhelming and difficult such ‘simple’ things are for a newcomer like me. Finally I can continue working on my game.
That's because the new Input System is catastrophically bad for newcomers to Unity. It is really, REALLY bad. I don't know what they were thinking, really.
Such an amazing tutorial, I found some others but they didn't address the issue with using the scriptable object versus the c# generated script. Such a life saver!
I had to watch this few times in order to get my head around things but overall well done on a topic that hasn't been documented very well. Awesome job.
Fraking ace videos probably one of the best I seen straight to the point clean and very well explained! Much love for these!
This is the best input binding video that I understand it beautifully.
Thank you so much for your great explanation.
most professional tutorial I've seen on CZcams regarding any Unity topic! Good job! Very useful!
Had an absolute nightmare trying to get this working. This video potentially saved me days of torment. Thank you for explaining this so well!
This is the best video on CZcams for understanding how to rebind keys with the new input system. Thank you!
Thank you for this tutorial, very helpful. If anyone runs into the problems I had here are some changes I had to make:
- My InputManager is set up as a singleton, so I had to change all the Null checks in LoadBindingOverride(), GetBindingName() etc. accordingly:
if (Instance.inputActions == null)
Instance.inputActions = new InputActions();
- I was also getting Null reference exceptions due to actionName not being references, I assume this is happening because of script execution order. I "fixed" it by adding:
if (actionName == null)
return;
to LoadBindingOverride()
- Since the Instance of my InputManager is handled in Awake, I had to move all the OnEnable() stuff from the ReBindUI script to Start, so the instance already exists in the scene before anything gets called from there. Except for the two lines of UpdateUI event subscription.
- I also had to change OnValidate because the Instance doesn't exist when this is first called. But only when running the game, otherwise the changes made in the Inspector (like changing the keybind) wouldn't be reflected:
if (Application.isPlaying)
if (InputManager.Instance == null)
return;
Thanks so much for the actionName == Null hint, now there's no null reference exception in the build, DANKE!!!
Hello, currently I encounter the same problem within build. Injecting code works but not for the first part of your tip (Instance.inputActions == null...). Unity said to me that "instance.inputActions" is an inaccessible reference in the context. Remove the "static" parameter to the reference seems to clear the problem but it's occur errors on other sides of functions. How did you override this problem ? Thanks,
dis iz a hella dynamic system bro, good job
Thanks for answering the most important question upfront, that the example does not work with the C# generated class.
Excellent tutorial and super thanks for providing the code.
As always, thank you very much for this awesome video !
Thanks so much for this video, it has been really useful! I thought the route of using generated code would end up being painful, but you've explained it all.
This is an immense tutorial! Thank you. It has been so helpful, I never would have managed this on my own.
As others have pointed out there were a few errors but solutions have also been provided, so that's great.
I played about with getting the composite individual key names to appear during binding, but I think I'll go the route of having each key, W A S D on their own, so that it is much clear which key you are currently rebinding.
Also I'll add in some extra functionality to alert the player if they are overwriting a key which has already been bound. When playing with the composites I bound Up, Down, Left, and Right all to the same key :D So I'll have to find a way to prevent that.
Did you manage to figure out the part with separating composite binding?
@@Not_Lucifer No. I can't change composite to none composite, or vice versa. So I just noticed it in the bug notes when I released my demo
dear "One Wheel Studio", I really liked this video, I congratulate you for helping us, now I'm going to study all your videos, thanks
Glad it was useful! Feel free to drop by the OWS discord if you've got questions :)
This was extremely helpful. Thanks a lot!
Thank you
I can't understand all of them but almost!
Just finished implementing all this stuff, works great, thanks (tested on build, worked either).
It's pain in the ass that we can't add multiple Cancel Rebind Buttons, but right now (I dunno if you could do it in August) it's better to add InputActionReference to WithCancelingThrough function rather than hardcode a string.
So in RebindManager we can add serialized reference to some InputActionReference and then use it like this:
>rebind.WithCancelingThrough(cancelRebindButton.action.controls[bindingIndex]);
I personally made a separate key for canceling with 2 bindings - first one for keyboard and second one for gamepad (how you choose what to use is up to you).
Returning here to say that rebinding saves are actually acting kinda strange in the actual build. So, when you are changing bindings, they are saved and stuff. But when you close the game and run it again they are not working (even though my rebinding UI panel is showing me the new inputs). The coolest part is that when I'm making an absolute the same build new bindings are working again until I change some of them. In that case everything is reset to default once I restart the game.
I have two questions:
1) Did you try out the composite rebinding? I'm having trouble with it. It seems the sub-bindings for lack of a better term return FALSE for isComposite- only the first one (the WASD one) returns true for that. The subsequent ones return true for isPartOfComposite, but if I just sub those out as -seems- appropriate, I get an InvalidOperationException: Cannot perform rebinding on composite binding 'Move:Dpad'
2) Is there a particular reason you didn't just make the InputManager just use a singleton for the InputActions reference? Seems like a decent use case for a singleton, and would resolve the issue of forgetting to put the InputManager script on an object in the scene.
Ok, figured out the first problem. Using the line numbers in your linked project on github:
Line 34 is:
if (firstPartIndex < action.bindings.Count && action.bindings[firstPartIndex].isComposite)
But needs to be:
if (firstPartIndex < action.bindings.Count && action.bindings[firstPartIndex].isPartOfComposite)
(change .isComposite to .isPartOfComposite)
Line 35 is:
DoRebind(action, bindingIndex, statusText, true, excludeMouse);
But needs to be
DoRebind(action, firstPartIndex, statusText, true, excludeMouse);
(Need to pass firstPartIndex, not bindingIndex. If you pass bindingIndex, you get that invalid operation exception)
Line 85 is:
if (nextBindingIndex < actionToRebind.bindings.Count && actionToRebind.bindings[nextBindingIndex].isComposite)
But needs to be
if (nextBindingIndex < actionToRebind.bindings.Count && actionToRebind.bindings[nextBindingIndex].isPartOfComposite)
(Same issue as the first correction)
There is still some wonkiness with this approach, since as-presented, the order that the user needs to press their new bindings in isn't terribly clear. Despite the display reading W/A/S/D, the order the buttons actually need to be pressed in (in my case, at least) is, Up, Down, Left, Right. I'm going to work on adding some extra feedback to the status text to help make this more clear to the user.
Oops thanks for the catch. I did test it but I must have copied the code wrong when recording.
@@OneWheelStudio On the subject of point #1, implementing the InputManager as a singleton (instead of extending monobehaviour) worked quite well for me. This way, I don't need to worry about having the InputManager attached to some object in the scene. I also included logic to apply custom keybind overrides in this section, such that they'll get applied as soon as ANY part of the project attempts to access the InputManagaer instance. I did it like this (RiverGameInputActions is the name of my c# generated InputActions class):
private static RiverGameInputActions _inputActions;
public static RiverGameInputActions inputActions
{
get
{
if(_inputActions == null)
{
SetupInputActionsInstance();
}
return _inputActions;
}
}
private static void SetupInputActionsInstance()
{
_inputActions = new RiverGameInputActions();
var maps = _inputActions.asset.actionMaps;
for(int i = 0; i < maps.Count; i++)
{
//Debug.Log("MAP: " + maps[i].name);
var actions = maps[i].actions;
for(int j = 0; j < actions.Count; j++)
{
//Debug.Log("---ACTION: " + actions[j].name);
//Load the custom bindings
LoadBindingOverride(actions[j].name);
var bindings = actions[j].bindings;
for(int k = 0; k < bindings.Count; k++)
{
//Debug.Log($"___---Binding path: {bindings[k].effectivePath}");
}
}
}
}
@@Frenotx Following your advice here, I was able to also fix the composite input issue. I am trying to show also extra feedback to the status text. So far I have this:
// If it's a part binding, show the name of the part in the UI.
if (actionToRebind.bindings[bindingIndex].isPartOfComposite)
{
statusText.text = $"Binding '{actionToRebind.bindings[bindingIndex].name}'. ";
}
else
statusText.text = "Press a " + actionToRebind.expectedControlType;
But it only works on the first biding and not the rest. Have you manage to do this? Thank you! :D
@@Frenotx Thank you for your help! I noticed though that the reset button doesn't work though. Any idea how to fix that? Thanks in advance!
Fantastic video - thanks for your time creating it!
Thank you for this explanation, wanna add keybindings to my game and they maybe make a Co-op, this video helps a lot to understanding this topic.
I gotta say. great video! Saved me a headache
Oh my god, finally something that works ! Thank you so much :)
Thank you sir!
Your tutorial is really great, very helpful. Thank you for sharing this.
The reset code has a bug where you can't reset composite bindings. To fix, remove "&& action.bindings[i].isComposite" in the for loop. You don't need to check if each binding isComposite. You only need to know if the main binding is.
Fixed code for 48:47:
if (action.bindings[bindingIndex].isComposite)
{
for (int i = bindingIndex; i < action.bindings.Count; i++)
{
action.RemoveBindingOverride(i);
}
}
Original video and your answer both have errors, actually you have to check for isPartOfComposiste in a loop, according to Unity documentation.
Checking for action.bindings.Count means you will rebind from first composite till the end of all bindings.
Hey, thanks a lot for the video, huge time saver. Otherwise i'd have to completely dig into unity code samples
One small note: it should be if (nextBindingIndex < action.bindings.Count && action.bindings[nextBindingIndex].isPartOfComposite)
isntead of if (nextBindingIndex < action.bindings.Count && action.bindings[nextBindingIndex].isComposite)
Otherwise rebinding of composite actions wouldn't work properly
Regards your race condition:
OnValidate runs before Awake
and OnEnable runs after Awake
Therefore your trying to access the InputActions in Validate to rebind the UI. Which beats the Awake of the InputManager.
Switching InputManager to OnValidate does work, in my case, but still runs a race against who runs first.
Can I ask you one thing about saving string to the next scene when I load second scene my Inputs only saves when i have canvas in with same keybinds but if canvas is disabled or there is no keybindings on canvas, game just using default keys not saved ones from previous scene... its really strange is not global saved them keybindings any idea how to approach this issue?
Hmm. I haven't played with that. If I had to guess, it would be that the key binding is saved with the instance of the Input Action Asset. So, maybe, one work around would be to make sure the object that has a reference to the input action asset is not destroyed between scenes?
@@OneWheelStudio try make it with persist way to save it globally in inputs, to REBIND them, not to change them in script
Hi! This tutorial really is so helpful and works like a charm with the C# generated class. While using it I'm just encountering an issue, if you might be able to help or anyone, please contact :( the problem comes if you try to rebind the UI asset (let's say Submit or Cancel actions), since the Event System, or in this case, the InputSystemUIModule is using the scriptable object as the actions asset, when you rebind the control, it doesnt update since you are updating the C# class instead of the scriptable object itself, therefore you are not able to rebind UI actions.
My workaround was simply when doing the rebind, update the UIModule to use the instance created during the rebind, it kind of work, the rebinded control is properly set, but you lose the references of the other actions (such as Point, Navigate, etc)
Found a way to make it work. If anyone has the same issue feel free to ask and I will post the solution^^
@@overgastoru Hi I'm actually having this same issue as well, was wondering what your solution ended up being
Great video, thank you :D
It is a fantastic tutorial that i didnt notice it was 50 damn minutes😅😂😂😂
Hi, can You instead of making a Input Rebinding in script which must be scripted to be saved and loaded, make some universal script for rebind using global persist way?
You are a Hero
Very helpful, unfortunately I have been really struggling trying to fix the composite input side of things. Using the great fixes provided in the comments I have been able to change the composite inputs and have them show in the text box. However, the reset functionality seems to elude me. Anyone had any luck there?
Normally put my comments under each other to save space. BUT its gone. (prob pending approval)
Anyways I ran ahead and realized in the OnComplete; I can set my text to the new binding.
HOWEVER your right in wanting Actions to call other functionality.
It also make for cleaner code to update the UI in one place, rather than two. Handling it in the RebindUI, rather than the InputManager
what about assigning already existing key problem ?
so i have a biiiiig problem please help !!
i followed your steps and make it work but wen i build my game it doesn't work at all
it does work very good on Unity but wen build my game nothing happens
please help !!😕😕
Hmm. This does occasionally happen. Interesting. What doesn't work? Does any part of the system work? It may be worth trying an older more stable build of Unity?
@@OneWheelStudio
i have no idea
the result is that the system doesn't work at all wen i build the game
wen i press the button it should start rebinding but it doesn't do that
i tried debugging nothing seems to work
but it works perfectly on Unity 🤷🏻♂️🤷🏻♂️
@@GamePlay-dh5mb Sorry. That's a hard one to debug. I'd suggest trying an older version of Unity 2019 or 2020 LTS.
@@GamePlay-dh5mb Did you ever figure this out? I'm having the same issue.
Same issue here
Thanks for the tutorial, it works fine but i have one small problem.
How would i handle 2 action in different categories that have the same name?
Example:
PlayerInput.Player1.Left and PlayerInput.Player2.Left will both change when rebinding PlayerInput.Player1.Left even though they should rebind separately
It's an amazing tutorial and learned a lot about the inner intricacies of working with the new Input System.
There's something that I'm trying to figure out though. I want to generate my rebinding UI dynamically by iterating through the Actions of a given InputActionAsset. In the RebindUI script, we are using InputActionReference however I cannot seem to find a way to get the InputActionReference from the asset or just InputAction. Any hints or pointers would be much appreciated! Thanks.
Edit: The issue can be avoided altogether by creating a static UI and manually assigning the InputActionReference for each binding. I am still hoping if there's some other way to achieve this instead of manually going through that.
This works amazing in the editor but whenever I build the game this doesn't work. I can rebind but when I load a new scene or reload the game nothing saves. Any help appreciated
I dealt with this in my latest project. You’ll have to save the data to player prefs or some other file - then reload it when needed.
Feel free to jump on the OWS discord and I can share some of my code. It may not perfectly fit your needs but might get you started.
@@OneWheelStudio Thanks for the reply, I actually fixed the issue. It was basically the input manager was reading its own input actions and my existing UserInput script which dealt with all the player inputs was reading a different one so I tied my exiting script into the InputManager and it saved all good!
Regarding "Exclude Devices", is it possible to exclude certain keys? I tried adding ".WithControlsExcluding("/escape")", but whenever I try to rebind Escape, instead of doing nothing, it rebinds the input to "Any Key". Is there a way to either prevent it from rebinding the key and switch on a condition (so I can add instructions if this key is pressed: "Please Select a Valid Key") if an excluded key is pressed?
lmao I ditched the project I was working on for 4 month exactly when I was trying to implement this :)
Is there an easy way to detect what controller the player is using and then switch out graphics for that controller? So if the player is on an xbox controller the south button is A, on Playstation it is O, on switch it is B. The triggers look different etc.
There is! I haven’t looked into it, but Unity does have a sample project that does exactly that. Should be available in the package manager - you may have to experiment to figure out which sample exactly.
I've spent the last couple evenings putting together a keybinding system using yours as a reference (which has been super useful by the way) and I was wondering if you knew a way that lets you press a key to unbind while performing interactive rebinding, much like you can cancel rebinding by pressing a key?
Glad it’s been useful. By unbind do you mean have so key bound to that action? I would imagine you might need a UI button to do that. To be honest I haven’t looked into it.
@@OneWheelStudio Yeah so for example you would press backspace and it would clear the binding completely.
I can probably figure out a workaround if no such feature is available. Could use the onstarted event to check when the binding is being edited and store that in a bool if the action matches. You then press delete and it completes the interactive binding, then using the oncomplete event check if the bool is true and if delete was pressed, then we can clear the binding and set the bool to false. Theoretically should work, only worry would be if delete would still be pressed on that frame or not.
Unfortunately I've just gotten into bed so I can't test it out right now as much as I want to lol
Ok. I can’t remember all the inner workings but seems like you would need to grab the inputs before the rebinding and not let certain values like backspace and delete go through.
@@OneWheelStudio I think my previous comment got removed because I included code, however I came up with a solution to this problem!
To clear keybinds, first decide what key you want to use to clear them.
Next, at the top of rebind.OnComplete add in a check for the current bindings overridePath and see if it's equal to your chosen keys binding string. For example, the delete key is "/delete".
If true, use ApplyBindingOverride on your actionToRebind and set it to an empty string.
Clearing will now work, however saving and loading will be broken because the string is empty.
To fix this, in the SaveBindingOverride you'll need to check if the current bindings overridePath is null or empty, if it is SetString to something like "empty", else SetString with the overridePath.
In LoadBindingOverride, you'll need to GetString and store it in a local variable, then check if it is equal to "empty". If it is, ApplyBindingOverride with an empty string, else ApplyBindingOverride with the stored string.
And that's it! It only works out to a few lines of code, but it looks like a monster here because of how I've had to explain it so it doesn't get deleted. I hope this helps anyone that needs this functionality!
Thanks a lot for the tutorial! Is there a way to start the scene with the new keybindings already in place? Keybidings always get reset until I open the RebindUI panel.
Life saver !
I have a generated C# class for my input action, But now I cannot drag and drop the input action asset into RebindUI script in the inspector.... Can anyone help me with that?
Sorry. Never used RebindUI.
is it possible to use this code for rebinding wasd aswell ?
if there's multiple input I want to rebind where should I reference it ?
or if set up array for InputActionReference will do the trick ?
Great video, but still need a bit of help. How/where would I add an overlay like you were talking about at 31:32, and how could I update the text specifically for each keybind? I have tried adding a gameobject panel but I can reference it in the InputManager because everything is static, and in the RebindUI there is nothing that I can see that I can hook into to turn it off and on.
I am very new to the Input system so I don't understand some of the concepts fully yet
how to check for duplicate bindings?
That's a good question. Hmm. I'm not sure off the top of my head. If you find a solution I'd love to see it and it might be useful for folks in the future.
@@OneWheelStudio Hey, any update on this? Really curious to know how you'd approach it. Thanks! :)
@@ThatRazzy No sorry, haven't dug into this one. I'm not sure if there is any built in functionality for this or if you just need to loop through all the bindings.
@@OneWheelStudio Thank you for the reply! I've found solutions in other tutorials but unfortunately they are all a bit buggy and don't account for things like interactions and composites. Or if they do there have been bugs :/ Anyways, I think this is good for now. Thanks for the detailed tutorial!
Great job, I dont think I'd be able to do it in less than a week so thank you a lot.
But if I may ask this, might there be a problem with "withCancelThrough" because the button I assigned there
DOESN'T cancel the operation, and I made sure repeatedly that I am not writing the wrong string when writing the button's path.. however "withtimeout" triggers the on cancel part...
Hmm. I'm not sure. I was able to make it work well. Does it work with other keys?
friend know why when exporting the project does not work? :(
Sorry was traveling. I looked through old comments and someone else had the same issue. The original question was asked by "Game Play" and it was answered by "Videogamemaker"
You need to do: Swap the GetBindingInfo call with the InputManager.LoadBindingOverride(actionName) in the OnEnable function in RebindUI.
Hope that helps.
@@OneWheelStudio thank you very much
First i really want to thank you for this guide. It helped me alot on my study project. ' and 'rebind.OnCancel(operation =>' seen in minute 45:49, in order to make the rebinding work correctly again, after implementing that 'ToggleActionMaps'-Method'.
(The problem was, that after pressing the keyboard-buttons to finish the rebind, my mouse disappeared and the player could not move, after closing the menu. Probably those '.Enable();' lines caused that problem.)
Did i fix that "bug" by luck, or could those lines be needed in the future and i created another problem, i did not stumble on until now?
Greetings and big thanks again.
I got stuck while trying to apply the texts and button into the inspector of rebind UI script. It won't recognize the textor button and doesn't allow me to add. Could you help me with that?
Have you added in the UI namespace? At the top you need to include "using UnityEngine.UI"
I can't remember if the button uses TextMeshPro. If it does you'll need to add "using TMPro"
Thank you very much for such a deep explanation, this tutorial is very useful for me.
But I have a question.
I want to stop the rebinding process if a new key is already used in the Input System and notify observers.
Could you please give an idea how it is possible to do?
I'm not entirely sure how this would be done, but it does appear that you can iterate over the bindings in a given input action asset --> InputActionAsset.bindings
From there you can iterate through the bindings and check the path assigned to each binding. I'm not sure this is the best way, but it's what I might try first.
@@OneWheelStudio
Thanks for the answer!
Yes, that's exactly what I've already implemented.
By the way, I found that the mouse wheel cannot be rebound separately for each direction.
For example, you can't separately rebind a key instead of scrolling up (or down) to, say, zoom in (zoom out).
But I found information on the forum that they promise to introduce a new DeltaControl type in the new 1.4 version, which will make it possible.
@@dmitriisafronov8178 can you tell us how you implemented the check for no doubles 😁
@@Sentenello Sure, no problem.
Each key rebinding loop goes through an intermediate layer that contains an array of all (re)bound buttons.
Each array element is a structure as following:
publci struct BindingKey {
public string Path {get; set;}
public string OverridePath {get; set;}
public int BindingIndex {get;set;}
public string ActionName { get; set; }
... others
}
When we rebind a key, the corresponding array element will have the relevant OverridePath and will also be null if the rebinding was removed. It means that this array will always reflect up-to-date data.
Thus, in order to understand whether there are duplicates, it is necessary to run through the array after each rebinding and compare whether there are keys with the same OverridePath, or Path if OverridePath is null.
If there are, then these duplicates are found, then they can be marked if necessary and reflected in the UI
This is a very superficial description, I hope it helps you.
@@dmitriisafronov8178 Hey, thank you for the explanation you provided. I'm just wondering if you could go into more detail on the actual code as I'm having difficulty implementing this. Thanks in advance!
What if I wanted to change an interaction? Say change a button from Press to Hold or Double Tap?
That I haven't dug into. I'm sure it's possible, but just haven't played with that.
Do you have a good video for a save/load system? Sometimes that most games seem to have at the beginning of a new game.
Try this: czcams.com/video/KBlmJ5FrXUw/video.html
It's designed around saving levels and less around saving general game state, but might give you a head start. While not perfect, I'm a big fan of Easy Save.
so you have a key binding for the new input system as well this indresting
however the new input system don't support therd patty controller
my question is do you have have eny key binding that use the old input system and have controller support ?
if you do make the option to set up the buttons for the controllers and not only the 3 mouse buttons and keyboard binding i know how to make the axes button and i thing i know how to change the joystick number on my own
I don't have any system to do key binding with the old input system and game controllers. There are paid assets out there that do it - but this was one of the big pros with the new input system.
@@OneWheelStudio you maly not remember but you have create a system that call click bind if not mestaiken that supposed all the keyboard binding
I don't know how this system works,
but it's if know how this system i will add the rest of the off the button by my self but (the controller buttons) but unfortunately I have 0 idea how you manage to make those button to setup anyway it's 5 years old video
Yeah I do remember the tool. I used it myself. The old input system was a pain with controller. I never really bothered to figure it out. With the new input system controllers are easy but rebinding is harder. Sorry wish I had another solution for you.
@@OneWheelStudio okay thanks don't wory I'm sure I will find some way
And the fact that you make binding for keyboard that has 100+ key's is more than enough help for my
Now I only have to how to make the rest of the system to work and I'm sure I will find the selution don't wory
that is good and all but what if you have more than one player on the same device and you want to Rebinding the key each off thos players it is possible with this method ?
for example in my game i have up to 4 players and i will be amazing if eny off thos players able to Rebinding thos keys
eny way if you don't know the answer don't worry about it im sure i will find the solution im looking on in fact a already find a way to Rebinding keys with the old Input method that supports all therd party controllers but it's in progress
Thanks for the tutorial! Everything works fine except for the "rebind.WithCancelingThrough(">/backspace"); " option. I thought it worked because the UI shows the rebinding was canceled, but when I type any key after that, it rebinds to that button. ^^ Did I miss something? Do you have any idea what's going wrong? Thanks! :)
This problem has come up a handful of times in recent weeks. I'm wondering if it's a version issue - as in Unity changed or broke something.
I wish I could be more helpful. You can dive into Unity's example - maybe that's been updated too? I stole a lot of my code directly from the project.
@@OneWheelStudio Okay thanks ! I will take a look ;)
@@OneWheelStudio I also noticed, that when I press the cancelingThrough button 2 times in a row, it works and a new rebind isn't possible.
Hmm. Weird. Any chance there two instances running?
@@OneWheelStudio I solved it by upgrading to Unity 2021.2.19f1 👍🏼
i still cant get mine to rebind
aah my bad looks like i also forgot to put the inputmanager in the scene, thank you for this
Danke!
Much appreciated! Hope it was helpful.
useless ...
Lol, just like your comment. Perhaps you can expand on your single word and let me know why you think that? Maybe it would help me.
@@linzenmeyer input system with old ui system ... you need unity 2019.1 minimum for the new input system so why not using ui toolkit ... both input allow you to manage all existing input so the only difference is in performance. ui toolkit is faster than ugui so keeping using old ui with new input system is similar to using an 3090 video card with any 25 years old computer. anyway it's only a question of time before the ui is deprecated.