Skip to content

Instantly share code, notes, and snippets.

@phosphoer
Last active June 8, 2023 09:33
Show Gist options
  • Star 17 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save phosphoer/87f87fe5f99c4331bd15d4b762e1a572 to your computer and use it in GitHub Desktop.
Save phosphoer/87f87fe5f99c4331bd15d4b762e1a572 to your computer and use it in GitHub Desktop.
Rewired Glyph Mappings
using UnityEngine;
using System.Collections.Generic;
[CreateAssetMenu(fileName = "new-controller-icon-mapping", menuName = "Controller Icon Mapping")]
public class ControllerIconMapDefinition : ScriptableObject
{
[SerializeField]
private InputIconMapDefinition gamepadMap = null;
[SerializeField]
private InputIconMapDefinition keyboardMap = null;
[SerializeField]
private InputIconMapDefinition mouseMap = null;
[SerializeField]
private ControllerMapPair[] controllerMaps = null;
[System.Serializable]
private class ControllerMapPair
{
public string Name = string.Empty;
public string ControllerGuid = string.Empty;
public InputIconMapDefinition IconMap = null;
}
private Dictionary<string, InputIconMapDefinition> _controllerIdMap;
public static void GetElementMapsForAction(int actionId, Rewired.Player player, List<Rewired.ActionElementMap> elementMaps)
{
Rewired.Controller lastController = player.controllers.GetLastActiveController();
foreach (var elementMap in player.controllers.maps.ElementMapsWithAction(lastController, actionId, skipDisabledMaps: false))
{
elementMaps.Add(elementMap);
}
}
public InputIconMapDefinition GetMapForHardware(Rewired.Controller controller)
{
EnsureControllerMaps();
string hardwareTypeGuid = controller.hardwareTypeGuid.ToString();
if (_controllerIdMap.TryGetValue(hardwareTypeGuid, out InputIconMapDefinition hardwareMap))
{
return hardwareMap;
}
if (controller.type == Rewired.ControllerType.Joystick)
return gamepadMap;
else if (controller == Rewired.ReInput.controllers.Keyboard)
return keyboardMap;
else if (controller == Rewired.ReInput.controllers.Mouse)
return mouseMap;
return null;
}
// Find icons for an input action, based on whatever the current detected controller type is
public bool GetIconsForAction(int actionId, Rewired.Player player, List<InputIcon> inputIcons)
{
// Find out the last controller used
EnsureControllerMaps();
Rewired.Controller lastController = player.controllers.GetLastActiveController();
if (lastController == null)
lastController = GetPlatformDefaultController();
// Store previous icon count so we can determine whether we successfully found more
int prevCount = inputIcons.Count;
var iconMap = GetMapForHardware(lastController);
if (iconMap == mouseMap || iconMap == keyboardMap)
{
FillInputIcons(inputIcons, mouseMap, player, Rewired.ReInput.controllers.Mouse, actionId);
FillInputIcons(inputIcons, keyboardMap, player, Rewired.ReInput.controllers.Keyboard, actionId);
}
else
{
FillInputIcons(inputIcons, iconMap, player, lastController, actionId);
}
return inputIcons.Count > prevCount;
}
// Find an icon for a specific input mapping, which is already associated with a controller
public InputIcon GetIconForInput(Rewired.ActionElementMap elementMap)
{
// This is an unassigned action, and therefore can't have any icon
if (elementMap.controllerMap == null)
return null;
// Get info about the input action
EnsureControllerMaps();
Rewired.Controller controller = elementMap.controllerMap.controller;
// Get the icon map for this hardware
InputIconMapDefinition iconMap = GetMapForHardware(controller);
if (iconMap != null)
{
return iconMap.GetInputIcon(elementMap, controller);
}
return null;
}
private Rewired.Controller GetPlatformDefaultController()
{
// If there's any connected gamepad, use that
if (Rewired.ReInput.controllers.Joysticks.Count > 0)
{
return Rewired.ReInput.controllers.Joysticks[0];
}
// Default to the keyboard (this does not affect console)
return Rewired.ReInput.controllers.Keyboard;
}
private void EnsureControllerMaps()
{
if (_controllerIdMap == null)
{
_controllerIdMap = new Dictionary<string, InputIconMapDefinition>();
for (int i = 0; i < controllerMaps.Length; ++i)
{
ControllerMapPair pair = controllerMaps[i];
_controllerIdMap[pair.ControllerGuid.ToString()] = pair.IconMap;
}
}
}
// Fill input icons using the player and a controller
private void FillInputIcons(List<InputIcon> inputIcons, InputIconMapDefinition iconMap, Rewired.Player player, Rewired.Controller controller, int actionId)
{
Rewired.InputAction action = Rewired.ReInput.mapping.GetAction(actionId);
foreach (var elementMap in player.controllers.maps.ElementMapsWithAction(controller, actionId, skipDisabledMaps: false))
{
var inputIcon = iconMap.GetInputIcon(elementMap, controller);
if (inputIcon != null)
inputIcons.Add(inputIcon);
else
Debug.LogWarning($"Failed to get input icon for action {action.name} with input id {elementMap.elementIdentifierId}");
}
}
// Fill input icons using just a controller map and an action, with no controller object
// This uses the default rewired gamepad template which most gamepads implement
// NOTE: Using this will bypass any player-specific mappings!
private void FillInputIcons(List<InputIcon> inputIcons, ControllerMapPair controllerIconMap, int actionId)
{
// Debug.Log($"Getting input icon for action {actionId} and controllerMap {controllerMap.Name}");
// Apply layout rule sets based on controller type, kind of a hack?
// This allows Switch to properly show the correct mapped buttons while no controller is connected
int layoutId = 0;
var ruleSets = Rewired.ReInput.players.GetPlayer(0).controllers.maps.layoutManager.ruleSets;
foreach (var ruleSet in ruleSets)
{
foreach (var rule in ruleSet)
{
// Debug.Log($"Comparing rule with hardware id {rule.controllerSetSelector.hardwareTypeGuid.ToString()} to {controllerMap.ControllerGuid}");
if (rule.controllerSetSelector.hardwareTypeGuid.ToString() == controllerIconMap.ControllerGuid)
{
layoutId = rule.layoutId;
break;
}
}
}
// Now get the mapping for this controller template and layout
Rewired.InputAction action = Rewired.ReInput.mapping.GetAction(actionId);
Rewired.ControllerIdentifier controllerIdentifier = new Rewired.ControllerIdentifier();
controllerIdentifier.hardwareTypeGuid = new System.Guid(controllerIconMap.ControllerGuid);
controllerIdentifier.controllerType = Rewired.ControllerType.Joystick;
foreach (var mapCategory in Rewired.ReInput.mapping.MapCategories)
{
if (controllerIconMap.IconMap.UseTemplateIds)
{
var mapInstance = Rewired.ReInput.mapping.GetControllerTemplateMapInstance(Rewired.GamepadTemplate.typeGuid, mapCategory.id, layoutId);
if (mapInstance != null)
{
foreach (var actionElement in mapInstance.ElementMaps)
{
if (actionElement.actionId == actionId)
{
var inputIcon = controllerIconMap.IconMap.GetInputIcon(actionElement.elementIdentifierId, actionElement.actionId);
if (inputIcon != null)
{
inputIcons.Add(inputIcon);
}
}
}
}
}
else
{
var controllerMap = Rewired.ReInput.mapping.GetControllerMapInstance(controllerIdentifier, mapCategory.id, layoutId);
if (controllerMap != null)
{
foreach (var actionElement in controllerMap.AllMaps)
{
if (actionElement.actionId == actionId)
{
var inputIcon = controllerIconMap.IconMap.GetInputIcon(actionElement.elementIdentifierId, actionElement.actionId);
if (inputIcon != null)
{
inputIcons.Add(inputIcon);
}
}
}
}
}
}
}
}
using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
[System.Serializable]
public class InputIcon
{
public string Comment;
public int InputId;
public Sprite IconSprite;
public Color IconColor = Color.white;
public string IconLabel;
public Sprite IconLabelSprite;
public Color IconLabelColor = Color.white;
public Vector3 IconLabelOffset;
public bool FlipX;
public bool DisableMapping;
}
[CreateAssetMenu(fileName = "new-input-icon-mapping", menuName = "Input Icon Mapping")]
public class InputIconMapDefinition : ScriptableObject
{
public bool UseTemplateIds = true;
public InputIcon TiltIcon => _tiltIcon;
[SerializeField, UnityEngine.Serialization.FormerlySerializedAs("inputIcons")]
private InputIcon[] _inputIcons = null;
[SerializeField]
private ActionIconOverride[] _actionIconOverrides = null;
[SerializeField]
private InputIcon _tiltIcon = null;
#pragma warning disable CS0414
[SerializeField]
private Rewired.Data.Mapping.HardwareJoystickMap _hardwareMap = null;
#pragma warning restore CS0414
private static List<Rewired.ControllerTemplateElementTarget> _elementTargets = new List<Rewired.ControllerTemplateElementTarget>();
[System.Serializable]
private struct ActionIconOverride
{
[Rewired.ActionIdProperty(typeof(RewiredConsts.Action))]
public int ActionId;
public InputIcon InputIcon;
}
// Get the id of the corresponding input element from the controller's template definition, if it exists
public static int GetTemplatedInputId(Rewired.ActionElementMap elementMap)
{
Rewired.Controller controller = elementMap.controllerMap.controller;
int inputId = elementMap.elementIdentifierId;
if (controller.templateCount > 0)
{
_elementTargets.Clear();
controller.Templates[0].GetElementTargets(elementMap, _elementTargets);
if (_elementTargets.Count > 0)
{
inputId = _elementTargets[0].element.id;
}
}
return inputId;
}
public InputIcon GetInputIcon(Rewired.ActionElementMap elementMap, Rewired.Controller controller)
{
int inputId = UseTemplateIds ? GetTemplatedInputId(elementMap) : elementMap.elementIdentifierId;
return GetInputIcon(inputId, elementMap.actionId);
}
public InputIcon GetInputIcon(int inputId, int actionId)
{
if (_actionIconOverrides != null)
{
for (int i = 0; i < _actionIconOverrides.Length; ++i)
{
if (_actionIconOverrides[i].ActionId == actionId)
{
return _actionIconOverrides[i].InputIcon;
}
}
}
return GetInputIcon(inputId);
}
public InputIcon GetInputIcon(int inputId)
{
foreach (InputIcon inputIcon in _inputIcons)
{
if (inputIcon.InputId == inputId)
{
return inputIcon;
}
}
return null;
}
#if UNITY_EDITOR
[ContextMenu("Load From Hardware Map")]
private void LoadFromHardwareMap()
{
var oldInputIcons = _inputIcons;
UnityEditor.Undo.RecordObject(this, "Load from hardware map");
int index = 0;
_inputIcons = new InputIcon[_hardwareMap.elementIdentifierCount];
foreach (var elementIdentifier in _hardwareMap.ElementIdentifiers)
{
var icon = new InputIcon();
icon.InputId = elementIdentifier.id;
icon.Comment = elementIdentifier.name;
icon.IconColor = Color.white;
icon.IconLabelColor = Color.white;
_inputIcons[index] = icon;
++index;
}
for (int i = 0; i < oldInputIcons.Length && i < _inputIcons.Length; ++i)
{
if (oldInputIcons[i].InputId == _inputIcons[i].InputId)
{
_inputIcons[i].IconSprite = oldInputIcons[i].IconSprite;
_inputIcons[i].IconLabel = oldInputIcons[i].IconLabel;
_inputIcons[i].IconLabelSprite = oldInputIcons[i].IconLabelSprite;
}
}
UnityEditor.EditorUtility.SetDirty(this);
}
#endif
[ContextMenu("Generate Keyboard Map")]
private void GenerateKeyboardMap()
{
Rewired.Keyboard keyboard = Rewired.ReInput.controllers.Keyboard;
InputIcon firstIcon = _inputIcons[0];
_inputIcons = new InputIcon[keyboard.ElementIdentifiers.Count];
for (int i = 0; i < keyboard.ElementIdentifiers.Count; ++i)
{
var keyElement = keyboard.ElementIdentifiers[i];
InputIcon keyIcon = new InputIcon();
keyIcon.IconSprite = firstIcon.IconSprite;
keyIcon.IconColor = firstIcon.IconColor;
keyIcon.IconLabelColor = firstIcon.IconLabelColor;
keyIcon.Comment = keyElement.name.ToString();
keyIcon.IconLabel = keyElement.name;
keyIcon.InputId = keyElement.id;
_inputIcons[i] = keyIcon;
}
}
[ContextMenu("Apply Item 0 Icon Color")]
private void DebugAdjustColors()
{
for (int i = 1; i < _inputIcons.Length; ++i)
{
_inputIcons[i].IconColor = _inputIcons[0].IconColor;
}
}
[ContextMenu("Apply Item 0 Icon")]
private void DebugAdjustIcons()
{
for (int i = 1; i < _inputIcons.Length; ++i)
{
_inputIcons[i].IconSprite = _inputIcons[0].IconSprite;
}
}
}
@phosphoer
Copy link
Author

phosphoer commented Apr 12, 2019

This requires the Unity Asset: Rewired.

I use these assets to define glyphs for the different input devices my game supports. It works by storing a mapping of hardware input ID -> icon. You ask for an icon for a given Rewired action, I then find all the inputs mapped for it and return the set of icons. This approach means that even when controls are remapped by the user, or you change the mappings during development, the correct icons will be returned. If the input device has a template, I use the template input IDs rather than the hardware IDs, so my gamepad icon mapping applies to all gamepads that Rewired supports generically. You can see the hardware/template IDs by expanding the Debug menu in the inspector for the Rewired Input Manager at runtime.

I didn't include the code to display the icons, as that is pretty specific to my game's architecture, but pretty much you just need to display the graphic the returned InputIcon structure contains in some fashion. You could easily modify this to use some other type of visual for the icon instead of a Sprite, or even just use the name of the input and display the string.

@chaosmonger
Copy link

This requires the Unity Asset: Rewired.

I use these assets to define glyphs for the different input devices my game supports. It works by storing a mapping of hardware input ID -> icon. You ask for an icon for a given Rewired action, I then find all the inputs mapped for it and return the set of icons. This approach means that even when controls are remapped by the user, or you change the mappings during development, the correct icons will be returned. If the input device has a template, I use the template input IDs rather than the hardware IDs, so my gamepad icon mapping applies to all gamepads that Rewired supports generically. You can see the hardware/template IDs by expanding the Debug menu in the inspector for the Rewired Input Manager at runtime.

I didn't include the code to display the icons, as that is pretty specific to my game's architecture, but pretty much you just need to display the graphic the returned InputIcon structure contains in some fashion. You could easily modify this to use some other type of visual for the icon instead of a Sprite, or even just use the name of the input and display the string.

Currently, I use just a generic set of icons for all types of gamepads, as you can see by the single InputIconMapDefinition entry for gamepad. It would be pretty easy to make overrides for specific gamepads by storing a mapping of rewired controller/template IDs to InputIconMapDefinitions and falling back to the generic template when necessary.

Hi, I'm approaching this and I've a couple of questions...
1: How can I create the icon set for Keyboard? What's the elementID of a keyboard key?
2: How can I display only positive/negative icons? For instance, if I want to show the right direction button (which could be "D" icon on keyboard, and "stick-to-the-right" icon on gamepad? Considering axis like Horizontal/Vertical relies on positive/negative values.
3: I'm not sure I understand how can you link different gamepads... Let's say I want to create 5 icons sets: Xbox360, XboxOne, SonyDualShock4, SteamController, UnknownController(Default), how can I change your scripts to fit those?

Thanks a lot and sorry if some of the questions might sound noob...

@phosphoer
Copy link
Author

Hi @chaosmonger, sorry for the late reply I didn't see this notification till now. I've also updated the gist with my latest code (minus some project specific stuff), which may answer some of your questions.

1: How can I create the icon set for Keyboard? What's the elementID of a keyboard key?

Check out the helper function I added to the InputIconMapDefinition, basically I iterated over the Rewired keyboard controller elements and created all the entries programmatically. I then had to manually go in and fill in the icons and edit names where appropriate.

2: How can I display only positive/negative icons? For instance, if I want to show the right direction button (which could be "D" icon on keyboard, and "stick-to-the-right" icon on gamepad? Considering axis like Horizontal/Vertical relies on positive/negative values.

I think this may be easier in the new version of the code, but I think I didn't have a need for that case so it's explicitly exposed. You could probably add extra override methods for GetIconForInput that takes a direction and then use that to filter the results?

3: I'm not sure I understand how can you link different gamepads... Let's say I want to create 5 icons sets: Xbox360, XboxOne, SonyDualShock4, SteamController, UnknownController(Default), how can I change your scripts to fit those?

This is something I added in the newest version, as I mentioned above I just had a single generic gamepad entry before. Now I have a map of hardware IDs to controller maps. You can find out the GUID of the controller by digging through rewired's data files.

@Zilk
Copy link

Zilk commented Mar 13, 2023

I've just gotten into this script trying to add glyphs to the remapping we are adding. Keyboard is working great but all controller maps seem to get the wrong sprite assigned. I created the bases from the data available in Rewired to be able to assign the different sprites to Xbox/PS/Switch controllers etc but it seems like it gets the ids mixed up. If I assign a Triangle it gets the Circle for instance. Could it be that I have the hardware ids and the script uses the elementalIds? Anyway to translate these?

@phosphoer
Copy link
Author

I've just gotten into this script trying to add glyphs to the remapping we are adding. Keyboard is working great but all controller maps seem to get the wrong sprite assigned. I created the bases from the data available in Rewired to be able to assign the different sprites to Xbox/PS/Switch controllers etc but it seems like it gets the ids mixed up. If I assign a Triangle it gets the Circle for instance. Could it be that I have the hardware ids and the script uses the elementalIds? Anyway to translate these?

Check out the new version I just posted, I ran into a similar problem eventually and ended up having the input icon assets define whether they are using template IDs or hardware IDs. The old script always used template IDs if available, with the new one you can uncheck that box to always use hardware IDs.

@Zilk
Copy link

Zilk commented Mar 14, 2023

Thanks! I was kind of suspecting something like that was the issue, got it working by just using the hardware id but then keyboard started acting out instead. This will solve it, thanks again!

@spireggs
Copy link

Hey, thanks for the script! I was wondering how you handled Unknown controllers with these scriptable objects. Any tips?

@phosphoer
Copy link
Author

Hey, thanks for the script! I was wondering how you handled Unknown controllers with these scriptable objects. Any tips?

Currently, I do not handle them at all which hasn't yet been a problem (afaik) with a shipped game. I did just updated the gist with a small refactor that should make adding this easier though.

I'm guessing you are talking about the case where the controller is completely unknown to Rewired and how to show icons for it? I think I'd approach that by making an InputIconMapDefinition asset with generic icons for up to N buttons (e.g., 'Button1', 'Button2'), and then adding a special case in ControllerIconMapDefinition.cs in GetMapForHardware() which returns that asset when the Rewired.Controller is of an unknown type.

Hope that helps!

@Zilk
Copy link

Zilk commented Jun 8, 2023

Bumped into another issue with this on iOS. Seems like on iOS it returns odd sprites but on Android it works as intended. Could it be because of the iOS MFI? Got any ideas?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment