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()); }
/// <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.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.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\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 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"); 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 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); } 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 System.Collections;"); writer.WriteLine("using System.Collections.Generic;"); writer.WriteLine("using UnityEngine;"); 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 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}\");"); } } 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"); writer.BeginBlock(); writer.WriteLine("get => asset.controlSchemes;"); writer.EndBlock(); 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 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 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} {{ get {{ return 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 { 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. 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}"); writer.BeginBlock(); writer.WriteLine("get"); writer.BeginBlock(); 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"); 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(); } // 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()); }
private static string MakeControlVariableName(InputControl control) { return("ctrl" + CSharpCodeHelpers.MakeIdentifier(control.path)); }
// 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(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 mapName = CSharpCodeHelpers.MakeIdentifier(map.name); var mapTypeName = CSharpCodeHelpers.MakeTypeName(mapName, "Actions"); // Caching field for action map. writer.WriteLine(string.Format("private InputActionMap m_{0};", 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(string.Format("private InputAction m_{0}_{1};", 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(string.Format("public struct {0}", mapTypeName)); writer.BeginBlock(); // Constructor. writer.WriteLine(string.Format("private {0} m_Wrapper;", options.className)); writer.WriteLine(string.Format("public {0}({1} wrapper) {{ m_Wrapper = wrapper; }}", mapTypeName, 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, 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(string.Format("public InputActionMap Get() {{ return m_Wrapper.m_{0}; }}", 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(string.Format( "public static implicit operator InputActionMap({0} set) {{ return set.Get(); }}", mapTypeName)); // SetCallbacks method. if (options.generateInterfaces) { writer.WriteLine(string.Format("public void SetCallbacks(I{0} instance)", mapTypeName)); writer.BeginBlock(); ////REVIEW: this would benefit from having a single callback on InputActions rather than three different endpoints // Uninitialize existing interface. writer.WriteLine(string.Format("if (m_Wrapper.m_{0}CallbackInterface != null)", mapTypeName)); writer.BeginBlock(); foreach (var action in map.actions) { var actionName = CSharpCodeHelpers.MakeIdentifier(action.name); var actionTypeName = CSharpCodeHelpers.MakeTypeName(action.name); writer.WriteLine(string.Format("{1}.started -= m_Wrapper.m_{0}CallbackInterface.On{2};", mapTypeName, actionName, actionTypeName)); writer.WriteLine(string.Format("{1}.performed -= m_Wrapper.m_{0}CallbackInterface.On{2};", mapTypeName, actionName, actionTypeName)); writer.WriteLine(string.Format("{1}.cancelled -= m_Wrapper.m_{0}CallbackInterface.On{2};", mapTypeName, actionName, actionTypeName)); } writer.EndBlock(); // Initialize new interface. writer.WriteLine(string.Format("m_Wrapper.m_{0}CallbackInterface = instance;", mapTypeName)); 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(string.Format("{0}.started += instance.On{1};", actionName, actionTypeName)); writer.WriteLine(string.Format("{0}.performed += instance.On{1};", actionName, actionTypeName)); writer.WriteLine(string.Format("{0}.cancelled += instance.On{1};", actionName, actionTypeName)); } writer.EndBlock(); writer.EndBlock(); } writer.EndBlock(); // Getter for instance of struct. writer.WriteLine(string.Format("public {0} @{1}", mapTypeName, mapName)); writer.BeginBlock(); writer.WriteLine("get"); writer.BeginBlock(); writer.WriteLine("if (!m_Initialized) Initialize();"); writer.WriteLine(string.Format("return new {0}(this);", mapTypeName)); 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(); // Generate interfaces. if (options.generateInterfaces) { foreach (var map in maps) { var typeName = CSharpCodeHelpers.MakeTypeName(map.name); writer.WriteLine(string.Format("public interface I{0}Actions", typeName)); writer.BeginBlock(); foreach (var action in map.actions) { var methodName = CSharpCodeHelpers.MakeTypeName(action.name); writer.WriteLine(string.Format("void On{0}(InputAction.CallbackContext context);", methodName)); } writer.EndBlock(); } } // End namespace. if (haveNamespace) { writer.EndBlock(); } return(writer.buffer.ToString()); }
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(string.Format("if (m_{0}{1}Action{2} != null)", setName, actionNameCased, phase)); ++writer.indentLevel; writer.WriteLine(string.Format("m_{0}_{4}.{3} {5}= m_{0}{1}Action{2}.Invoke;", setName, actionNameCased, phase, callbackName, CSharpCodeHelpers.MakeIdentifier(actionName), removeCallback ? "-" : "+")); --writer.indentLevel; }