private static void WriteActionEventInitializer(string setName, string actionName, InputActionPhase phase, Writer writer, bool removeCallback = false)
        {
            var actionNameCased = actionName;

            if (char.IsLower(actionNameCased[0]))
            {
                actionNameCased = char.ToUpper(actionNameCased[0]) + actionNameCased.Substring(1);
            }

            string callbackName;

            switch (phase)
            {
            case InputActionPhase.Started: callbackName = "started"; break;

            case InputActionPhase.Performed: callbackName = "performed"; break;

            case InputActionPhase.Cancelled: callbackName = "cancelled"; break;

            default:
                throw new Exception("Internal error: No known callback for " + phase);
            }

            writer.WriteLine($"if (m_{setName}{actionNameCased}Action{phase} != null)");
            ++writer.indentLevel;
            writer.WriteLine($"m_{setName}_{CSharpCodeHelpers.MakeIdentifier(actionName)}.{callbackName} {(removeCallback ? "-" : "+")}= m_{setName}{actionNameCased}Action{phase}.Invoke;");
            --writer.indentLevel;
        }
 public static string GenerateWrapperCode(InputActionAsset asset, Options options = new Options())
 {
     if (string.IsNullOrEmpty(options.sourceAssetPath))
     {
         options.sourceAssetPath = AssetDatabase.GetAssetPath(asset);
     }
     if (string.IsNullOrEmpty(options.className) && !string.IsNullOrEmpty(asset.name))
     {
         options.className =
             CSharpCodeHelpers.MakeTypeName(asset.name);
     }
     return(GenerateWrapperCode(asset.actionMaps, options));
 }
        public override void OnInspectorGUI()
        {
            // 'assetEditor' is set only after the editor is enabled so do the
            // initialization here.
            if (!m_Initialized)
            {
                // Read current asset as backup.
                if (m_Backup == null)
                {
                    m_Backup = GetAsset().ToJson();
                }

                m_Initialized = true;
            }

            if (GUILayout.Button("Edit asset"))
            {
                ActionInspectorWindow.OnOpenAsset(GetAsset().GetInstanceID(), 0);
            }

            EditorGUILayout.Space();

            // Look up properties on importer object.
            var generateWapperCodeProperty = serializedObject.FindProperty("m_GenerateWrapperCode");

            // Add settings UI.
            EditorGUILayout.PropertyField(generateWapperCodeProperty, Contents.generateWrapperCode);
            if (generateWapperCodeProperty.boolValue)
            {
                var wrapperCodePathProperty      = serializedObject.FindProperty("m_WrapperCodePath");
                var wrapperClassNameProperty     = serializedObject.FindProperty("m_WrapperClassName");
                var wrapperCodeNamespaceProperty = serializedObject.FindProperty("m_WrapperCodeNamespace");

                ////TODO: tie a file selector to this
                EditorGUILayout.PropertyField(wrapperCodePathProperty);

                EditorGUILayout.PropertyField(wrapperClassNameProperty);
                if (!CSharpCodeHelpers.IsEmptyOrProperIdentifier(wrapperClassNameProperty.stringValue))
                {
                    EditorGUILayout.HelpBox("Must be a valid C# identifier", MessageType.Error);
                }

                EditorGUILayout.PropertyField(wrapperCodeNamespaceProperty);
                if (!CSharpCodeHelpers.IsEmptyOrProperNamespaceName(wrapperCodeNamespaceProperty.stringValue))
                {
                    EditorGUILayout.HelpBox("Must be a valid C# namespace name", MessageType.Error);
                }
            }

            ApplyRevertGUI();
        }
        private static void ConvertInputActionToVDF(InputAction action, StringBuilder builder, Dictionary <string, string> localizationStrings)
        {
            builder.Append("\t\t\t\t\"");
            builder.Append(action.name);

            var mapIdentifier    = CSharpCodeHelpers.MakeIdentifier(action.actionMap.name);
            var actionIdentifier = CSharpCodeHelpers.MakeIdentifier(action.name);
            var titleId          = "Action_" + mapIdentifier + "_" + actionIdentifier;

            localizationStrings[titleId] = action.name;

            // StickPadGyros are objects. Everything else is just strings.
            var inputType = GetSteamControllerInputType(action);

            if (inputType == "StickPadGyro")
            {
                builder.Append("\"\n");
                builder.Append("\t\t\t\t{\n");

                // Title.
                builder.Append("\t\t\t\t\t\"title\"\t\"#");
                builder.Append(titleId);
                builder.Append("\"\n");

                // Decide on "input_mode". Assume "absolute_mouse" by default and take
                // anything built on StickControl as "joystick_move".
                var inputMode   = "absolute_mouse";
                var controlType = EditorInputControlLayoutCache.TryGetLayout(action.expectedControlType).type;
                if (typeof(StickControl).IsAssignableFrom(controlType))
                {
                    inputMode = "joystick_move";
                }
                builder.Append("\t\t\t\t\t\"input_mode\"\t\"");
                builder.Append(inputMode);
                builder.Append("\"\n");

                builder.Append("\t\t\t\t}\n");
            }
            else
            {
                builder.Append("\"\t\"");
                builder.Append(titleId);
                builder.Append("\"\n");
            }
        }
Exemplo n.º 5
0
        public override void OnInspectorGUI()
        {
            // Button to pop up window to edit the asset.
            if (GUILayout.Button("Edit asset"))
            {
                AssetInspectorWindow.OnOpenAsset(GetAsset().GetInstanceID(), 0);
            }

            EditorGUILayout.Space();

            // Importer settings UI.
            var generateWapperCodeProperty = serializedObject.FindProperty("m_GenerateWrapperCode");

            EditorGUILayout.PropertyField(generateWapperCodeProperty, m_GenerateWrapperCodeLabel);
            if (generateWapperCodeProperty.boolValue)
            {
                var wrapperCodePathProperty      = serializedObject.FindProperty("m_WrapperCodePath");
                var wrapperClassNameProperty     = serializedObject.FindProperty("m_WrapperClassName");
                var wrapperCodeNamespaceProperty = serializedObject.FindProperty("m_WrapperCodeNamespace");
                var generateActionEventsProperty = serializedObject.FindProperty("m_GenerateActionEvents");
                var generateInterfacesProperty   = serializedObject.FindProperty("m_GenerateInterfaces");

                ////TODO: tie a file selector to this
                EditorGUILayout.PropertyField(wrapperCodePathProperty, m_WrapperCodePathLabel);

                EditorGUILayout.PropertyField(wrapperClassNameProperty, m_WrapperClassNameLabel);
                if (!CSharpCodeHelpers.IsEmptyOrProperIdentifier(wrapperClassNameProperty.stringValue))
                {
                    EditorGUILayout.HelpBox("Must be a valid C# identifier", MessageType.Error);
                }

                EditorGUILayout.PropertyField(wrapperCodeNamespaceProperty, m_WrapperCodeNamespaceLabel);
                if (!CSharpCodeHelpers.IsEmptyOrProperNamespaceName(wrapperCodeNamespaceProperty.stringValue))
                {
                    EditorGUILayout.HelpBox("Must be a valid C# namespace name", MessageType.Error);
                }

                EditorGUILayout.PropertyField(generateActionEventsProperty, m_GenerateActionEventsLabel);
                EditorGUILayout.PropertyField(generateInterfacesProperty);
            }

            ApplyRevertGUI();
        }
        public override void OnInspectorGUI()
        {
            if (GUILayout.Button("Edit asset"))
            {
                ActionInspectorWindow.OnOpenAsset(GetAsset().GetInstanceID(), 0);
            }

            EditorGUILayout.Space();

            // Look up properties on importer object.
            var generateWapperCodeProperty = serializedObject.FindProperty("m_GenerateWrapperCode");

            // Add settings UI.
            EditorGUILayout.PropertyField(generateWapperCodeProperty, Contents.generateWrapperCode);
            if (generateWapperCodeProperty.boolValue)
            {
                var wrapperCodePathProperty      = serializedObject.FindProperty("m_WrapperCodePath");
                var wrapperClassNameProperty     = serializedObject.FindProperty("m_WrapperClassName");
                var wrapperCodeNamespaceProperty = serializedObject.FindProperty("m_WrapperCodeNamespace");

                ////TODO: tie a file selector to this
                EditorGUILayout.PropertyField(wrapperCodePathProperty);

                EditorGUILayout.PropertyField(wrapperClassNameProperty);
                if (!CSharpCodeHelpers.IsEmptyOrProperIdentifier(wrapperClassNameProperty.stringValue))
                {
                    EditorGUILayout.HelpBox("Must be a valid C# identifier", MessageType.Error);
                }

                EditorGUILayout.PropertyField(wrapperCodeNamespaceProperty);
                if (!CSharpCodeHelpers.IsEmptyOrProperNamespaceName(wrapperCodeNamespaceProperty.stringValue))
                {
                    EditorGUILayout.HelpBox("Must be a valid C# namespace name", MessageType.Error);
                }
            }

            ApplyRevertGUI();
        }
Exemplo n.º 7
0
        public override void OnInspectorGUI()
        {
            // Button to pop up window to edit the asset.
            if (GUILayout.Button("Edit asset"))
            {
                AssetInspectorWindow.OnOpenAsset(GetAsset().GetInstanceID(), 0);
            }

            EditorGUILayout.Space();

            // Importer settings UI.
            var generateWapperCodeProperty = serializedObject.FindProperty("m_GenerateWrapperCode");

            EditorGUILayout.PropertyField(generateWapperCodeProperty, m_GenerateWrapperCodeLabel);
            if (generateWapperCodeProperty.boolValue)
            {
                var wrapperCodePathProperty      = serializedObject.FindProperty("m_WrapperCodePath");
                var wrapperClassNameProperty     = serializedObject.FindProperty("m_WrapperClassName");
                var wrapperCodeNamespaceProperty = serializedObject.FindProperty("m_WrapperCodeNamespace");
                var generateActionEventsProperty = serializedObject.FindProperty("m_GenerateActionEvents");
                var generateInterfacesProperty   = serializedObject.FindProperty("m_GenerateInterfaces");

                EditorGUILayout.BeginHorizontal();
                EditorGUILayout.PropertyField(wrapperCodePathProperty, m_WrapperCodePathLabel);
                if (GUILayout.Button("...", EditorStyles.miniButton, GUILayout.MaxWidth(20)))
                {
                    var assetPath       = AssetDatabase.GetAssetPath(GetAsset());
                    var defaultFileName = Path.ChangeExtension(assetPath, ".cs");
                    var fileName        = EditorUtility.SaveFilePanel("Location for generated C# file",
                                                                      Path.GetDirectoryName(defaultFileName),
                                                                      Path.GetFileName(defaultFileName), "cs");
                    if (!string.IsNullOrEmpty(fileName))
                    {
                        if (fileName.StartsWith(Application.dataPath))
                        {
                            fileName = "Assets/" + fileName.Substring(Application.dataPath.Length + 1);
                        }

                        wrapperCodePathProperty.stringValue = fileName;
                    }
                }
                EditorGUILayout.EndHorizontal();

                EditorGUILayout.PropertyField(wrapperClassNameProperty, m_WrapperClassNameLabel);
                if (!CSharpCodeHelpers.IsEmptyOrProperIdentifier(wrapperClassNameProperty.stringValue))
                {
                    EditorGUILayout.HelpBox("Must be a valid C# identifier", MessageType.Error);
                }

                EditorGUILayout.PropertyField(wrapperCodeNamespaceProperty, m_WrapperCodeNamespaceLabel);
                if (!CSharpCodeHelpers.IsEmptyOrProperNamespaceName(wrapperCodeNamespaceProperty.stringValue))
                {
                    EditorGUILayout.HelpBox("Must be a valid C# namespace name", MessageType.Error);
                }

                EditorGUILayout.PropertyField(generateActionEventsProperty, m_GenerateActionEventsLabel);
                EditorGUILayout.PropertyField(generateInterfacesProperty);
            }

            ApplyRevertGUI();
        }
        public static string GenerateInputDeviceFromSteamIGA(string vdf, string namespaceAndClassName)
        {
            if (string.IsNullOrEmpty(vdf))
            {
                throw new ArgumentNullException("vdf");
            }
            if (string.IsNullOrEmpty(namespaceAndClassName))
            {
                throw new ArgumentNullException("namespaceAndClassName");
            }

            // Parse VDF.
            var parsedVdf = ParseVDF(vdf);
            var actions   = (Dictionary <string, object>)((Dictionary <string, object>)parsedVdf["In Game Actions"])["actions"];

            // Determine class and namespace name.
            var namespaceName  = "";
            var className      = "";
            var indexOfLastDot = namespaceAndClassName.LastIndexOf('.');

            if (indexOfLastDot != -1)
            {
                namespaceName = namespaceAndClassName.Substring(0, indexOfLastDot);
                className     = namespaceAndClassName.Substring(indexOfLastDot + 1);
            }
            else
            {
                className = namespaceAndClassName;
            }
            var stateStructName = className + "State";

            var builder = new StringBuilder();

            builder.Append("// THIS FILE HAS BEEN AUTO-GENERATED\n");
            builder.Append("#if (UNITY_EDITOR || UNITY_STANDALONE) && UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT\n");
            builder.Append("using UnityEngine;\n");
            builder.Append("using UnityEngine.InputSystem;\n");
            builder.Append("using UnityEngine.InputSystem.Controls;\n");
            builder.Append("using UnityEngine.InputSystem.Layouts;\n");
            builder.Append("using UnityEngine.InputSystem.Utilities;\n");
            builder.Append("using UnityEngine.InputSystem.Steam;\n");
            builder.Append("#if UNITY_EDITOR\n");
            builder.Append("using UnityEditor;\n");
            builder.Append("#endif\n");
            builder.Append("\n");
            if (!string.IsNullOrEmpty(namespaceName))
            {
                builder.Append("namespace ");
                builder.Append(namespaceName);
                builder.Append("\n{\n");
            }

            // InitializeOnLoad attribute.
            builder.Append("#if UNITY_EDITOR\n");
            builder.Append("[InitializeOnLoad]\n");
            builder.Append("#endif\n");

            // Control layout attribute.
            builder.Append("[InputControlLayout(stateType = typeof(");
            builder.Append(stateStructName);
            builder.Append("))]\n");

            // Class declaration.
            builder.Append("public class ");
            builder.Append(className);
            builder.Append(" : SteamController\n");
            builder.Append("{\n");

            // Device matcher.
            builder.Append("    private static InputDeviceMatcher deviceMatcher\n");
            builder.Append("    {\n");
            builder.Append("        get { return new InputDeviceMatcher().WithInterface(\"Steam\").WithProduct(\"");
            builder.Append(className);
            builder.Append("\"); }\n");
            builder.Append("    }\n");

            // Static constructor.
            builder.Append('\n');
            builder.Append("#if UNITY_EDITOR\n");
            builder.Append("    static ");
            builder.Append(className);
            builder.Append("()\n");
            builder.Append("    {\n");
            builder.Append("        InputSystem.RegisterLayout<");
            builder.Append(className);
            builder.Append(">(matches: deviceMatcher);\n");
            builder.Append("    }\n");
            builder.Append("#endif\n");

            // RuntimeInitializeOnLoadMethod.
            // NOTE: Not relying on static ctor here. See il2cpp bug 1014293.
            builder.Append('\n');
            builder.Append("    [RuntimeInitializeOnLoadMethod(loadType: RuntimeInitializeLoadType.BeforeSceneLoad)]\n");
            builder.Append("    private static void RuntimeInitializeOnLoad()\n");
            builder.Append("    {\n");
            builder.Append("        InputSystem.RegisterLayout<");
            builder.Append(className);
            builder.Append(">(matches: deviceMatcher);\n");
            builder.Append("    }\n");

            // Control properties.
            builder.Append('\n');
            foreach (var setEntry in actions)
            {
                var setEntryProperties = (Dictionary <string, object>)setEntry.Value;

                // StickPadGyros.
                var stickPadGyros = (Dictionary <string, object>)setEntryProperties["StickPadGyro"];
                foreach (var entry in stickPadGyros)
                {
                    var entryProperties = (Dictionary <string, object>)entry.Value;
                    var isStick         = entryProperties.ContainsKey("input_mode") && (string)entryProperties["input_mode"] == "joystick_move";
                    builder.Append(string.Format("    public {0} {1} {{ get; protected set; }}\n", isStick ? "StickControl" : "Vector2Control",
                                                 CSharpCodeHelpers.MakeIdentifier(entry.Key)));
                }

                // Buttons.
                var buttons = (Dictionary <string, object>)setEntryProperties["Button"];
                foreach (var entry in buttons)
                {
                    builder.Append(string.Format("    public ButtonControl {0} {{ get; protected set; }}\n",
                                                 CSharpCodeHelpers.MakeIdentifier(entry.Key)));
                }

                // AnalogTriggers.
                var analogTriggers = (Dictionary <string, object>)setEntryProperties["AnalogTrigger"];
                foreach (var entry in analogTriggers)
                {
                    builder.Append(string.Format("    public AxisControl {0} {{ get; protected set; }}\n",
                                                 CSharpCodeHelpers.MakeIdentifier(entry.Key)));
                }
            }

            // FinishSetup method.
            builder.Append('\n');
            builder.Append("    protected override void FinishSetup(InputDeviceBuilder builder)\n");
            builder.Append("    {\n");
            builder.Append("        base.FinishSetup(builder);\n");
            foreach (var setEntry in actions)
            {
                var setEntryProperties = (Dictionary <string, object>)setEntry.Value;

                // StickPadGyros.
                var stickPadGyros = (Dictionary <string, object>)setEntryProperties["StickPadGyro"];
                foreach (var entry in stickPadGyros)
                {
                    var entryProperties = (Dictionary <string, object>)entry.Value;
                    var isStick         = entryProperties.ContainsKey("input_mode") && (string)entryProperties["input_mode"] == "joystick_move";
                    builder.Append(string.Format("        {0} = builder.GetControl<{1}>(\"{2}\");\n",
                                                 CSharpCodeHelpers.MakeIdentifier(entry.Key),
                                                 isStick ? "StickControl" : "Vector2Control",
                                                 entry.Key));
                }

                // Buttons.
                var buttons = (Dictionary <string, object>)setEntryProperties["Button"];
                foreach (var entry in buttons)
                {
                    builder.Append(string.Format("        {0} = builder.GetControl<ButtonControl>(\"{1}\");\n",
                                                 CSharpCodeHelpers.MakeIdentifier(entry.Key),
                                                 entry.Key));
                }

                // AnalogTriggers.
                var analogTriggers = (Dictionary <string, object>)setEntryProperties["AnalogTrigger"];
                foreach (var entry in analogTriggers)
                {
                    builder.Append(string.Format("        {0} = builder.GetControl<AxisControl>(\"{1}\");\n",
                                                 CSharpCodeHelpers.MakeIdentifier(entry.Key),
                                                 entry.Key));
                }
            }
            builder.Append("    }\n");

            // ResolveSteamActions method.
            builder.Append('\n');
            builder.Append("    protected override void ResolveSteamActions(ISteamControllerAPI api)\n");
            builder.Append("    {\n");
            foreach (var setEntry in actions)
            {
                var setEntryProperties = (Dictionary <string, object>)setEntry.Value;

                // Set handle.
                builder.Append(string.Format("        {0}SetHandle = api.GetActionSetHandle(\"{1}\");\n",
                                             CSharpCodeHelpers.MakeIdentifier(setEntry.Key),
                                             setEntry.Key));

                // StickPadGyros.
                var stickPadGyros = (Dictionary <string, object>)setEntryProperties["StickPadGyro"];
                foreach (var entry in stickPadGyros)
                {
                    builder.Append(string.Format("        {0}Handle = api.GetAnalogActionHandle(\"{1}\");\n",
                                                 CSharpCodeHelpers.MakeIdentifier(entry.Key),
                                                 entry.Key));
                }

                // Buttons.
                var buttons = (Dictionary <string, object>)setEntryProperties["Button"];
                foreach (var entry in buttons)
                {
                    builder.Append(string.Format("        {0}Handle = api.GetDigitalActionHandle(\"{1}\");\n",
                                                 CSharpCodeHelpers.MakeIdentifier(entry.Key),
                                                 entry.Key));
                }

                // AnalogTriggers.
                var analogTriggers = (Dictionary <string, object>)setEntryProperties["AnalogTrigger"];
                foreach (var entry in analogTriggers)
                {
                    builder.Append(string.Format("        {0}Handle = api.GetAnalogActionHandle(\"{1}\");\n",
                                                 CSharpCodeHelpers.MakeIdentifier(entry.Key),
                                                 entry.Key));
                }
            }
            builder.Append("    }\n");

            // Handle cache fields.
            builder.Append('\n');
            foreach (var setEntry in actions)
            {
                var setEntryProperties = (Dictionary <string, object>)setEntry.Value;

                // Set handle.
                builder.Append(string.Format("    public SteamHandle<InputActionMap> {0}SetHandle {{ get; private set; }}\n",
                                             CSharpCodeHelpers.MakeIdentifier(setEntry.Key)));

                // StickPadGyros.
                var stickPadGyros = (Dictionary <string, object>)setEntryProperties["StickPadGyro"];
                foreach (var entry in stickPadGyros)
                {
                    builder.Append(string.Format("    public SteamHandle<InputAction> {0}Handle {{ get; private set; }}\n",
                                                 CSharpCodeHelpers.MakeIdentifier(entry.Key)));
                }

                // Buttons.
                var buttons = (Dictionary <string, object>)setEntryProperties["Button"];
                foreach (var entry in buttons)
                {
                    builder.Append(string.Format("    public SteamHandle<InputAction> {0}Handle {{ get; private set; }}\n",
                                                 CSharpCodeHelpers.MakeIdentifier(entry.Key)));
                }

                // AnalogTriggers.
                var analogTriggers = (Dictionary <string, object>)setEntryProperties["AnalogTrigger"];
                foreach (var entry in analogTriggers)
                {
                    builder.Append(string.Format("    public SteamHandle<InputAction> {0}Handle {{ get; private set; }}\n",
                                                 CSharpCodeHelpers.MakeIdentifier(entry.Key)));
                }
            }

            // steamActionSets property.
            builder.Append('\n');
            builder.Append("    private SteamActionSetInfo[] m_ActionSets;\n");
            builder.Append("    public override ReadOnlyArray<SteamActionSetInfo> steamActionSets\n");
            builder.Append("    {\n");
            builder.Append("        get\n");
            builder.Append("        {\n");
            builder.Append("            if (m_ActionSets == null)\n");
            builder.Append("                m_ActionSets = new[]\n");
            builder.Append("                {\n");
            foreach (var setEntry in actions)
            {
                builder.Append(string.Format(
                                   "                    new SteamActionSetInfo {{ name = \"{0}\", handle = {1}SetHandle }},\n",
                                   setEntry.Key,
                                   CSharpCodeHelpers.MakeIdentifier(setEntry.Key)));
            }
            builder.Append("                };\n");
            builder.Append("            return new ReadOnlyArray<SteamActionSetInfo>(m_ActionSets);\n");
            builder.Append("        }\n");
            builder.Append("    }\n");

            // Update method.
            builder.Append('\n');
            builder.Append("    protected override unsafe void Update(ISteamControllerAPI api)\n");
            builder.Append("    {\n");
            builder.Append(string.Format("        {0} state;\n", stateStructName));
            var currentButtonBit = 0;

            foreach (var setEntry in actions)
            {
                var setEntryProperties = (Dictionary <string, object>)setEntry.Value;

                // StickPadGyros.
                var stickPadGyros = (Dictionary <string, object>)setEntryProperties["StickPadGyro"];
                foreach (var entry in stickPadGyros)
                {
                    builder.Append(string.Format("        state.{0} = api.GetAnalogActionData(steamControllerHandle, {0}Handle).position;\n",
                                                 CSharpCodeHelpers.MakeIdentifier(entry.Key)));
                }

                // Buttons.
                var buttons = (Dictionary <string, object>)setEntryProperties["Button"];
                foreach (var entry in buttons)
                {
                    builder.Append(string.Format("        if (api.GetDigitalActionData(steamControllerHandle, {0}Handle).pressed)\n",
                                                 CSharpCodeHelpers.MakeIdentifier(entry.Key)));
                    builder.Append(string.Format("            state.buttons[{0}] |= {1};\n", currentButtonBit / 8, currentButtonBit % 8));
                    ++currentButtonBit;
                }

                // AnalogTriggers.
                var analogTriggers = (Dictionary <string, object>)setEntryProperties["AnalogTrigger"];
                foreach (var entry in analogTriggers)
                {
                    builder.Append(string.Format("        state.{0} = api.GetAnalogActionData(steamControllerHandle, {0}Handle).position.x;\n",
                                                 CSharpCodeHelpers.MakeIdentifier(entry.Key)));
                }
            }
            builder.Append("        InputSystem.QueueStateEvent(this, state);\n");
            builder.Append("    }\n");

            builder.Append("}\n");

            if (!string.IsNullOrEmpty(namespaceName))
            {
                builder.Append("}\n");
            }

            // State struct.
            builder.Append("public unsafe struct ");
            builder.Append(stateStructName);
            builder.Append(" : IInputStateTypeInfo\n");
            builder.Append("{\n");
            builder.Append("    public FourCC format\n");
            builder.Append("    {\n");
            builder.Append("        get {\n");
            ////TODO: handle class names that are shorter than 4 characters
            ////TODO: uppercase characters
            builder.Append(string.Format("            return new FourCC('{0}', '{1}', '{2}', '{3}');\n", className[0], className[1], className[2], className[3]));
            builder.Append("        }\n");
            builder.Append("    }\n");
            builder.Append("\n");
            var totalButtonCount = 0;

            foreach (var setEntry in actions)
            {
                var setEntryProperties = (Dictionary <string, object>)setEntry.Value;

                // Buttons.
                var buttons     = (Dictionary <string, object>)setEntryProperties["Button"];
                var buttonCount = buttons.Count;
                if (buttonCount > 0)
                {
                    foreach (var entry in buttons)
                    {
                        builder.Append(string.Format(
                                           "    [InputControl(name = \"{0}\", layout = \"Button\", bit = {1})]\n", entry.Key, totalButtonCount));
                        ++totalButtonCount;
                    }
                }
            }
            if (totalButtonCount > 0)
            {
                var byteCount = (totalButtonCount + 7) / 8;
                builder.Append("    public fixed byte buttons[");
                builder.Append(byteCount.ToString());
                builder.Append("];\n");
            }
            foreach (var setEntry in actions)
            {
                var setEntryProperties = (Dictionary <string, object>)setEntry.Value;

                // StickPadGyros.
                var stickPadGyros = (Dictionary <string, object>)setEntryProperties["StickPadGyro"];
                foreach (var entry in stickPadGyros)
                {
                    var entryProperties = (Dictionary <string, object>)entry.Value;
                    var isStick         = entryProperties.ContainsKey("input_mode") && (string)entryProperties["input_mode"] == "joystick_move";

                    builder.Append(string.Format("    [InputControl(name = \"{0}\", layout = \"{1}\")]\n", entry.Key, isStick ? "Stick" : "Vector2"));
                    builder.Append(string.Format("    public Vector2 {0};\n", CSharpCodeHelpers.MakeIdentifier(entry.Key)));
                }

                // AnalogTriggers.
                var analogTriggers = (Dictionary <string, object>)setEntryProperties["AnalogTrigger"];
                foreach (var entry in analogTriggers)
                {
                    builder.Append(string.Format("    [InputControl(name = \"{0}\", layout = \"Axis\")]\n", entry.Key));
                    builder.Append(string.Format("    public float {0};\n", CSharpCodeHelpers.MakeIdentifier(entry.Key)));
                }
            }
            builder.Append("}\n");

            builder.Append("#endif\n");

            return(builder.ToString());
        }
        public static string ConvertInputActionsToSteamIGA(IEnumerable <InputActionMap> actionMaps, string locale = "english")
        {
            if (actionMaps == null)
            {
                throw new ArgumentNullException("actionMaps");
            }

            var localizationStrings = new Dictionary <string, string>();

            var builder = new StringBuilder();

            builder.Append("\"In Game Actions\"\n");
            builder.Append("{\n");

            // Add actions.
            builder.Append("\t\"actions\"\n");
            builder.Append("\t{\n");

            // Add each action map.
            foreach (var actionMap in actionMaps)
            {
                var actionMapName       = actionMap.name;
                var actionMapIdentifier = CSharpCodeHelpers.MakeIdentifier(actionMapName);

                builder.Append("\t\t\"");
                builder.Append(actionMapName);
                builder.Append("\"\n");
                builder.Append("\t\t{\n");

                // Title.
                builder.Append("\t\t\t\"title\"\t\"#Set_");
                builder.Append(actionMapIdentifier);
                builder.Append("\"\n");
                localizationStrings["Set_" + actionMapIdentifier] = actionMapName;

                // StickPadGyro actions.
                builder.Append("\t\t\t\"StickPadGyro\"\n");
                builder.Append("\t\t\t{\n");
                foreach (var action in actionMap.actions.Where(x => GetSteamControllerInputType(x) == "StickPadGyro"))
                {
                    ConvertInputActionToVDF(action, builder, localizationStrings);
                }
                builder.Append("\t\t\t}\n");

                // AnalogTrigger actions.
                builder.Append("\t\t\t\"AnalogTrigger\"\n");
                builder.Append("\t\t\t{\n");
                foreach (var action in actionMap.actions.Where(x => GetSteamControllerInputType(x) == "AnalogTrigger"))
                {
                    ConvertInputActionToVDF(action, builder, localizationStrings);
                }
                builder.Append("\t\t\t}\n");

                // Button actions.
                builder.Append("\t\t\t\"Button\"\n");
                builder.Append("\t\t\t{\n");
                foreach (var action in actionMap.actions.Where(x => GetSteamControllerInputType(x) == "Button"))
                {
                    ConvertInputActionToVDF(action, builder, localizationStrings);
                }
                builder.Append("\t\t\t}\n");

                builder.Append("\t\t}\n");
            }

            builder.Append("\t}\n");

            // Add localizations.
            builder.Append("\t\"localization\"\n");
            builder.Append("\t{\n");
            builder.Append("\t\t\"");
            builder.Append(locale);
            builder.Append("\"\n");
            builder.Append("\t\t{\n");
            foreach (var entry in localizationStrings)
            {
                builder.Append("\t\t\t\"");
                builder.Append(entry.Key);
                builder.Append("\"\t\"");
                builder.Append(entry.Value);
                builder.Append("\"\n");
            }
            builder.Append("\t\t}\n");
            builder.Append("\t}\n");

            builder.Append("}\n");

            return(builder.ToString());
        }
        // Generate a string containing C# code that simplifies working with the given
        // action sets in code.
        public static string GenerateWrapperCode(IEnumerable <InputActionMap> sets, Options options)
        {
            if (string.IsNullOrEmpty(options.sourceAssetPath))
            {
                throw new ArgumentException("options.sourceAssetPath");
            }

            if (string.IsNullOrEmpty(options.className))
            {
                options.className =
                    CSharpCodeHelpers.MakeTypeName(Path.GetFileNameWithoutExtension(options.sourceAssetPath));
            }

            var writer = new Writer
            {
                buffer = new StringBuilder()
            };

            // Header.
            writer.WriteLine(string.Format("// GENERATED AUTOMATICALLY FROM '{0}'\n", options.sourceAssetPath));

            // Begin namespace.
            var haveNamespace = !string.IsNullOrEmpty(options.namespaceName);

            if (haveNamespace)
            {
                writer.WriteLine(string.Format("namespace {0}", options.namespaceName));
                writer.BeginBlock();
            }

            // Begin class.
            writer.WriteLine("[System.Serializable]");
            writer.WriteLine(string.Format("public class {0} : UnityEngine.Experimental.Input.InputActionWrapper", options.className));
            writer.BeginBlock();

            // Initialize method.
            writer.WriteLine("private bool m_Initialized;");
            writer.WriteLine("private void Initialize()");
            writer.BeginBlock();
            foreach (var set in sets)
            {
                var setName = CSharpCodeHelpers.MakeIdentifier(set.name);
                writer.WriteLine(string.Format("// {0}", set.name));
                writer.WriteLine(string.Format("m_{0} = asset.GetActionMap(\"{1}\");", setName, set.name));
                foreach (var action in set.actions)
                {
                    writer.WriteLine(string.Format("m_{0}_{1} = m_{2}.GetAction(\"{3}\");", setName, CSharpCodeHelpers.MakeIdentifier(action.name),
                                                   setName, action.name));
                }
            }
            writer.WriteLine("m_Initialized = true;");
            writer.EndBlock();

            // Action set accessors.
            foreach (var set in sets)
            {
                writer.WriteLine(string.Format("// {0}", set.name));

                var setName       = CSharpCodeHelpers.MakeIdentifier(set.name);
                var setStructName = CSharpCodeHelpers.MakeTypeName(setName, "Actions");

                // Caching field for action set.
                writer.WriteLine(string.Format("private UnityEngine.Experimental.Input.InputActionMap m_{0};", setName));

                // Caching fields for all actions.
                foreach (var action in set.actions)
                {
                    writer.WriteLine(string.Format("private UnityEngine.Experimental.Input.InputAction m_{0}_{1};", setName, CSharpCodeHelpers.MakeIdentifier(action.name)));
                }

                // Struct wrapping access to action set.
                writer.WriteLine(string.Format("public struct {0}", setStructName));
                writer.BeginBlock();

                // Constructor.
                writer.WriteLine(string.Format("private {0} m_Wrapper;", options.className));
                writer.WriteLine(string.Format("public {0}({1} wrapper) {{ m_Wrapper = wrapper; }}", setStructName,
                                               options.className));

                // Getter for each action.
                foreach (var action in set.actions)
                {
                    var actionName = CSharpCodeHelpers.MakeIdentifier(action.name);
                    writer.WriteLine(string.Format(
                                         "public UnityEngine.Experimental.Input.InputAction @{0} {{ get {{ return m_Wrapper.m_{1}_{2}; }} }}", actionName,
                                         setName, actionName));
                }

                // Action set getter.
                writer.WriteLine(string.Format("public UnityEngine.Experimental.Input.InputActionMap Get() {{ return m_Wrapper.m_{0}; }}",
                                               setName));

                // Enable/disable methods.
                writer.WriteLine("public void Enable() { Get().Enable(); }");
                writer.WriteLine("public void Disable() { Get().Disable(); }");

                // Clone method.
                writer.WriteLine("public UnityEngine.Experimental.Input.InputActionMap Clone() { return Get().Clone(); }");

                // Implicit conversion operator.
                writer.WriteLine(string.Format(
                                     "public static implicit operator UnityEngine.Experimental.Input.InputActionMap({0} set) {{ return set.Get(); }}",
                                     setStructName));

                writer.EndBlock();

                // Getter for instance of struct.
                writer.WriteLine(string.Format("public {0} @{1}", setStructName, setName));
                writer.BeginBlock();

                writer.WriteLine("get");
                writer.BeginBlock();
                writer.WriteLine("if (!m_Initialized) Initialize();");
                writer.WriteLine(string.Format("return new {0}(this);", setStructName));
                writer.EndBlock();

                writer.EndBlock();
            }

            // End class.
            writer.EndBlock();

            // End namespace.
            if (haveNamespace)
            {
                writer.EndBlock();
            }

            return(writer.buffer.ToString());
        }
        // Generate a string containing C# code that simplifies working with the given
        // action sets in code.
        public static string GenerateWrapperCode(IEnumerable <InputActionMap> maps, IEnumerable <InputControlScheme> schemes, Options options)
        {
            if (string.IsNullOrEmpty(options.sourceAssetPath))
            {
                throw new ArgumentException("options.sourceAssetPath");
            }

            if (string.IsNullOrEmpty(options.className))
            {
                options.className =
                    CSharpCodeHelpers.MakeTypeName(Path.GetFileNameWithoutExtension(options.sourceAssetPath));
            }

            var writer = new Writer
            {
                buffer = new StringBuilder()
            };

            // Header.
            writer.WriteLine(string.Format("// GENERATED AUTOMATICALLY FROM '{0}'\n", options.sourceAssetPath));

            // Usings.
            writer.WriteLine("using System;");
            writer.WriteLine("using UnityEngine;");
            if (options.generateEvents)
            {
                writer.WriteLine("using UnityEngine.Events;");
            }
            writer.WriteLine("using UnityEngine.Experimental.Input;");
            writer.WriteLine("\n");

            // Begin namespace.
            var haveNamespace = !string.IsNullOrEmpty(options.namespaceName);

            if (haveNamespace)
            {
                writer.WriteLine(string.Format("namespace {0}", options.namespaceName));
                writer.BeginBlock();
            }

            // Begin class.
            writer.WriteLine("[Serializable]");
            writer.WriteLine(string.Format("public class {0} : InputActionAssetReference", options.className));
            writer.BeginBlock();

            // Default constructor.
            writer.WriteLine(string.Format("public {0}()", options.className));
            writer.BeginBlock();
            writer.EndBlock();

            // Explicit constructor.
            writer.WriteLine(string.Format("public {0}(InputActionAsset asset)", options.className));
            ++writer.indentLevel;
            writer.WriteLine(": base(asset)");
            --writer.indentLevel;
            writer.BeginBlock();
            writer.EndBlock();

            // Initialize method.
            writer.WriteLine("private bool m_Initialized;");
            writer.WriteLine("private void Initialize()");
            writer.BeginBlock();
            foreach (var set in maps)
            {
                var setName = CSharpCodeHelpers.MakeIdentifier(set.name);
                writer.WriteLine(string.Format("// {0}", set.name));
                writer.WriteLine(string.Format("m_{0} = asset.GetActionMap(\"{1}\");", setName, set.name));

                foreach (var action in set.actions)
                {
                    var actionName = CSharpCodeHelpers.MakeIdentifier(action.name);
                    writer.WriteLine(string.Format("m_{0}_{1} = m_{2}.GetAction(\"{3}\");", setName, actionName,
                                                   setName, action.name));

                    if (options.generateEvents)
                    {
                        WriteActionEventInitializer(setName, actionName, InputActionPhase.Started, writer);
                        WriteActionEventInitializer(setName, actionName, InputActionPhase.Performed, writer);
                        WriteActionEventInitializer(setName, actionName, InputActionPhase.Cancelled, writer);
                    }
                }
            }
            writer.WriteLine("m_Initialized = true;");
            writer.EndBlock();

            // Uninitialize method.
            writer.WriteLine("private void Uninitialize()");
            writer.BeginBlock();
            foreach (var set in maps)
            {
                var setName = CSharpCodeHelpers.MakeIdentifier(set.name);
                writer.WriteLine(string.Format("m_{0} = null;", setName));

                foreach (var action in set.actions)
                {
                    var actionName = CSharpCodeHelpers.MakeIdentifier(action.name);
                    writer.WriteLine(string.Format("m_{0}_{1} = null;", setName, actionName));

                    if (options.generateEvents)
                    {
                        WriteActionEventInitializer(setName, actionName, InputActionPhase.Started, writer, removeCallback: true);
                        WriteActionEventInitializer(setName, actionName, InputActionPhase.Performed, writer, removeCallback: true);
                        WriteActionEventInitializer(setName, actionName, InputActionPhase.Cancelled, writer, removeCallback: true);
                    }
                }
            }
            writer.WriteLine("m_Initialized = false;");
            writer.EndBlock();

            // SwitchAsset method.
            writer.WriteLine("public void SwitchAsset(InputActionAsset newAsset)");
            writer.BeginBlock();
            writer.WriteLine("if (newAsset == asset) return;");
            writer.WriteLine("if (m_Initialized) Uninitialize();");
            writer.WriteLine("asset = newAsset;");
            writer.EndBlock();

            ////REVIEW: DuplicateActionsAndBindings?
            // DuplicateAndSwitchAsset method.
            writer.WriteLine("public void DuplicateAndSwitchAsset()");
            writer.BeginBlock();
            writer.WriteLine("SwitchAsset(ScriptableObject.Instantiate(asset));");
            writer.EndBlock();

            // Action map accessors.
            foreach (var map in maps)
            {
                writer.WriteLine(string.Format("// {0}", map.name));

                var setName       = CSharpCodeHelpers.MakeIdentifier(map.name);
                var setStructName = CSharpCodeHelpers.MakeTypeName(setName, "Actions");

                // Caching field for action set.
                writer.WriteLine(string.Format("private InputActionMap m_{0};", setName));

                // Caching fields for all actions.
                foreach (var action in map.actions)
                {
                    var actionName = CSharpCodeHelpers.MakeIdentifier(action.name);
                    writer.WriteLine(string.Format("private InputAction m_{0}_{1};", setName, actionName));

                    if (options.generateEvents)
                    {
                        WriteActionEventField(setName, actionName, InputActionPhase.Started, writer);
                        WriteActionEventField(setName, actionName, InputActionPhase.Performed, writer);
                        WriteActionEventField(setName, actionName, InputActionPhase.Cancelled, writer);
                    }
                }

                // Struct wrapping access to action set.
                writer.WriteLine(string.Format("public struct {0}", setStructName));
                writer.BeginBlock();

                // Constructor.
                writer.WriteLine(string.Format("private {0} m_Wrapper;", options.className));
                writer.WriteLine(string.Format("public {0}({1} wrapper) {{ m_Wrapper = wrapper; }}", setStructName,
                                               options.className));

                // Getter for each action.
                foreach (var action in map.actions)
                {
                    var actionName = CSharpCodeHelpers.MakeIdentifier(action.name);
                    writer.WriteLine(string.Format(
                                         "public InputAction @{0} {{ get {{ return m_Wrapper.m_{1}_{2}; }} }}", actionName,
                                         setName, actionName));

                    // Action event getters.
                    if (options.generateEvents)
                    {
                        WriteActionEventGetter(setName, actionName, InputActionPhase.Started, writer);
                        WriteActionEventGetter(setName, actionName, InputActionPhase.Performed, writer);
                        WriteActionEventGetter(setName, actionName, InputActionPhase.Cancelled, writer);
                    }
                }

                // Action set getter.
                writer.WriteLine(string.Format("public InputActionMap Get() {{ return m_Wrapper.m_{0}; }}",
                                               setName));

                // Enable/disable methods.
                writer.WriteLine("public void Enable() { Get().Enable(); }");
                writer.WriteLine("public void Disable() { Get().Disable(); }");

                // Clone method.
                writer.WriteLine("public InputActionMap Clone() { return Get().Clone(); }");

                // Implicit conversion operator.
                writer.WriteLine(string.Format(
                                     "public static implicit operator InputActionMap({0} set) {{ return set.Get(); }}",
                                     setStructName));

                writer.EndBlock();

                // Getter for instance of struct.
                writer.WriteLine(string.Format("public {0} @{1}", setStructName, setName));
                writer.BeginBlock();

                writer.WriteLine("get");
                writer.BeginBlock();
                writer.WriteLine("if (!m_Initialized) Initialize();");
                writer.WriteLine(string.Format("return new {0}(this);", setStructName));
                writer.EndBlock();

                writer.EndBlock();
            }

            // Control scheme accessors.
            foreach (var scheme in schemes)
            {
                var identifier = CSharpCodeHelpers.MakeIdentifier(scheme.name);

                writer.WriteLine(string.Format("private int m_{0}SchemeIndex = -1;", identifier));
                writer.WriteLine(string.Format("public InputControlScheme {0}Scheme", identifier));
                writer.BeginBlock();
                writer.WriteLine("get\n");
                writer.BeginBlock();
                writer.WriteLine(string.Format(
                                     "if (m_{0}SchemeIndex == -1) m_{0}SchemeIndex = asset.GetControlSchemeIndex(\"{1}\");", identifier,
                                     scheme.name));
                writer.WriteLine(string.Format("return asset.controlSchemes[m_{0}SchemeIndex];", identifier));
                writer.EndBlock();
                writer.EndBlock();
            }

            // Action event class.
            if (options.generateEvents)
            {
                writer.WriteLine("[Serializable]");
                writer.WriteLine("public class ActionEvent : UnityEvent<InputAction.CallbackContext>");
                writer.BeginBlock();
                writer.EndBlock();
            }

            // End class.
            writer.EndBlock();

            // End namespace.
            if (haveNamespace)
            {
                writer.EndBlock();
            }

            return(writer.buffer.ToString());
        }
Exemplo n.º 12
0
        public override void OnInspectorGUI()
        {
            // ScriptedImporterEditor in 2019.2 now requires explicitly updating the SerializedObject
            // like in other types of editors.
            serializedObject.Update();

            // Button to pop up window to edit the asset.
            if (GUILayout.Button("Edit asset"))
            {
                InputActionEditorWindow.OnOpenAsset(GetAsset().GetInstanceID(), 0);
            }

            EditorGUILayout.Space();

            // Importer settings UI.
            var generateWrapperCodeProperty = serializedObject.FindProperty("m_GenerateWrapperCode");

            EditorGUILayout.PropertyField(generateWrapperCodeProperty, m_GenerateWrapperCodeLabel);
            if (generateWrapperCodeProperty.boolValue)
            {
                var wrapperCodePathProperty      = serializedObject.FindProperty("m_WrapperCodePath");
                var wrapperClassNameProperty     = serializedObject.FindProperty("m_WrapperClassName");
                var wrapperCodeNamespaceProperty = serializedObject.FindProperty("m_WrapperCodeNamespace");

                EditorGUILayout.BeginHorizontal();
                var assetPath       = AssetDatabase.GetAssetPath(GetAsset());
                var defaultFileName = Path.ChangeExtension(assetPath, ".cs");

                wrapperCodePathProperty.PropertyFieldWithDefaultText(m_WrapperCodePathLabel, defaultFileName);

                if (GUILayout.Button("…", EditorStyles.miniButton, GUILayout.MaxWidth(20)))
                {
                    var fileName = EditorUtility.SaveFilePanel("Location for generated C# file",
                                                               Path.GetDirectoryName(defaultFileName),
                                                               Path.GetFileName(defaultFileName), "cs");
                    if (!string.IsNullOrEmpty(fileName))
                    {
                        if (fileName.StartsWith(Application.dataPath))
                        {
                            fileName = "Assets/" + fileName.Substring(Application.dataPath.Length + 1);
                        }

                        wrapperCodePathProperty.stringValue = fileName;
                    }
                }
                EditorGUILayout.EndHorizontal();

                wrapperClassNameProperty.PropertyFieldWithDefaultText(m_WrapperClassNameLabel, CSharpCodeHelpers.MakeTypeName(GetAsset().name));

                if (!CSharpCodeHelpers.IsEmptyOrProperIdentifier(wrapperClassNameProperty.stringValue))
                {
                    EditorGUILayout.HelpBox("Must be a valid C# identifier", MessageType.Error);
                }

                wrapperCodeNamespaceProperty.PropertyFieldWithDefaultText(m_WrapperCodeNamespaceLabel, "<Global namespace>");

                if (!CSharpCodeHelpers.IsEmptyOrProperNamespaceName(wrapperCodeNamespaceProperty.stringValue))
                {
                    EditorGUILayout.HelpBox("Must be a valid C# namespace name", MessageType.Error);
                }
            }

            // Using ApplyRevertGUI requires calling Update and ApplyModifiedProperties around the serializedObject,
            // and will print warning messages otherwise (see warning message in ApplyRevertGUI implementation).
            serializedObject.ApplyModifiedProperties();

            ApplyRevertGUI();
        }
Exemplo n.º 13
0
        public static string GenerateWrapperCode(InputActionAsset asset, Options options = new Options())
        {
            if (asset == null)
            {
                throw new ArgumentNullException(nameof(asset));
            }

            if (string.IsNullOrEmpty(options.sourceAssetPath))
            {
                options.sourceAssetPath = AssetDatabase.GetAssetPath(asset);
            }
            if (string.IsNullOrEmpty(options.className) && !string.IsNullOrEmpty(asset.name))
            {
                options.className =
                    CSharpCodeHelpers.MakeTypeName(asset.name);
            }

            if (string.IsNullOrEmpty(options.className))
            {
                if (string.IsNullOrEmpty(options.sourceAssetPath))
                {
                    throw new ArgumentException("options.sourceAssetPath");
                }
                options.className =
                    CSharpCodeHelpers.MakeTypeName(Path.GetFileNameWithoutExtension(options.sourceAssetPath));
            }

            var writer = new Writer
            {
                buffer = new StringBuilder()
            };

            // Header.
            if (!string.IsNullOrEmpty(options.sourceAssetPath))
            {
                writer.WriteLine($"// GENERATED AUTOMATICALLY FROM '{options.sourceAssetPath}'\n");
            }

            // Usings.
            writer.WriteLine("using System.Collections;");
            writer.WriteLine("using System.Collections.Generic;");
            writer.WriteLine("using UnityEngine.InputSystem;");
            writer.WriteLine("using UnityEngine.InputSystem.Utilities;");
            writer.WriteLine("");

            // Begin namespace.
            var haveNamespace = !string.IsNullOrEmpty(options.namespaceName);

            if (haveNamespace)
            {
                writer.WriteLine($"namespace {options.namespaceName}");
                writer.BeginBlock();
            }

            // Begin class.
            writer.WriteLine($"public class {options.className} : IInputActionCollection");
            writer.BeginBlock();

            writer.WriteLine($"private InputActionAsset asset;");

            // Default constructor.
            writer.WriteLine($"public {options.className}()");
            writer.BeginBlock();
            writer.WriteLine($"asset = InputActionAsset.FromJson(@\"{asset.ToJson().Replace("\"", "\"\"")}\");");

            var maps    = asset.actionMaps;
            var schemes = asset.controlSchemes;

            foreach (var map in maps)
            {
                var mapName = CSharpCodeHelpers.MakeIdentifier(map.name);
                writer.WriteLine($"// {map.name}");
                writer.WriteLine($"m_{mapName} = asset.FindActionMap(\"{map.name}\", throwIfNotFound: true);");

                foreach (var action in map.actions)
                {
                    var actionName = CSharpCodeHelpers.MakeIdentifier(action.name);
                    writer.WriteLine($"m_{mapName}_{actionName} = m_{mapName}.FindAction(\"{action.name}\", throwIfNotFound: true);");
                }
            }
            writer.EndBlock();
            writer.WriteLine();

            writer.WriteLine($"~{options.className}()");
            writer.BeginBlock();
            writer.WriteLine("UnityEngine.Object.Destroy(asset);");
            writer.EndBlock();
            writer.WriteLine();

            writer.WriteLine("public InputBinding? bindingMask");
            writer.BeginBlock();
            writer.WriteLine("get => asset.bindingMask;");
            writer.WriteLine("set => asset.bindingMask = value;");
            writer.EndBlock();
            writer.WriteLine();

            writer.WriteLine("public ReadOnlyArray<InputDevice>? devices");
            writer.BeginBlock();
            writer.WriteLine("get => asset.devices;");
            writer.WriteLine("set => asset.devices = value;");
            writer.EndBlock();
            writer.WriteLine();

            writer.WriteLine("public ReadOnlyArray<InputControlScheme> controlSchemes => asset.controlSchemes;");
            writer.WriteLine();

            writer.WriteLine("public bool Contains(InputAction action)");
            writer.BeginBlock();
            writer.WriteLine("return asset.Contains(action);");
            writer.EndBlock();
            writer.WriteLine();

            writer.WriteLine("public IEnumerator<InputAction> GetEnumerator()");
            writer.BeginBlock();
            writer.WriteLine("return asset.GetEnumerator();");
            writer.EndBlock();
            writer.WriteLine();

            writer.WriteLine("IEnumerator IEnumerable.GetEnumerator()");
            writer.BeginBlock();
            writer.WriteLine("return GetEnumerator();");
            writer.EndBlock();
            writer.WriteLine();

            writer.WriteLine("public void Enable()");
            writer.BeginBlock();
            writer.WriteLine("asset.Enable();");
            writer.EndBlock();
            writer.WriteLine();

            writer.WriteLine("public void Disable()");
            writer.BeginBlock();
            writer.WriteLine("asset.Disable();");
            writer.EndBlock();

            // Action map accessors.
            foreach (var map in maps)
            {
                writer.WriteLine();
                writer.WriteLine($"// {map.name}");

                var mapName     = CSharpCodeHelpers.MakeIdentifier(map.name);
                var mapTypeName = CSharpCodeHelpers.MakeTypeName(mapName, "Actions");

                // Caching field for action map.
                writer.WriteLine($"private readonly InputActionMap m_{mapName};");
                writer.WriteLine(string.Format("private I{0} m_{0}CallbackInterface;", mapTypeName));

                // Caching fields for all actions.
                foreach (var action in map.actions)
                {
                    var actionName = CSharpCodeHelpers.MakeIdentifier(action.name);
                    writer.WriteLine($"private readonly InputAction m_{mapName}_{actionName};");
                }

                // Struct wrapping access to action set.
                writer.WriteLine($"public struct {mapTypeName}");
                writer.BeginBlock();

                // Constructor.
                writer.WriteLine($"private {options.className} m_Wrapper;");
                writer.WriteLine($"public {mapTypeName}({options.className} wrapper) {{ m_Wrapper = wrapper; }}");

                // Getter for each action.
                foreach (var action in map.actions)
                {
                    var actionName = CSharpCodeHelpers.MakeIdentifier(action.name);
                    writer.WriteLine(
                        $"public InputAction @{actionName} => m_Wrapper.m_{mapName}_{actionName};");
                }

                // Action map getter.
                writer.WriteLine($"public InputActionMap Get() {{ return m_Wrapper.m_{mapName}; }}");

                // Enable/disable methods.
                writer.WriteLine("public void Enable() { Get().Enable(); }");
                writer.WriteLine("public void Disable() { Get().Disable(); }");
                writer.WriteLine("public bool enabled => Get().enabled;");

                // Implicit conversion operator.
                writer.WriteLine(
                    $"public static implicit operator InputActionMap({mapTypeName} set) {{ return set.Get(); }}");

                // SetCallbacks method.
                writer.WriteLine($"public void SetCallbacks(I{mapTypeName} instance)");
                writer.BeginBlock();

                ////REVIEW: this would benefit from having a single callback on InputActions rather than three different endpoints

                // Uninitialize existing interface.
                writer.WriteLine($"if (m_Wrapper.m_{mapTypeName}CallbackInterface != null)");
                writer.BeginBlock();
                foreach (var action in map.actions)
                {
                    var actionName     = CSharpCodeHelpers.MakeIdentifier(action.name);
                    var actionTypeName = CSharpCodeHelpers.MakeTypeName(action.name);

                    writer.WriteLine($"{actionName}.started -= m_Wrapper.m_{mapTypeName}CallbackInterface.On{actionTypeName};");
                    writer.WriteLine($"{actionName}.performed -= m_Wrapper.m_{mapTypeName}CallbackInterface.On{actionTypeName};");
                    writer.WriteLine($"{actionName}.canceled -= m_Wrapper.m_{mapTypeName}CallbackInterface.On{actionTypeName};");
                }
                writer.EndBlock();

                // Initialize new interface.
                writer.WriteLine($"m_Wrapper.m_{mapTypeName}CallbackInterface = instance;");
                writer.WriteLine("if (instance != null)");
                writer.BeginBlock();
                foreach (var action in map.actions)
                {
                    var actionName     = CSharpCodeHelpers.MakeIdentifier(action.name);
                    var actionTypeName = CSharpCodeHelpers.MakeTypeName(action.name);

                    writer.WriteLine($"{actionName}.started += instance.On{actionTypeName};");
                    writer.WriteLine($"{actionName}.performed += instance.On{actionTypeName};");
                    writer.WriteLine($"{actionName}.canceled += instance.On{actionTypeName};");
                }
                writer.EndBlock();
                writer.EndBlock();
                writer.EndBlock();

                // Getter for instance of struct.
                writer.WriteLine($"public {mapTypeName} @{mapName} => new {mapTypeName}(this);");
            }

            // Control scheme accessors.
            foreach (var scheme in schemes)
            {
                var identifier = CSharpCodeHelpers.MakeIdentifier(scheme.name);

                writer.WriteLine($"private int m_{identifier}SchemeIndex = -1;");
                writer.WriteLine($"public InputControlScheme {identifier}Scheme");
                writer.BeginBlock();
                writer.WriteLine("get");
                writer.BeginBlock();
                writer.WriteLine($"if (m_{identifier}SchemeIndex == -1) m_{identifier}SchemeIndex = asset.FindControlSchemeIndex(\"{scheme.name}\");");
                writer.WriteLine($"return asset.controlSchemes[m_{identifier}SchemeIndex];");
                writer.EndBlock();
                writer.EndBlock();
            }

            // Generate interfaces.
            foreach (var map in maps)
            {
                var typeName = CSharpCodeHelpers.MakeTypeName(map.name);
                writer.WriteLine($"public interface I{typeName}Actions");
                writer.BeginBlock();

                foreach (var action in map.actions)
                {
                    var methodName = CSharpCodeHelpers.MakeTypeName(action.name);
                    writer.WriteLine($"void On{methodName}(InputAction.CallbackContext context);");
                }

                writer.EndBlock();
            }

            // End class.
            writer.EndBlock();

            // End namespace.
            if (haveNamespace)
            {
                writer.EndBlock();
            }

            return(writer.buffer.ToString());
        }
Exemplo n.º 14
0
        private void OnNotificationBehaviorChange()
        {
            Debug.Assert(m_ActionAssetInitialized);
            serializedObject.ApplyModifiedProperties();

            var notificationBehavior = (PlayerNotifications)m_NotificationBehaviorProperty.intValue;

            switch (notificationBehavior)
            {
            // Create text that lists all the messages sent by the component.
            case PlayerNotifications.BroadcastMessages:
            case PlayerNotifications.SendMessages:
            {
                var builder = new StringBuilder();
                builder.Append("Will ");
                if (notificationBehavior == PlayerNotifications.BroadcastMessages)
                {
                    builder.Append("BroadcastMessage()");
                }
                else
                {
                    builder.Append("SendMessage()");
                }
                builder.Append(" to GameObject: ");
                builder.Append(PlayerInput.DeviceLostMessage);
                builder.Append(", ");
                builder.Append(PlayerInput.DeviceRegainedMessage);
                builder.Append(", ");
                builder.Append(PlayerInput.ControlsChangedMessage);

                var playerInput = (PlayerInput)target;
                var asset       = playerInput.m_Actions;
                if (asset != null)
                {
                    foreach (var action in asset)
                    {
                        builder.Append(", On");
                        builder.Append(CSharpCodeHelpers.MakeTypeName(action.name));
                    }
                }

                m_SendMessagesHelpText = new GUIContent(builder.ToString());
                break;
            }

            case PlayerNotifications.InvokeUnityEvents:
            {
                var playerInput = (PlayerInput)target;
                if (playerInput.m_DeviceLostEvent == null)
                {
                    playerInput.m_DeviceLostEvent = new PlayerInput.DeviceLostEvent();
                }
                if (playerInput.m_DeviceRegainedEvent == null)
                {
                    playerInput.m_DeviceRegainedEvent = new PlayerInput.DeviceRegainedEvent();
                }
                if (playerInput.m_ControlsChangedEvent == null)
                {
                    playerInput.m_ControlsChangedEvent = new PlayerInput.ControlsChangedEvent();
                }
                serializedObject.Update();

                // Force action refresh.
                m_ActionAssetInitialized = false;
                Refresh();
                break;
            }
            }

            m_NotificationBehaviorInitialized = true;
        }
Exemplo n.º 15
0
        /// <summary>
        /// Generate C# code that for the given device layout called <paramref name="layoutName"/> instantly creates
        /// an <see cref="InputDevice"/> equivalent to what the input system would create by manually interpreting
        /// the given <see cref="InputControlLayout"/>.
        /// </summary>
        /// <param name="layoutName">Name of the device layout to generate code for.</param>
        /// <param name="defines">Null/empty or a valid expression for an #if conditional compilation statement.</param>
        /// <param name="namePrefix">Prefix to prepend to the type name of <paramref name="layoutName"/>.</param>
        /// <param name="visibility">C# access modifier to use with the generated class.</param>
        /// <param name="namespace">Namespace to put the generated class in. If <c>null</c>, namespace of type behind <paramref name="layoutName"/> will be used.</param>
        /// <returns>C# source code for a precompiled version of the device layout.</returns>
        /// <remarks>
        /// The code generated by this method will be many times faster than the reflection-based <see cref="InputDevice"/>
        /// creation normally performed by the input system. It will also create less GC heap garbage.
        ///
        /// The downside to the generated code is that the makeup of the device is hardcoded and can no longer
        /// be changed by altering the <see cref="InputControlLayout"/> setup of the system.
        ///
        /// Note that it is possible to use this method with layouts generated on-the-fly by layout builders such as
        /// the one employed for <see cref="HID"/>. However, this must be done at compile/build time and can thus not
        /// be done for devices dynamically discovered at runtime. When this is acceptable, it is a way to dramatically
        /// speed up the creation of these devices.
        /// </remarks>
        /// <seealso cref="InputSystem.RegisterPrecompiledLayout{T}"/>
        public static string GenerateCodeForDeviceLayout(string layoutName, string defines = null, string namePrefix = "Fast", string visibility = "public", string @namespace = null)
        {
            if (string.IsNullOrEmpty(layoutName))
            {
                throw new ArgumentNullException(nameof(layoutName));
            }

            // Produce a device from the layout.
            var device = InputDevice.Build <InputDevice>(layoutName, noPrecompiledLayouts: true);

            // Get info about base type.
            var baseType          = device.GetType();
            var baseTypeName      = baseType.Name;
            var baseTypeNamespace = baseType.Namespace;

            // Begin generating code.
            var writer = new InputActionCodeGenerator.Writer
            {
                buffer = new StringBuilder()
            };

            writer.WriteLine(CSharpCodeHelpers.MakeAutoGeneratedCodeHeader("com.unity.inputsystem:InputLayoutCodeGenerator",
                                                                           InputSystem.version.ToString(),
                                                                           $"\"{layoutName}\" layout"));

            // Defines.
            if (defines != null)
            {
                writer.WriteLine($"#if {defines}");
                writer.WriteLine();
            }

            if (@namespace == null)
            {
                @namespace = baseTypeNamespace;
            }

            writer.WriteLine("using UnityEngine.InputSystem;");
            writer.WriteLine("using UnityEngine.InputSystem.LowLevel;");
            writer.WriteLine("using UnityEngine.InputSystem.Utilities;");
            writer.WriteLine("");
            if (@namespace != "")
            {
                writer.WriteLine("namespace " + @namespace);
            }
            writer.BeginBlock();

            writer.WriteLine($"{visibility} partial class {namePrefix}{baseTypeName} : {baseTypeNamespace}.{baseTypeName}");
            writer.BeginBlock();

            // "Metadata". ATM this is simply a flat, semicolon-separated list of names for layouts and processors that
            // we depend on. If any of them are touched, the precompiled layout should be considered invalidated.
            var internedLayoutName = new InternedString(layoutName);
            var allControls        = device.allControls;
            var usedControlLayouts = allControls.Select(x => x.m_Layout).Distinct().ToList();
            var layoutDependencies = string.Join(";",
                                                 usedControlLayouts.SelectMany(l => InputControlLayout.s_Layouts.GetBaseLayouts(l))
                                                 .Union(InputControlLayout.s_Layouts.GetBaseLayouts(internedLayoutName)));
            var processorDependencies = string.Join(";",
                                                    allControls.SelectMany(c => c.GetProcessors()).Select(p => InputProcessor.s_Processors.FindNameForType(p.GetType()))
                                                    .Where(n => !n.IsEmpty()).Distinct());
            var metadata = string.Join(";", processorDependencies, layoutDependencies);

            writer.WriteLine($"public const string metadata = \"{metadata}\";");

            // Constructor.
            writer.WriteLine($"public {namePrefix}{baseTypeName}()");
            writer.BeginBlock();

            var usagesForEachControl  = device.m_UsagesForEachControl;
            var usageToControl        = device.m_UsageToControl;
            var aliasesForEachControl = device.m_AliasesForEachControl;
            var controlCount          = allControls.Count;
            var usageCount            = usagesForEachControl?.Length ?? 0;
            var aliasCount            = aliasesForEachControl?.Length ?? 0;

            // Set up device control info.
            writer.WriteLine($"var builder = this.Setup({controlCount}, {usageCount}, {aliasCount})");
            writer.WriteLine($"    .WithName(\"{device.name}\")");
            writer.WriteLine($"    .WithDisplayName(\"{device.displayName}\")");
            writer.WriteLine($"    .WithChildren({device.m_ChildStartIndex}, {device.m_ChildCount})");
            writer.WriteLine($"    .WithLayout(new InternedString(\"{device.layout}\"))");
            if (device.noisy)
            {
                writer.WriteLine("    .IsNoisy(true)");
            }
            writer.WriteLine($"    .WithStateBlock(new InputStateBlock {{ format = new FourCC({(int)device.stateBlock.format}), sizeInBits = {device.stateBlock.sizeInBits} }});");

            // Add controls to device.
            writer.WriteLine();
            foreach (var layout in usedControlLayouts)
            {
                writer.WriteLine($"var k{layout}Layout = new InternedString(\"{layout}\");");
            }
            for (var i = 0; i < controlCount; ++i)
            {
                var control             = allControls[i];
                var controlType         = control.GetType();
                var controlVariableName = MakeControlVariableName(control);
                var controlFieldInits   = control.GetInitializersForPublicPrimitiveTypeFields();

                writer.WriteLine("");
                writer.WriteLine($"// {control.path}");
                writer.WriteLine($"var {controlVariableName} = new {controlType.FullName.Replace('+', '.')}{controlFieldInits};");
                writer.WriteLine($"{controlVariableName}.Setup()");
                writer.WriteLine($"    .At(this, {i})");
                writer.WriteLine(control.parent == device
                    ? "    .WithParent(this)"
                    : $"    .WithParent({MakeControlVariableName(control.parent)})");
                if (control.children.Count > 0)
                {
                    writer.WriteLine($"    .WithChildren({control.m_ChildStartIndex}, {control.m_ChildCount})");
                }
                writer.WriteLine($"    .WithName(\"{control.name}\")");
                writer.WriteLine($"    .WithDisplayName(\"{control.m_DisplayNameFromLayout.Replace("\\", "\\\\")}\")");
                if (!string.IsNullOrEmpty(control.m_ShortDisplayNameFromLayout))
                {
                    writer.WriteLine($"    .WithShortDisplayName(\"{control.m_ShortDisplayNameFromLayout.Replace("\\", "\\\\")}\")");
                }
                writer.WriteLine($"    .WithLayout(k{control.layout}Layout)");
                if (control.usages.Count > 0)
                {
                    writer.WriteLine($"    .WithUsages({control.m_UsageStartIndex}, {control.m_UsageCount})");
                }
                if (control.aliases.Count > 0)
                {
                    writer.WriteLine($"    .WithAliases({control.m_AliasStartIndex}, {control.m_AliasCount})");
                }
                if (control.noisy)
                {
                    writer.WriteLine("    .IsNoisy(true)");
                }
                if (control.synthetic)
                {
                    writer.WriteLine("    .IsSynthetic(true)");
                }
                writer.WriteLine("    .WithStateBlock(new InputStateBlock");
                writer.WriteLine("    {");
                writer.WriteLine($"        format = new FourCC({(int)control.stateBlock.format}),");
                writer.WriteLine($"        byteOffset = {control.stateBlock.byteOffset},");
                writer.WriteLine($"        bitOffset = {control.stateBlock.bitOffset},");
                writer.WriteLine($"        sizeInBits = {control.stateBlock.sizeInBits}");
                writer.WriteLine("    })");
                if (control.hasDefaultState)
                {
                    writer.WriteLine($"    .WithDefaultState({control.m_DefaultState})");
                }
                if (control.m_MinValue != default || control.m_MaxValue != default)
                {
                    writer.WriteLine($"    .WithMinAndMax({control.m_MinValue}, {control.m_MaxValue})");
                }
                foreach (var processor in control.GetProcessors())
                {
                    var isEditorWindowSpaceProcessor = processor is EditorWindowSpaceProcessor;
                    if (isEditorWindowSpaceProcessor)
                    {
                        writer.WriteLine("    #if UNITY_EDITOR");
                    }

                    var processorType = processor.GetType().FullName.Replace("+", ".");
                    var valueType     = InputProcessor.GetValueTypeFromType(processor.GetType());
                    var fieldInits    = processor.GetInitializersForPublicPrimitiveTypeFields();

                    writer.WriteLine($"    .WithProcessor<InputProcessor<{valueType}>, {valueType}>(new {processorType}{fieldInits})");

                    if (isEditorWindowSpaceProcessor)
                    {
                        writer.WriteLine("    #endif");
                    }
                }
                writer.WriteLine("    .Finish();");

                if (control is KeyControl key)
                {
                    writer.WriteLine($"{controlVariableName}.keyCode = UnityEngine.InputSystem.Key.{key.keyCode};");
                }
                else if (control is DpadControl.DpadAxisControl dpadAxis)
                {
                    writer.WriteLine($"{controlVariableName}.component = {dpadAxis.component};");
                }
            }

            // Initialize usages array.
            if (usageCount > 0)
            {
                writer.WriteLine();
                writer.WriteLine("// Usages.");
                for (var i = 0; i < usageCount; ++i)
                {
                    writer.WriteLine(
                        $"builder.WithControlUsage({i}, new InternedString(\"{usagesForEachControl[i]}\"), {MakeControlVariableName(usageToControl[i])});");
                }
            }

            // Initialize aliases array.
            if (aliasCount > 0)
            {
                writer.WriteLine();
                writer.WriteLine("// Aliases.");
                for (var i = 0; i < aliasCount; ++i)
                {
                    writer.WriteLine($"builder.WithControlAlias({i},  new InternedString(\"{aliasesForEachControl[i]}\"));");
                }
            }

            // Emit initializers for control getters and control arrays. This is usually what's getting set up
            // in FinishSetup(). We hardcode the look results here.
            var controlGetterProperties = new Dictionary <Type, List <PropertyInfo> >();
            var controlArrayProperties  = new Dictionary <Type, List <PropertyInfo> >();

            writer.WriteLine();
            writer.WriteLine("// Control getters/arrays.");
            writer.EmitControlArrayInitializers(device, "this", controlArrayProperties);
            writer.EmitControlGetterInitializers(device, "this", controlGetterProperties);

            for (var i = 0; i < controlCount; ++i)
            {
                var control             = allControls[i];
                var controlVariableName = MakeControlVariableName(control);

                writer.EmitControlArrayInitializers(control, controlVariableName, controlArrayProperties);
                writer.EmitControlGetterInitializers(control, controlVariableName, controlGetterProperties);
            }

            writer.WriteLine("builder.Finish();");
            writer.EndBlock();

            writer.EndBlock();
            writer.EndBlock();

            if (defines != null)
            {
                writer.WriteLine($"#endif // {defines}");
            }

            return(writer.buffer.ToString());
        }
        // Generate a string containing C# code that simplifies working with the given
        // action sets in code.
        public static string GenerateWrapperCode(IEnumerable <InputActionMap> maps, IEnumerable <InputControlScheme> schemes, Options options)
        {
            if (string.IsNullOrEmpty(options.className))
            {
                if (string.IsNullOrEmpty(options.sourceAssetPath))
                {
                    throw new ArgumentException("options.sourceAssetPath");
                }
                options.className =
                    CSharpCodeHelpers.MakeTypeName(Path.GetFileNameWithoutExtension(options.sourceAssetPath));
            }

            var writer = new Writer
            {
                buffer = new StringBuilder()
            };

            // Header.
            if (!string.IsNullOrEmpty(options.sourceAssetPath))
            {
                writer.WriteLine($"// GENERATED AUTOMATICALLY FROM '{options.sourceAssetPath}'\n");
            }

            // Usings.
            writer.WriteLine("using System;");
            writer.WriteLine("using UnityEngine;");
            if (options.generateEvents)
            {
                writer.WriteLine("using UnityEngine.Events;");
            }
            writer.WriteLine("using UnityEngine.Experimental.Input;");
            writer.WriteLine("\n");

            // Begin namespace.
            var haveNamespace = !string.IsNullOrEmpty(options.namespaceName);

            if (haveNamespace)
            {
                writer.WriteLine($"namespace {options.namespaceName}");
                writer.BeginBlock();
            }

            // Begin class.
            writer.WriteLine("[Serializable]");
            writer.WriteLine($"public class {options.className} : InputActionAssetReference");
            writer.BeginBlock();

            // Default constructor.
            writer.WriteLine($"public {options.className}()");
            writer.BeginBlock();
            writer.EndBlock();

            // Explicit constructor.
            writer.WriteLine($"public {options.className}(InputActionAsset asset)");
            ++writer.indentLevel;
            writer.WriteLine(": base(asset)");
            --writer.indentLevel;
            writer.BeginBlock();
            writer.EndBlock();

            // Initialize method.
            writer.WriteLine("[NonSerialized] private bool m_Initialized;");
            writer.WriteLine("private void Initialize()");
            writer.BeginBlock();
            foreach (var set in maps)
            {
                var setName = CSharpCodeHelpers.MakeIdentifier(set.name);
                writer.WriteLine($"// {set.name}");
                writer.WriteLine($"m_{setName} = asset.GetActionMap(\"{set.name}\");");

                foreach (var action in set.actions)
                {
                    var actionName = CSharpCodeHelpers.MakeIdentifier(action.name);
                    writer.WriteLine($"m_{setName}_{actionName} = m_{setName}.GetAction(\"{action.name}\");");

                    if (options.generateEvents)
                    {
                        WriteActionEventInitializer(setName, actionName, InputActionPhase.Started, writer);
                        WriteActionEventInitializer(setName, actionName, InputActionPhase.Performed, writer);
                        WriteActionEventInitializer(setName, actionName, InputActionPhase.Cancelled, writer);
                    }
                }
            }
            writer.WriteLine("m_Initialized = true;");
            writer.EndBlock();

            // Uninitialize method.
            writer.WriteLine("private void Uninitialize()");
            writer.BeginBlock();
            foreach (var map in maps)
            {
                var mapName = CSharpCodeHelpers.MakeIdentifier(map.name);

                if (options.generateInterfaces)
                {
                    var mapTypeName = CSharpCodeHelpers.MakeTypeName(map.name, "Actions");
                    writer.WriteLine($"if (m_{mapTypeName}CallbackInterface != null)");
                    writer.BeginBlock();
                    writer.WriteLine($"{mapName}.SetCallbacks(null);");
                    writer.EndBlock();
                }

                writer.WriteLine($"m_{mapName} = null;");

                foreach (var action in map.actions)
                {
                    var actionName = CSharpCodeHelpers.MakeIdentifier(action.name);
                    writer.WriteLine($"m_{mapName}_{actionName} = null;");

                    if (options.generateEvents)
                    {
                        WriteActionEventInitializer(mapName, actionName, InputActionPhase.Started, writer, removeCallback: true);
                        WriteActionEventInitializer(mapName, actionName, InputActionPhase.Performed, writer, removeCallback: true);
                        WriteActionEventInitializer(mapName, actionName, InputActionPhase.Cancelled, writer, removeCallback: true);
                    }
                }
            }
            writer.WriteLine("m_Initialized = false;");
            writer.EndBlock();

            // SwitchAsset method.
            writer.WriteLine("public void SetAsset(InputActionAsset newAsset)");
            writer.BeginBlock();
            writer.WriteLine("if (newAsset == asset) return;");
            if (options.generateInterfaces)
            {
                foreach (var map in maps)
                {
                    var mapName     = CSharpCodeHelpers.MakeIdentifier(map.name);
                    var mapTypeName = CSharpCodeHelpers.MakeTypeName(map.name, "Actions");
                    writer.WriteLine($"var {mapName}Callbacks = m_{mapTypeName}CallbackInterface;");
                }
            }
            writer.WriteLine("if (m_Initialized) Uninitialize();");
            writer.WriteLine("asset = newAsset;");
            if (options.generateInterfaces)
            {
                foreach (var map in maps)
                {
                    var mapName = CSharpCodeHelpers.MakeIdentifier(map.name);
                    writer.WriteLine(string.Format("{0}.SetCallbacks({0}Callbacks);", mapName));
                }
            }
            writer.EndBlock();

            // MakePrivateCopyOfActions method.
            writer.WriteLine("public override void MakePrivateCopyOfActions()");
            writer.BeginBlock();
            writer.WriteLine("SetAsset(ScriptableObject.Instantiate(asset));");
            writer.EndBlock();

            // Action map accessors.
            foreach (var map in maps)
            {
                writer.WriteLine($"// {map.name}");

                var mapName     = CSharpCodeHelpers.MakeIdentifier(map.name);
                var mapTypeName = CSharpCodeHelpers.MakeTypeName(mapName, "Actions");

                // Caching field for action map.
                writer.WriteLine($"private InputActionMap m_{mapName};");
                if (options.generateInterfaces)
                {
                    writer.WriteLine(string.Format("private I{0} m_{0}CallbackInterface;", mapTypeName));
                }

                // Caching fields for all actions.
                foreach (var action in map.actions)
                {
                    var actionName = CSharpCodeHelpers.MakeIdentifier(action.name);
                    writer.WriteLine($"private InputAction m_{mapName}_{actionName};");

                    if (options.generateEvents)
                    {
                        WriteActionEventField(mapName, actionName, InputActionPhase.Started, writer);
                        WriteActionEventField(mapName, actionName, InputActionPhase.Performed, writer);
                        WriteActionEventField(mapName, actionName, InputActionPhase.Cancelled, writer);
                    }
                }

                // Struct wrapping access to action set.
                writer.WriteLine($"public struct {mapTypeName}");
                writer.BeginBlock();

                // Constructor.
                writer.WriteLine($"private {options.className} m_Wrapper;");
                writer.WriteLine($"public {mapTypeName}({options.className} wrapper) {{ m_Wrapper = wrapper; }}");

                // Getter for each action.
                foreach (var action in map.actions)
                {
                    var actionName = CSharpCodeHelpers.MakeIdentifier(action.name);
                    writer.WriteLine(
                        $"public InputAction @{actionName} {{ get {{ return m_Wrapper.m_{mapName}_{actionName}; }} }}");

                    // Action event getters.
                    if (options.generateEvents)
                    {
                        WriteActionEventGetter(mapName, actionName, InputActionPhase.Started, writer);
                        WriteActionEventGetter(mapName, actionName, InputActionPhase.Performed, writer);
                        WriteActionEventGetter(mapName, actionName, InputActionPhase.Cancelled, writer);
                    }
                }

                // Action map getter.
                writer.WriteLine($"public InputActionMap Get() {{ return m_Wrapper.m_{mapName}; }}");

                // Enable/disable methods.
                writer.WriteLine("public void Enable() { Get().Enable(); }");
                writer.WriteLine("public void Disable() { Get().Disable(); }");
                writer.WriteLine("public bool enabled { get { return Get().enabled; } }");

                // Clone method.
                writer.WriteLine("public InputActionMap Clone() { return Get().Clone(); }");

                // Implicit conversion operator.
                writer.WriteLine(
                    $"public static implicit operator InputActionMap({mapTypeName} set) {{ return set.Get(); }}");

                // SetCallbacks method.
                if (options.generateInterfaces)
                {
                    writer.WriteLine($"public void SetCallbacks(I{mapTypeName} instance)");
                    writer.BeginBlock();

                    ////REVIEW: this would benefit from having a single callback on InputActions rather than three different endpoints

                    // Uninitialize existing interface.
                    writer.WriteLine($"if (m_Wrapper.m_{mapTypeName}CallbackInterface != null)");
                    writer.BeginBlock();
                    foreach (var action in map.actions)
                    {
                        var actionName     = CSharpCodeHelpers.MakeIdentifier(action.name);
                        var actionTypeName = CSharpCodeHelpers.MakeTypeName(action.name);

                        writer.WriteLine($"{actionName}.started -= m_Wrapper.m_{mapTypeName}CallbackInterface.On{actionTypeName};");
                        writer.WriteLine($"{actionName}.performed -= m_Wrapper.m_{mapTypeName}CallbackInterface.On{actionTypeName};");
                        writer.WriteLine($"{actionName}.cancelled -= m_Wrapper.m_{mapTypeName}CallbackInterface.On{actionTypeName};");
                    }
                    writer.EndBlock();

                    // Initialize new interface.
                    writer.WriteLine($"m_Wrapper.m_{mapTypeName}CallbackInterface = instance;");
                    writer.WriteLine("if (instance != null)");
                    writer.BeginBlock();
                    foreach (var action in map.actions)
                    {
                        var actionName     = CSharpCodeHelpers.MakeIdentifier(action.name);
                        var actionTypeName = CSharpCodeHelpers.MakeTypeName(action.name);

                        writer.WriteLine($"{actionName}.started += instance.On{actionTypeName};");
                        writer.WriteLine($"{actionName}.performed += instance.On{actionTypeName};");
                        writer.WriteLine($"{actionName}.cancelled += instance.On{actionTypeName};");
                    }
                    writer.EndBlock();

                    writer.EndBlock();
                }

                writer.EndBlock();

                // Getter for instance of struct.
                writer.WriteLine($"public {mapTypeName} @{mapName}");
                writer.BeginBlock();

                writer.WriteLine("get");
                writer.BeginBlock();
                writer.WriteLine("if (!m_Initialized) Initialize();");
                writer.WriteLine($"return new {mapTypeName}(this);");
                writer.EndBlock();

                writer.EndBlock();
            }

            // Control scheme accessors.
            foreach (var scheme in schemes)
            {
                var identifier = CSharpCodeHelpers.MakeIdentifier(scheme.name);

                writer.WriteLine($"private int m_{identifier}SchemeIndex = -1;");
                writer.WriteLine($"public InputControlScheme {identifier}Scheme");
                writer.BeginBlock();
                writer.WriteLine("get\n");
                writer.BeginBlock();
                writer.WriteLine($"if (m_{identifier}SchemeIndex == -1) m_{identifier}SchemeIndex = asset.GetControlSchemeIndex(\"{scheme.name}\");");
                writer.WriteLine($"return asset.controlSchemes[m_{identifier}SchemeIndex];");
                writer.EndBlock();
                writer.EndBlock();
            }

            // Action event class.
            if (options.generateEvents)
            {
                writer.WriteLine("[Serializable]");
                writer.WriteLine("public class ActionEvent : UnityEvent<InputAction.CallbackContext>");
                writer.BeginBlock();
                writer.EndBlock();
            }

            // End class.
            writer.EndBlock();

            // Generate interfaces.
            if (options.generateInterfaces)
            {
                foreach (var map in maps)
                {
                    var typeName = CSharpCodeHelpers.MakeTypeName(map.name);
                    writer.WriteLine($"public interface I{typeName}Actions");
                    writer.BeginBlock();

                    foreach (var action in map.actions)
                    {
                        var methodName = CSharpCodeHelpers.MakeTypeName(action.name);
                        writer.WriteLine($"void On{methodName}(InputAction.CallbackContext context);");
                    }

                    writer.EndBlock();
                }
            }

            // End namespace.
            if (haveNamespace)
            {
                writer.EndBlock();
            }

            return(writer.buffer.ToString());
        }
Exemplo n.º 17
0
        /// <summary>
        /// Generate C# code for an <see cref="InputDevice"/> derived class that exposes the controls
        /// for the actions found in the given Steam IGA description.
        /// </summary>
        /// <param name="vdf"></param>
        /// <param name="namespaceAndClassName"></param>
        /// <returns></returns>
        public static string GenerateInputDeviceFromSteamIGA(string vdf, string namespaceAndClassName)
        {
            if (string.IsNullOrEmpty(vdf))
            {
                throw new ArgumentNullException("vdf");
            }
            if (string.IsNullOrEmpty(namespaceAndClassName))
            {
                throw new ArgumentNullException("namespaceAndClassName");
            }

            // Parse VDF.
            var parsedVdf = ParseVDF(vdf);
            var actions   = (Dictionary <string, object>)((Dictionary <string, object>)parsedVdf["In Game Actions"])["actions"];

            // Determine class and namespace name.
            var namespaceName  = "";
            var className      = "";
            var indexOfLastDot = namespaceAndClassName.LastIndexOf('.');

            if (indexOfLastDot != -1)
            {
                namespaceName = namespaceAndClassName.Substring(0, indexOfLastDot);
                className     = namespaceAndClassName.Substring(indexOfLastDot + 1);
            }
            else
            {
                className = namespaceAndClassName;
            }
            var stateStructName = className + "State";

            var builder = new StringBuilder();

            builder.Append("// THIS FILE HAS BEEN AUTO-GENERATED\n");
            builder.Append("#if (UNITY_EDITOR || UNITY_STANDALONE) && UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT\n");
            builder.Append("using UnityEngine;\n");
            builder.Append("using UnityEngine.Experimental.Input;\n");
            builder.Append("using UnityEngine.Experimental.Input.Controls;\n");
            builder.Append("using UnityEngine.Experimental.Input.Utilities;\n");
            builder.Append("using UnityEngine.Experimental.Input.Plugins.Steam;\n");
            builder.Append("#if UNITY_EDITOR\n");
            builder.Append("using UnityEditor;\n");
            builder.Append("#endif\n");
            builder.Append("\n");
            if (!string.IsNullOrEmpty(namespaceName))
            {
                builder.Append("namespace ");
                builder.Append(namespaceName);
                builder.Append("\n{\n");
            }

            // InitializeOnLoad attribute.
            builder.Append("#if UNITY_EDITOR\n");
            builder.Append("[InitializeOnLoad]\n");
            builder.Append("#endif\n");

            // Control layout attribute.
            builder.Append("[InputControlLayout(stateType = typeof(");
            builder.Append(stateStructName);
            builder.Append("))]\n");

            // Class declaration.
            builder.Append("public class ");
            builder.Append(className);
            builder.Append(" : SteamController, IInputUpdateCallbackReceiver\n{\n");

            // Device matcher.
            builder.Append("    private static InputDeviceMatcher deviceMatcher\n");
            builder.Append("    {\n");
            builder.Append("        get { return new InputDeviceMatcher().WithInterface(\"Steam\").WithProduct(\"");
            builder.Append(className);
            builder.Append("\"); }\n");
            builder.Append("    }\n");

            // Static constructor.
            builder.Append("#if UNITY_EDITOR\n");
            builder.Append("    static ");
            builder.Append(className);
            builder.Append("()\n");
            builder.Append("    {\n");
            builder.Append("        InputSystem.RegisterLayout<");
            builder.Append(className);
            builder.Append(">(matches: deviceMatcher);\n");
            builder.Append("    }\n");
            builder.Append("#endif\n");

            // Update method.
            builder.Append("    public void OnUpdate(InputUpdateType updateType)\n");
            builder.Append("    {\n");
            builder.Append("        ////TODO\n");
            builder.Append("    }\n");

            // RuntimeInitializeOnLoadMethod.
            // NOTE: Not relying on static ctor here. See il2cpp bug 1014293.
            builder.Append("    [RuntimeInitializeOnLoadMethod(loadType: RuntimeInitializeLoadType.BeforeSceneLoad)]\n");
            builder.Append("    private static void RuntimeInitializeOnLoad()\n");
            builder.Append("    {\n");
            builder.Append("        InputSystem.RegisterLayout<");
            builder.Append(className);
            builder.Append(">(matches: deviceMatcher);\n");
            builder.Append("    }\n");

            // Control properties.
            foreach (var setEntry in actions)
            {
                var setEntryProperties = (Dictionary <string, object>)setEntry.Value;

                // StickPadGyros.
                var stickPadGyros = (Dictionary <string, object>)setEntryProperties["StickPadGyro"];
                foreach (var entry in stickPadGyros)
                {
                    var entryProperties = (Dictionary <string, object>)entry.Value;
                    var isStick         = entryProperties.ContainsKey("input_mode") && (string)entryProperties["input_mode"] == "joystick_move";
                    builder.Append(string.Format("    public {0} {1} {{ get; protected set; }}\n", isStick ? "StickControl" : "Vector2Control",
                                                 CSharpCodeHelpers.MakeIdentifier(entry.Key)));
                }

                // Buttons.
                var buttons = (Dictionary <string, object>)setEntryProperties["Button"];
                foreach (var entry in buttons)
                {
                    builder.Append(string.Format("    public ButtonControl {0} {{ get; protected set; }}\n",
                                                 CSharpCodeHelpers.MakeIdentifier(entry.Key)));
                }

                // AnalogTriggers.
                var analogTriggers = (Dictionary <string, object>)setEntryProperties["AnalogTrigger"];
                foreach (var entry in analogTriggers)
                {
                    builder.Append(string.Format("    public AxisControl {0} {{ get; protected set; }}\n",
                                                 CSharpCodeHelpers.MakeIdentifier(entry.Key)));
                }
            }

            // FinishSetup method.
            builder.Append("    protected override void FinishSetup(InputDeviceBuilder builder)\n");
            builder.Append("    {\n");
            builder.Append("        base.FinishSetup(builder);\n");
            foreach (var setEntry in actions)
            {
                var setEntryProperties = (Dictionary <string, object>)setEntry.Value;

                // StickPadGyros.
                var stickPadGyros = (Dictionary <string, object>)setEntryProperties["StickPadGyro"];
                foreach (var entry in stickPadGyros)
                {
                    var entryProperties = (Dictionary <string, object>)entry.Value;
                    var isStick         = entryProperties.ContainsKey("input_mode") && (string)entryProperties["input_mode"] == "joystick_move";
                    builder.Append(string.Format("        {0} = builder.GetControl<{1}>(\"{2}\");\n",
                                                 CSharpCodeHelpers.MakeIdentifier(entry.Key),
                                                 isStick ? "StickControl" : "Vector2Control",
                                                 entry.Key));
                }

                // Buttons.
                var buttons = (Dictionary <string, object>)setEntryProperties["Button"];
                foreach (var entry in buttons)
                {
                    builder.Append(string.Format("        {0} = builder.GetControl<ButtonControl>(\"{1}\");\n",
                                                 CSharpCodeHelpers.MakeIdentifier(entry.Key),
                                                 entry.Key));
                }

                // AnalogTriggers.
                var analogTriggers = (Dictionary <string, object>)setEntryProperties["AnalogTrigger"];
                foreach (var entry in analogTriggers)
                {
                    builder.Append(string.Format("        {0} = builder.GetControl<AxisControl>(\"{1}\");\n",
                                                 CSharpCodeHelpers.MakeIdentifier(entry.Key),
                                                 entry.Key));
                }
            }
            builder.Append("    }\n");

            builder.Append("}\n");

            if (!string.IsNullOrEmpty(namespaceName))
            {
                builder.Append("}\n");
            }

            // State struct.
            builder.Append("public unsafe struct ");
            builder.Append(stateStructName);
            builder.Append(" : IInputStateTypeInfo\n");
            builder.Append("{\n");
            builder.Append("    public FourCC GetFormat()\n");
            builder.Append("    {\n");
            ////TODO: handle class names that are shorter than 4 characters
            ////TODO: uppercase characters
            builder.Append(string.Format("        return new FourCC('{0}', '{1}', '{2}', '{3}');\n", className[0], className[1], className[2], className[3]));
            builder.Append("    }\n");
            builder.Append("\n");
            foreach (var setEntry in actions)
            {
                var setEntryProperties = (Dictionary <string, object>)setEntry.Value;

                // StickPadGyros.
                var stickPadGyros = (Dictionary <string, object>)setEntryProperties["StickPadGyro"];
                foreach (var entry in stickPadGyros)
                {
                    var entryProperties = (Dictionary <string, object>)entry.Value;
                    var isStick         = entryProperties.ContainsKey("input_mode") && (string)entryProperties["input_mode"] == "joystick_move";

                    builder.Append(string.Format("    [InputControl(name = \"{0}\", layout = \"{1}\")]\n", entry.Key, isStick ? "Stick" : "Vector2"));
                    builder.Append(string.Format("    public Vector2 {0};\n", CSharpCodeHelpers.MakeIdentifier(entry.Key)));
                }

                // Buttons.
                var buttons     = (Dictionary <string, object>)setEntryProperties["Button"];
                var buttonCount = buttons.Count;
                if (buttonCount > 0)
                {
                    var bit = 0;
                    foreach (var entry in buttons)
                    {
                        builder.Append(string.Format(
                                           "    [InputControl(name = \"{0}\", layout = \"Button\", bit = {1})]\n", entry.Key, bit));
                        ++bit;
                    }

                    var byteCount = (buttonCount + 7) / 8;
                    builder.Append("    public fixed byte buttons[");
                    builder.Append(byteCount.ToString());
                    builder.Append("];\n");
                }

                // AnalogTriggers.
                var analogTriggers = (Dictionary <string, object>)setEntryProperties["AnalogTrigger"];
                foreach (var entry in analogTriggers)
                {
                    builder.Append(string.Format("    [InputControl(name = \"{0}\", layout = \"Axis\")]\n", entry.Key));
                    builder.Append(string.Format("    public float {0};\n", CSharpCodeHelpers.MakeIdentifier(entry.Key)));
                }
            }
            builder.Append("}\n");

            builder.Append("#endif\n");

            return(builder.ToString());
        }
Exemplo n.º 18
0
        /// <summary>
        /// Generate C# code that for the given device layout called <paramref name="layoutName"/> instantly creates
        /// an <see cref="InputDevice"/> equivalent to what the input system would create by manually interpreting
        /// the given <see cref="InputControlLayout"/>.
        /// </summary>
        /// <param name="layoutName">Name of the device layout to generate code for.</param>
        /// <param name="defines">Null/empty or a valid expression for an #if conditional compilation statement.</param>
        /// <param name="namePrefix">Prefix to prepend to the type name of <paramref name="layoutName"/>.</param>
        /// <param name="visibility">C# access modifier to use with the generated class.</param>
        /// <param name="namespace">Namespace to put the generated class in. If <c>null</c>, namespace of type behind <paramref name="layoutName"/> will be used.</param>
        /// <returns>C# source code for a precompiled version of the device layout.</returns>
        /// <remarks>
        /// The code generated by this method will be many times faster than the reflection-based <see cref="InputDevice"/>
        /// creation normally performed by the input system. It will also create less GC heap garbage.
        ///
        /// The downside to the generated code is that the makeup of the device is hardcoded and can no longer
        /// be changed by altering the <see cref="InputControlLayout"/> setup of the system.
        ///
        /// Note that it is possible to use this method with layouts generated on-the-fly by layout builders such as
        /// the one employed for <see cref="HID"/>. However, this must be done at compile/build time and can thus not
        /// be done for devices dynamically discovered at runtime. When this is acceptable, it is a way to dramatically
        /// speed up the creation of these devices.
        /// </remarks>
        /// <seealso cref="InputSystem.RegisterPrecompiledLayout{T}"/>
        public static string GenerateCodeForDeviceLayout(string layoutName, string defines = null, string namePrefix = "Fast", string visibility = "public", string @namespace = null)
        {
            if (string.IsNullOrEmpty(layoutName))
            {
                throw new ArgumentNullException(nameof(layoutName));
            }

            // Produce a device from the layout.
            var device = InputDevice.Build <InputDevice>(layoutName, noPrecompiledLayouts: true);

            // Get info about base type.
            var baseType          = device.GetType();
            var baseTypeName      = baseType.Name;
            var baseTypeNamespace = baseType.Namespace;

            // Begin generating code.
            var writer = new InputActionCodeGenerator.Writer
            {
                buffer = new StringBuilder()
            };

            writer.WriteLine(CSharpCodeHelpers.MakeAutoGeneratedCodeHeader("com.unity.inputsystem:InputLayoutCodeGenerator",
                                                                           InputSystem.version.ToString(),
                                                                           $"\"{layoutName}\" layout"));

            // Defines.
            if (defines != null)
            {
                writer.WriteLine($"#if {defines}");
                writer.WriteLine();
            }

            if (@namespace == null)
            {
                @namespace = baseTypeNamespace;
            }

            writer.WriteLine("using UnityEngine.InputSystem;");
            writer.WriteLine("using UnityEngine.InputSystem.LowLevel;");
            writer.WriteLine("using UnityEngine.InputSystem.Utilities;");
            writer.WriteLine("");
            writer.WriteLine("// Suppress warnings from local variables for control references");
            writer.WriteLine("// that we don't end up using.");
            writer.WriteLine("#pragma warning disable CS0219");
            writer.WriteLine("");
            if (@namespace != "")
            {
                writer.WriteLine("namespace " + @namespace);
            }
            writer.BeginBlock();

            writer.WriteLine($"{visibility} partial class {namePrefix}{baseTypeName} : {baseTypeNamespace}.{baseTypeName}");
            writer.BeginBlock();

            // "Metadata". ATM this is simply a flat, semicolon-separated list of names for layouts and processors that
            // we depend on. If any of them are touched, the precompiled layout should be considered invalidated.
            var internedLayoutName = new InternedString(layoutName);
            var allControls        = device.allControls;
            var usedControlLayouts = allControls.Select(x => x.m_Layout).Distinct().ToList();
            var layoutDependencies = string.Join(";",
                                                 usedControlLayouts.SelectMany(l => InputControlLayout.s_Layouts.GetBaseLayouts(l))
                                                 .Union(InputControlLayout.s_Layouts.GetBaseLayouts(internedLayoutName)));
            var processorDependencies = string.Join(";",
                                                    allControls.SelectMany(c => c.GetProcessors()).Select(p => InputProcessor.s_Processors.FindNameForType(p.GetType()))
                                                    .Where(n => !n.IsEmpty()).Distinct());
            var metadata = string.Join(";", processorDependencies, layoutDependencies);

            writer.WriteLine($"public const string metadata = \"{metadata}\";");

            // Constructor.
            writer.WriteLine($"public {namePrefix}{baseTypeName}()");
            writer.BeginBlock();

            var usagesForEachControl  = device.m_UsagesForEachControl;
            var usageToControl        = device.m_UsageToControl;
            var aliasesForEachControl = device.m_AliasesForEachControl;
            var controlCount          = allControls.Count;
            var usageCount            = usagesForEachControl?.Length ?? 0;
            var aliasCount            = aliasesForEachControl?.Length ?? 0;

            // Set up device control info.
            writer.WriteLine($"var builder = this.Setup({controlCount}, {usageCount}, {aliasCount})");
            writer.WriteLine($"    .WithName(\"{device.name}\")");
            writer.WriteLine($"    .WithDisplayName(\"{device.displayName}\")");
            writer.WriteLine($"    .WithChildren({device.m_ChildStartIndex}, {device.m_ChildCount})");
            writer.WriteLine($"    .WithLayout(new InternedString(\"{device.layout}\"))");
            writer.WriteLine($"    .WithStateBlock(new InputStateBlock {{ format = new FourCC({(int)device.stateBlock.format}), sizeInBits = {device.stateBlock.sizeInBits} }});");

            if (device.noisy)
            {
                writer.WriteLine("builder.IsNoisy(true);");
            }

            // Add controls to device.
            writer.WriteLine();
            foreach (var layout in usedControlLayouts)
            {
                writer.WriteLine($"var k{layout}Layout = new InternedString(\"{layout}\");");
            }

            for (var i = 0; i < controlCount; ++i)
            {
                var control             = allControls[i];
                var controlVariableName = MakeControlVariableName(control);

                writer.WriteLine("");
                writer.WriteLine($"// {control.path}");
                var parentName = "this";
                if (control.parent != device)
                {
                    parentName = MakeControlVariableName(control.parent);
                }
                writer.WriteLine($"var {controlVariableName} = {NameOfControlMethod(controlVariableName)}(k{control.layout}Layout, {parentName});");
            }

            // Initialize usages array.
            if (usageCount > 0)
            {
                writer.WriteLine();
                writer.WriteLine("// Usages.");
                for (var i = 0; i < usageCount; ++i)
                {
                    writer.WriteLine(
                        $"builder.WithControlUsage({i}, new InternedString(\"{usagesForEachControl[i]}\"), {MakeControlVariableName(usageToControl[i])});");
                }
            }

            // Initialize aliases array.
            if (aliasCount > 0)
            {
                writer.WriteLine();
                writer.WriteLine("// Aliases.");
                for (var i = 0; i < aliasCount; ++i)
                {
                    writer.WriteLine($"builder.WithControlAlias({i},  new InternedString(\"{aliasesForEachControl[i]}\"));");
                }
            }

            // Emit initializers for control getters and control arrays. This is usually what's getting set up
            // in FinishSetup(). We hardcode the look results here.
            var controlGetterProperties = new Dictionary <Type, List <PropertyInfo> >();
            var controlArrayProperties  = new Dictionary <Type, List <PropertyInfo> >();

            writer.WriteLine();
            writer.WriteLine("// Control getters/arrays.");
            writer.EmitControlArrayInitializers(device, "this", controlArrayProperties);
            writer.EmitControlGetterInitializers(device, "this", controlGetterProperties);

            for (var i = 0; i < controlCount; ++i)
            {
                var control             = allControls[i];
                var controlVariableName = MakeControlVariableName(control);

                writer.EmitControlArrayInitializers(control, controlVariableName, controlArrayProperties);
                writer.EmitControlGetterInitializers(control, controlVariableName, controlGetterProperties);
            }

            // State offset to control index map.
            if (device.m_StateOffsetToControlMap != null)
            {
                writer.WriteLine();
                writer.WriteLine("// State offset to control index map.");
                writer.WriteLine("builder.WithStateOffsetToControlIndexMap(new uint[]");
                writer.WriteLine("{");
                ++writer.indentLevel;
                var map        = device.m_StateOffsetToControlMap;
                var entryCount = map.Length;
                for (var index = 0; index < entryCount;)
                {
                    if (index != 0)
                    {
                        writer.WriteLine();
                    }
                    // 10 entries a line.
                    writer.WriteIndent();
                    for (var i = 0; i < 10 && index < entryCount; ++index, ++i)
                    {
                        writer.Write((index != 0 ? ", " : "") + map[index] + "u");
                    }
                }
                writer.WriteLine();
                --writer.indentLevel;
                writer.WriteLine("});");
            }

            writer.WriteLine();

            writer.WriteLine("builder.Finish();");
            writer.EndBlock();

            for (var i = 0; i < controlCount; ++i)
            {
                var control             = allControls[i];
                var controlType         = control.GetType();
                var controlVariableName = MakeControlVariableName(control);
                var controlFieldInits   = control.GetInitializersForPublicPrimitiveTypeFields();
                writer.WriteLine();
                EmitControlMethod(writer, controlVariableName, controlType, controlFieldInits, i, control);
            }

            writer.EndBlock();
            writer.EndBlock();

            if (defines != null)
            {
                writer.WriteLine($"#endif // {defines}");
            }

            return(writer.buffer.ToString());
        }
Exemplo n.º 19
0
 private static string MakeControlVariableName(InputControl control)
 {
     return("ctrl" + CSharpCodeHelpers.MakeIdentifier(control.path));
 }