using System; using System.Collections.Generic; using System.Text; using UnityEngine; namespace Framework.Input { /// Provides helper methods for the input system public static class InputHelper { /// /// Text that is used to indicate that an action has no buttons or axes assigned /// private const string UnassignedText = ""; /// /// Retrieves the human-readable name of the first key or axis assigned to an action /// /// Input mapper that holds the key bindings /// Name of the action whose bindings will be looked up /// The human-readable name of the key or axis assigned to the action public static string GetAssignedKeyName(InputMapper inputMapper, string actionName) { IAction action; if(inputMapper.Actions.TryGet(actionName, out action)) { if(action.BoundJoystickAxes.Count > 0) { return getBoundJoystickAxisName(action.BoundJoystickAxes); } else if(action.BoundKeys.Count > 0) { return getBoundKeyName(action.BoundKeys); } } return UnassignedText; } /// /// Replaces any action names in a string with the keys assigned to the action /// /// /// Text in which the action names will be replaced with their bound keys /// /// The input string with all action names replaced by their bound keys /// /// /// This replaces any action names in the format %action% with the keys bound to /// the action. This is useful if you want to display instructions to the player, /// for example "Press %jump% to jump!" or "Move left and right /// with %left% and %right%!". /// /// /// If you want to use a percent character in your text, just write a double /// percent character (eg. "Press %fire% when the weapon is 100%% charged"). /// /// /// While this method is optimized fairly well, since text will be displayed for /// multiple frames, it's a good idea to cache the result and display that instead /// of expanding the actions over and over (at the very least it will feed /// the garbage collector). /// /// /// /// /// void Awake() { /// this.tutorialMessage = InputHelper.ExpandActions( /// this.myInputMapper, /// "Press %jump% and then %crouch% to jump into the small gap" /// ); /// } /// /// /// /// public static string ExpandActions(InputMapper inputMapper, string textWithPlaceholders) { if(string.IsNullOrEmpty(textWithPlaceholders)) { return textWithPlaceholders; } var builder = new StringBuilder(textWithPlaceholders.Length); int actionStartIndex = -1; int textStartIndex = 0; // Append text outside '%' characters normally and replace text inside '%' characters for(int index = 0; index < textWithPlaceholders.Length; ++index) { if(textWithPlaceholders[index] == '%') { if(actionStartIndex == -1) { // First '%' character, append text until here builder.Append(textWithPlaceholders, textStartIndex, index - textStartIndex); textStartIndex = -1; actionStartIndex = index + 1; } else if(index == actionStartIndex) { // Double '%' character? builder.Append('%'); } else { // Second '%' character with content inbetween, replace action name string actionName = textWithPlaceholders.Substring( actionStartIndex, index - actionStartIndex ); builder.Append(GetAssignedKeyName(inputMapper, actionName)); actionStartIndex = -1; textStartIndex = index + 1; } } } // Append the last chunk of text, if any if(textStartIndex < textWithPlaceholders.Length) { builder.Append( textWithPlaceholders, textStartIndex, textWithPlaceholders.Length - textStartIndex ); } return builder.ToString(); } /// Retrieves the name of a bound key /// Keys that have been bound to the action /// The name of the first bound key in the collection private static string getBoundKeyName(ICollection boundKeys) { foreach(KeyCode keyCode in boundKeys) { return keyCode.ToString(); } return UnassignedText; } /// Retrieves the name of a bound axis /// Joystick axis that have been bound to the action /// The name of the first bound joystick axis in the collection private static string getBoundJoystickAxisName(ICollection boundAxes) { foreach(JoystickAxis axis in boundAxes) { // Check if this is a joystick's X axis int index = indexOfAxis(axis.AxisName, "X"); if(index == -1) { index = axis.AxisName.IndexOf( "horizontal", StringComparison.InvariantCultureIgnoreCase ); } if(index != -1) { string joystickName = axis.AxisName.Substring(0, index); return string.Concat(joystickName, " ", axis.IsNegative ? "left" : "right"); } // Check if this is a joystick's Y axis index = indexOfAxis(axis.AxisName, "Y"); if(index == -1) { index = axis.AxisName.IndexOf( "vertical", StringComparison.InvariantCultureIgnoreCase ); } if(index != -1) { string joystickName = axis.AxisName.Substring(0, index); // TODO: Verify that 'up' and 'down' are not flipped here return string.Concat(joystickName, " ", axis.IsNegative ? "up" : "down"); } } // It's probably not the X or Y axis, so use the axis name foreach(JoystickAxis axis in boundAxes) { return string.Concat(axis.AxisName, " ", axis.IsNegative ? "negative" : "positive"); } return UnassignedText; } /// Looks for an axis in a joystick axis name /// Joystick axis name in which the axis part will be searched /// Axis to search the joystick axis name for /// The index of the axis part in the joystick axis name private static int indexOfAxis(string axisName, string axis) { int index; index = axisName.IndexOf(axis + " axis", StringComparison.InvariantCultureIgnoreCase); if(index != -1) { return index; } index = axisName.IndexOf(axis + "axis", StringComparison.InvariantCultureIgnoreCase); if(index != -1) { return index; } index = axisName.IndexOf("axis " + axis, StringComparison.InvariantCultureIgnoreCase); if(index != -1) { return index; } return axisName.IndexOf("axis" + axis, StringComparison.InvariantCultureIgnoreCase); } } } // namespace Framework.Input