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