Search Unity

Showcase Configurable controls, using the old input system

Discussion in 'Scripting' started by Were_Elf, Apr 10, 2022.

  1. Were_Elf

    Were_Elf

    Joined:
    Feb 25, 2022
    Posts:
    53
    Last time I searched how to make configurable player controls, I only found some threads, commenting how to do it for the new input system, so I had to figure stuff out on my own. But now that I have, I'm sharing what I came up with:
    1) I'm using an enum, containing all of the actions with configurable input, but one can just use integers or strings as IDs, if they don't wanna use enums (I think it's more intuitive this way tho):
    Code (CSharp):
    1. public enum Controls
    2. {
    3.     Stop,
    4.     SpellOne,
    5.     SpellTwo,
    6.     SpellThree,
    7.     SpellFour,
    8.     SpellFive,
    9.     SpellSix,
    10.     CameraFollow,
    11.     CenterCamera,
    12.     ResetRotation,
    13.     RotateClockwise,
    14.     RotateCountercl,
    15.     RotateDownwards,
    16.     RotateUpdawdrs
    17. }
    2) I create a Dictionary<Controls, KeyCode> for my player class (can be static for single player games)
    Code (CSharp):
    1. public class PlayerScript : Player // My AIScript is also derived from player
    2. {
    3.     //.......
    4.     public Dictionary<Controls, KeyCode> controls; //Can be int/char/string instead of Controls
    5.     //.........
    6.  
    7.     private void Awake()
    8.     {
    9.         //.....
    10.         controls = new Dictionary<Controls, KeyCode>();
    11.         // Initiating the controls, will be described in the next part.
    12. }
    3) In the player Awake method I initiate the controls:
    Code (CSharp):
    1.     private void Awake()
    2.     {
    3.         //...........
    4.         path = Application.persistentDataPath + "/PlayerControlls.ini"; // path is a static string
    5.         bool initialize = false;
    6.         if (System.IO.File.Exists(path)) //Checking if Controls already exist, if yes - load them, otherwise create a new file.
    7.         {
    8.             System.IO.StreamReader reader = new System.IO.StreamReader(path);
    9.             while (!reader.EndOfStream)
    10.             {
    11.                 string[] row = reader.ReadLine().Split((char)32); //(char)32 is space
    12.                 Controls ctrl = 0;
    13.                 if (!GameControllerScript.StringToEnum<Controls>(row[0], ref ctrl)) // Personally I have put this method into my GameControllerScript, but it can be anywhere.
    14.                 {
    15.                     initialize = true;
    16.                     break;
    17.                 }
    18.                 if (controls.ContainsKey(ctrl))
    19.                 {
    20.                     initialize = true;
    21.                     break;
    22.                 }
    23.                 KeyCode key = 0;
    24.                 if (!GameControllerScript.StringToEnum<KeyCode>(row[1], ref key))
    25.                 {
    26.                     initialize = true;
    27.                     break;
    28.                 }
    29.                 controls.Add(ctrl, key); // Loading the control into the Dictionary
    30.             }
    31.             reader.Close();
    32.             //verify:
    33.             foreach (Controls ctrls in System.Enum.GetValues(typeof(Controls)))
    34.             {
    35.                 if (!controls.ContainsKey(ctrls))
    36.                 {
    37.                     initialize = true;
    38.                     break;
    39.                 }
    40.             }
    41.         }
    42.         else
    43.         {
    44.             initialize = true;
    45.         }
    46.         if (initialize) //Controls file doesn't exist, or has been corrupted - restore default settings.
    47.         {
    48.             DefaultControls();
    49.         }
    50.         // More things, if necessary. Personally I update some interface elements, showing which keys are used for the spells.
    51.     }
    And here are the called functions:
    Code (CSharp):
    1.     public void DefaultControls()
    2.     {
    3.         controls.Clear();
    4.         controls.Add(Controls.Stop, KeyCode.S);
    5.         controls.Add(Controls.SpellOne, KeyCode.Q);
    6.         controls.Add(Controls.SpellTwo, KeyCode.W);
    7.         controls.Add(Controls.SpellThree, KeyCode.E);
    8.         controls.Add(Controls.SpellFour, KeyCode.R);
    9.         controls.Add(Controls.SpellFive, KeyCode.D);
    10.         controls.Add(Controls.SpellSix, KeyCode.F);
    11.         controls.Add(Controls.CameraFollow, KeyCode.L);
    12.         controls.Add(Controls.CenterCamera, KeyCode.Space);
    13.         controls.Add(Controls.ResetRotation, KeyCode.Semicolon);
    14.         controls.Add(Controls.RotateClockwise, KeyCode.LeftBracket);
    15.         controls.Add(Controls.RotateCountercl, KeyCode.RightBracket);
    16.         controls.Add(Controls.RotateDownwards, KeyCode.PageDown);
    17.         controls.Add(Controls.RotateUpdawdrs, KeyCode.PageUp);
    18.         SaveControls();
    19.     }
    20.  
    21.     public void SaveControls()
    22.     {
    23.         string fileText = "";
    24.         fileText += Controls.Stop + " " + controls[Controls.Stop] + "\n"
    25.                  + Controls.SpellOne + " " + controls[Controls.SpellOne] + "\n"
    26.                  + Controls.SpellTwo + " " + controls[Controls.SpellTwo] + "\n"
    27.                  + Controls.SpellThree + " " + controls[Controls.SpellThree] + "\n"
    28.                  + Controls.SpellFour + " " + controls[Controls.SpellFour] + "\n"
    29.                  + Controls.SpellFive + " " + controls[Controls.SpellFive] + "\n"
    30.                  + Controls.SpellSix + " " + controls[Controls.SpellSix] + "\n"
    31.                  + Controls.CameraFollow + " " + controls[Controls.CameraFollow] + "\n"
    32.                  + Controls.CenterCamera + " " + controls[Controls.CenterCamera] + "\n"
    33.                  + Controls.ResetRotation + " " + controls[Controls.ResetRotation] + "\n"
    34.                  + Controls.RotateClockwise + " " + controls[Controls.RotateClockwise] + "\n"
    35.                  + Controls.RotateCountercl + " " + controls[Controls.RotateCountercl] + "\n"
    36.                  + Controls.RotateDownwards + " " + controls[Controls.RotateDownwards] + "\n"
    37.                  + Controls.RotateUpdawdrs + " " + controls[Controls.RotateUpdawrs];
    38.         System.IO.File.WriteAllText(path, fileText);
    39.         // Here I'm updating the interface.
    40.     }
    Code (CSharp):
    1.     public static bool StringToEnum<T>(string val, ref T t)
    2.     {
    3.         foreach (T value in System.Enum.GetValues(typeof(T)))
    4.         {
    5.             if (val.Equals(value.ToString()))
    6.             {
    7.                 t = value;
    8.                 return true;
    9.             }
    10.         }
    11.         return false;
    12.     }
    4) I formulate my input operations as such:
    Code (CSharp):
    1.  
    2.                 if (Input.GetKeyDown(player.controls[Controls.SpellOne])) PrepareSpell(1);
    3.                 else if (Input.GetKeyDown(player.controls[Controls.SpellTwo])) PrepareSpell(2);
    4.                 else if (Input.GetKeyDown(player.controls[Controls.SpellThree])) PrepareSpell(3);
    5.                 else if (Input.GetKeyDown(player.controls[Controls.SpellFour])) PrepareSpell(4);
    6.                 else if (Input.GetKeyDown(player.controls[Controls.SpellFive])) PrepareSpell(5);
    7.                 else if (Input.GetKeyDown(player.controls[Controls.SpellSix])) PrepareSpell(6);
    8.                 // And so on
    What we have so far is functioning, but requires you to change the ini file in order to change the controls.
    5) Implement user interface for changing the controls.... I could share how I've done this part, but I don't think that the way I've done so is the best, so I'll only share some key elements:
    - While the player is setting up the keys, I'm "listening" for Input.anyKeyDown and then I "translate this input, using my GetKeyDown() method:
    Code (CSharp):
    1.     public static KeyCode GetKeyDown() // Note that this list is incomplete. I am not listening for mouse inputs or the inputs of the arrow keys, because these controls are hard baked into how my game is played (for now).
    2.     {
    3.         KeyCode pressed = KeyCode.None;
    4.         if (Input.GetKeyDown(KeyCode.Escape)) pressed = KeyCode.Escape;
    5.         else if (Input.GetKeyDown(KeyCode.Space)) pressed = KeyCode.Space;
    6.         else if (Input.GetKeyDown(KeyCode.F1)) pressed = KeyCode.F1;
    7.         else if (Input.GetKeyDown(KeyCode.F2)) pressed = KeyCode.F2;
    8.         else if (Input.GetKeyDown(KeyCode.F3)) pressed = KeyCode.F3;
    9.         else if (Input.GetKeyDown(KeyCode.F4)) pressed = KeyCode.F4;
    10.         else if (Input.GetKeyDown(KeyCode.F5)) pressed = KeyCode.F5;
    11.         else if (Input.GetKeyDown(KeyCode.F6)) pressed = KeyCode.F6;
    12.         else if (Input.GetKeyDown(KeyCode.F7)) pressed = KeyCode.F7;
    13.         else if (Input.GetKeyDown(KeyCode.F8)) pressed = KeyCode.F8;
    14.         else if (Input.GetKeyDown(KeyCode.F9)) pressed = KeyCode.F9;
    15.         else if (Input.GetKeyDown(KeyCode.F10)) pressed = KeyCode.F10;
    16.         else if (Input.GetKeyDown(KeyCode.F11)) pressed = KeyCode.F11;
    17.         else if (Input.GetKeyDown(KeyCode.F12)) pressed = KeyCode.F12;
    18.         else if (Input.GetKeyDown(KeyCode.Tilde)) pressed = KeyCode.Tilde;
    19.         else if (Input.GetKeyDown(KeyCode.Alpha0)) pressed = KeyCode.Alpha0;
    20.         else if (Input.GetKeyDown(KeyCode.Alpha1)) pressed = KeyCode.Alpha1;
    21.         else if (Input.GetKeyDown(KeyCode.Alpha2)) pressed = KeyCode.Alpha2;
    22.         else if (Input.GetKeyDown(KeyCode.Alpha3)) pressed = KeyCode.Alpha3;
    23.         else if (Input.GetKeyDown(KeyCode.Alpha4)) pressed = KeyCode.Alpha4;
    24.         else if (Input.GetKeyDown(KeyCode.Alpha5)) pressed = KeyCode.Alpha5;
    25.         else if (Input.GetKeyDown(KeyCode.Alpha6)) pressed = KeyCode.Alpha6;
    26.         else if (Input.GetKeyDown(KeyCode.Alpha7)) pressed = KeyCode.Alpha7;
    27.         else if (Input.GetKeyDown(KeyCode.Alpha8)) pressed = KeyCode.Alpha8;
    28.         else if (Input.GetKeyDown(KeyCode.Alpha9)) pressed = KeyCode.Alpha9;
    29.         else if (Input.GetKeyDown(KeyCode.Keypad0)) pressed = KeyCode.Keypad0;
    30.         else if (Input.GetKeyDown(KeyCode.Keypad1)) pressed = KeyCode.Keypad1;
    31.         else if (Input.GetKeyDown(KeyCode.Keypad2)) pressed = KeyCode.Keypad2;
    32.         else if (Input.GetKeyDown(KeyCode.Keypad3)) pressed = KeyCode.Keypad3;
    33.         else if (Input.GetKeyDown(KeyCode.Keypad4)) pressed = KeyCode.Keypad4;
    34.         else if (Input.GetKeyDown(KeyCode.Keypad5)) pressed = KeyCode.Keypad5;
    35.         else if (Input.GetKeyDown(KeyCode.Keypad6)) pressed = KeyCode.Keypad6;
    36.         else if (Input.GetKeyDown(KeyCode.Keypad7)) pressed = KeyCode.Keypad7;
    37.         else if (Input.GetKeyDown(KeyCode.Keypad8)) pressed = KeyCode.Keypad8;
    38.         else if (Input.GetKeyDown(KeyCode.Keypad9)) pressed = KeyCode.Keypad9;
    39.         else if (Input.GetKeyDown(KeyCode.KeypadDivide)) pressed = KeyCode.KeypadDivide;
    40.         else if (Input.GetKeyDown(KeyCode.KeypadPeriod)) pressed = KeyCode.KeypadPeriod;
    41.         else if (Input.GetKeyDown(KeyCode.KeypadEnter)) pressed = KeyCode.KeypadEnter;
    42.         else if (Input.GetKeyDown(KeyCode.KeypadEquals)) pressed = KeyCode.KeypadEquals;
    43.         else if (Input.GetKeyDown(KeyCode.KeypadMinus)) pressed = KeyCode.KeypadMinus;
    44.         else if (Input.GetKeyDown(KeyCode.KeypadPlus)) pressed = KeyCode.KeypadPlus;
    45.         else if (Input.GetKeyDown(KeyCode.KeypadMultiply)) pressed = KeyCode.KeypadMultiply;
    46.         else if (Input.GetKeyDown(KeyCode.Numlock)) pressed = KeyCode.Numlock;
    47.         else if (Input.GetKeyDown(KeyCode.CapsLock)) pressed = KeyCode.CapsLock;
    48.         else if (Input.GetKeyDown(KeyCode.ScrollLock)) pressed = KeyCode.ScrollLock;
    49.         else if (Input.GetKeyDown(KeyCode.Q)) pressed = KeyCode.Q;
    50.         else if (Input.GetKeyDown(KeyCode.W)) pressed = KeyCode.W;
    51.         else if (Input.GetKeyDown(KeyCode.E)) pressed = KeyCode.E;
    52.         else if (Input.GetKeyDown(KeyCode.R)) pressed = KeyCode.R;
    53.         else if (Input.GetKeyDown(KeyCode.T)) pressed = KeyCode.T;
    54.         else if (Input.GetKeyDown(KeyCode.Y)) pressed = KeyCode.Y;
    55.         else if (Input.GetKeyDown(KeyCode.U)) pressed = KeyCode.U;
    56.         else if (Input.GetKeyDown(KeyCode.I)) pressed = KeyCode.I;
    57.         else if (Input.GetKeyDown(KeyCode.O)) pressed = KeyCode.O;
    58.         else if (Input.GetKeyDown(KeyCode.P)) pressed = KeyCode.P;
    59.         else if (Input.GetKeyDown(KeyCode.LeftBracket)) pressed = KeyCode.LeftBracket;
    60.         else if (Input.GetKeyDown(KeyCode.RightBracket)) pressed = KeyCode.RightBracket;
    61.         else if (Input.GetKeyDown(KeyCode.A)) pressed = KeyCode.A;
    62.         else if (Input.GetKeyDown(KeyCode.S)) pressed = KeyCode.S;
    63.         else if (Input.GetKeyDown(KeyCode.D)) pressed = KeyCode.D;
    64.         else if (Input.GetKeyDown(KeyCode.F)) pressed = KeyCode.F;
    65.         else if (Input.GetKeyDown(KeyCode.G)) pressed = KeyCode.G;
    66.         else if (Input.GetKeyDown(KeyCode.H)) pressed = KeyCode.H;
    67.         else if (Input.GetKeyDown(KeyCode.J)) pressed = KeyCode.J;
    68.         else if (Input.GetKeyDown(KeyCode.K)) pressed = KeyCode.K;
    69.         else if (Input.GetKeyDown(KeyCode.L)) pressed = KeyCode.L;
    70.         else if (Input.GetKeyDown(KeyCode.Semicolon)) pressed = KeyCode.Semicolon;
    71.         else if (Input.GetKeyDown(KeyCode.Quote)) pressed = KeyCode.Quote;
    72.         else if (Input.GetKeyDown(KeyCode.Backslash)) pressed = KeyCode.Backslash;
    73.         else if (Input.GetKeyDown(KeyCode.Z)) pressed = KeyCode.Z;
    74.         else if (Input.GetKeyDown(KeyCode.X)) pressed = KeyCode.X;
    75.         else if (Input.GetKeyDown(KeyCode.C)) pressed = KeyCode.C;
    76.         else if (Input.GetKeyDown(KeyCode.V)) pressed = KeyCode.V;
    77.         else if (Input.GetKeyDown(KeyCode.B)) pressed = KeyCode.B;
    78.         else if (Input.GetKeyDown(KeyCode.N)) pressed = KeyCode.N;
    79.         else if (Input.GetKeyDown(KeyCode.M)) pressed = KeyCode.M;
    80.         else if (Input.GetKeyDown(KeyCode.Comma)) pressed = KeyCode.Comma;
    81.         else if (Input.GetKeyDown(KeyCode.Tab)) pressed = KeyCode.Tab;
    82.         else if (Input.GetKeyDown(KeyCode.Period)) pressed = KeyCode.Period;
    83.         else if (Input.GetKeyDown(KeyCode.Slash)) pressed = KeyCode.Slash;
    84.         else if (Input.GetKeyDown(KeyCode.LeftShift)) pressed = KeyCode.LeftShift;
    85.         else if (Input.GetKeyDown(KeyCode.RightShift)) pressed = KeyCode.RightShift;
    86.         else if (Input.GetKeyDown(KeyCode.LeftControl)) pressed = KeyCode.LeftControl;
    87.         else if (Input.GetKeyDown(KeyCode.LeftAlt)) pressed = KeyCode.LeftAlt;
    88.         else if (Input.GetKeyDown(KeyCode.RightControl)) pressed = KeyCode.RightControl;
    89.         else if (Input.GetKeyDown(KeyCode.RightAlt)) pressed = KeyCode.RightAlt;
    90.         else if (Input.GetKeyDown(KeyCode.Backspace)) pressed = KeyCode.Backspace;
    91.         else if (Input.GetKeyDown(KeyCode.Return)) pressed = KeyCode.Return;
    92.         else if (Input.GetKeyDown(KeyCode.Insert)) pressed = KeyCode.Insert;
    93.         else if (Input.GetKeyDown(KeyCode.Delete)) pressed = KeyCode.Delete;
    94.         else if (Input.GetKeyDown(KeyCode.Home)) pressed = KeyCode.Home;
    95.         else if (Input.GetKeyDown(KeyCode.PageUp)) pressed = KeyCode.PageUp;
    96.         else if (Input.GetKeyDown(KeyCode.PageDown)) pressed = KeyCode.PageDown;
    97.         else if (Input.GetKeyDown(KeyCode.Print)) pressed = KeyCode.Print;
    98.         return pressed;
    99.     }
    If C# has textmacros, one can be used here to simplify this monstrosity. I tried using #define, but that's a C++ thing, it seems.

    I hope this is helpful :p
     
    Last edited: Apr 10, 2022
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,736
    You should put a small project together with all that stuff set up properly, make a demo scene to show it off, then post it publicly on github. I do that with all of my Unity example projects.

    If you don't do that, the odds of some random reader here downloading, saving, configuring and connecting all the above parts is essentially zero.