Manipulating the Input Manager in Script

  August 23, 2014   Programming   Leslie Young

Today we will look at how to access the Unity Input Manager and change values in it via editor scripting. This is useful when you want to define new axis or button that your runtime code might need and there are too many too define manually via the Unity Input Manager. In this guide I’ll only show how axis are defined but defining buttons are done in a similar way.

The input manager’s settings are saved in the InputManager.asset file under the ProjectSettings folder of the project. This can be opened and manipulated like a SerializedObject.

In the code I’ll present I do not want to destroy any existing definition, but if you wanted to do that you could use the following lines to first clear the input manager of all input definitions. This is of course all editor side script so the script should be saved in a folder, or sub-folder of a folder, named Editor.

SerializedObject serializedObject = new SerializedObject(AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/InputManager.asset")[0]);
SerializedProperty axesProperty = serializedObject.FindProperty("m_Axes");
axesProperty.ClearArray();
serializedObject.ApplyModifiedProperties();

First we need a way to get to a child property of a SerializedProperty. This simple function will take care of that. It simply runs through all the child objects and return the one that matches the given name.

private static SerializedProperty GetChildProperty(SerializedProperty parent, string name)
{
    SerializedProperty child = parent.Copy();
    child.Next(true);
    do
    {
        if (child.name == name) return child;
    }
    while (child.Next(false));
    return null;
}

Next I will add a function that can check if a definition exist. In my tool I allow the designer to press a button to “Reset” the input manager and I do not want to define duplicate axis. You might go as far as to take the existing one and making sure it settings are correct but I simply skip it of it exists. You will note I reload the InputManager object each time. This does not really matter performance wise since this is stuff done once in the editor and is fast enough. It keeps the code simpler.

private static bool AxisDefined(string axisName)
{
    SerializedObject serializedObject = new SerializedObject(AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/InputManager.asset")[0]);
    SerializedProperty axesProperty = serializedObject.FindProperty("m_Axes");

    axesProperty.Next(true);
    axesProperty.Next(true);
    while (axesProperty.Next(false))
    {
        SerializedProperty axis = axesProperty.Copy();
        axis.Next(true);
        if (axis.stringValue == axisName) return true;
    }
    return false;
}

Now comes the function used to define an axis. Not much to it, it opens the asset file, create a space in the list of axis, apply this and then grab a reference to this new entry and change the properties of the axis, and finally applies the changes.

public enum AxisType
{
    KeyOrMouseButton = 0,
    MouseMovement = 1,
    JoystickAxis = 2
};

public class InputAxis
{
    public string name;
    public string descriptiveName;
    public string descriptiveNegativeName;
    public string negativeButton;
    public string positiveButton;
    public string altNegativeButton;
    public string altPositiveButton;

    public float gravity;
    public float dead;
    public float sensitivity;

    public bool snap = false;
    public bool invert = false;

    public AxisType type;

    public int axis;
    public int joyNum;
}

private static void AddAxis(InputAxis axis)
{
    if (AxisDefined(axis.name)) return;

    SerializedObject serializedObject = new SerializedObject(AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/InputManager.asset")[0]);
    SerializedProperty axesProperty = serializedObject.FindProperty("m_Axes");

    axesProperty.arraySize++;
    serializedObject.ApplyModifiedProperties();

    SerializedProperty axisProperty = axesProperty.GetArrayElementAtIndex(axesProperty.arraySize - 1);

    GetChildProperty(axisProperty, "m_Name").stringValue = axis.name;
    GetChildProperty(axisProperty, "descriptiveName").stringValue = axis.descriptiveName;
    GetChildProperty(axisProperty, "descriptiveNegativeName").stringValue = axis.descriptiveNegativeName;
    GetChildProperty(axisProperty, "negativeButton").stringValue = axis.negativeButton;
    GetChildProperty(axisProperty, "positiveButton").stringValue = axis.positiveButton;
    GetChildProperty(axisProperty, "altNegativeButton").stringValue = axis.altNegativeButton;
    GetChildProperty(axisProperty, "altPositiveButton").stringValue = axis.altPositiveButton;
    GetChildProperty(axisProperty, "gravity").floatValue = axis.gravity;
    GetChildProperty(axisProperty, "dead").floatValue = axis.dead;
    GetChildProperty(axisProperty, "sensitivity").floatValue = axis.sensitivity;
    GetChildProperty(axisProperty, "snap").boolValue = axis.snap;
    GetChildProperty(axisProperty, "invert").boolValue = axis.invert;
    GetChildProperty(axisProperty, "type").intValue = (int)axis.type;
    GetChildProperty(axisProperty, "axis").intValue = axis.axis - 1;
    GetChildProperty(axisProperty, "joyNum").intValue = axis.joyNum;

    serializedObject.ApplyModifiedProperties();
}

How you use these functions will depend on your needs. In plyGame I needed to define axis for the mouse and for one gamepad, so I run through a loop to define all the different axis for the gamepad. The mouse and gamepad buttons are read through Unity’s Input class so I did not need to define those here. You will see I’ve left a commented out loop for defining more than one gamepad. I’ll also only define up to 10 axis.

public static void SetupInputManager()
{
    // Add mouse definitions
    AddAxis(new InputAxis() { name = "myMouseX",        sensitivity = 1f, type = AxisType.MouseMovement, axis = 1 });
    AddAxis(new InputAxis() { name = "myMouseY",        sensitivity = 1f, type = AxisType.MouseMovement, axis = 2 });
    AddAxis(new InputAxis() { name = "myScrollWheel", sensitivity = 1f, type = AxisType.MouseMovement, axis = 3 });

    // Add gamepad definitions
    int i = 1;
    //for (int i = 1; i <= (int)InputBind.Gamepad.Gamepad4; i++)
    //{
        for (int j = 0; j <= (int)InputBind.GamepadAxis.Axis10; j++)
        {
            AddAxis(new InputAxis() 
            { 
                name = "myPad" + i + "A" + (j + 1).ToString(), 
                dead = 0.2f,
                sensitivity = 1f,
                type = AxisType.JoystickAxis,
                axis = (j + 1),
                joyNum = i,
            });
        }
    //}
}

and that is pretty much it.


Discussion


     Follow