Input Action Visualization
The (Base) Input Action Visualization can be referenced by Input Button Trigger, Navigation Group Switch Controller and Tab Switch Controller.
It's purpose is displaying a certain gamepad button when the user can press it to perform a direct action (can also be used for keyboard keys).
There is just an abstract base class and no real implementation, as the actual implementation relies on a number of factors (the used input system; the way, actions are mapped; the way sprites are managed; the sprites you want to use in your game).
To get you started, you can use the InputActionVisualization class from the Navigation Example and adjust it to your needs.
Please note that you would need correctly named sprites in a folder called "Resources" for the examples approach. But a better way would be some kind of sprite lookup in a scriptable object.
Here is the code from he example scene (please mind the comments):
using System.Collections.Generic;
using TheraBytes.BetterUi;
using UnityEditor;
using UnityEngine;
// Component to handle viszualizations of input buttons referenced by InputButonHandlers.
public class InputActionVisualization : BaseInputActionVisualization
{
// this image is representing the input-button graphic
[SerializeField] BetterImage image;
[Tooltip("An optional list of objects that should change their 'isActive' state along with the images visibility.")]
[SerializeField] List<GameObject> objectsToHide = new List<GameObject>();
// called by base class to set up the correct button-graphic.
// this is called whenever it becomes active again.
protected override void MapInputActionVisualizationLogic(InputActionType inputActionType)
{
// This is the most basic approach, which requires correctly named sprites in a Resources folder:
image.sprite = Resources.Load<Sprite>(inputActionType.ToString());
// A better approach would be some crazy conversion:
// InputActionType -> Mapped Action -> Button Binding -> Sprite lookup
//
// To get the mapped action, you can call one of these methods:
// --
// New Input System:
// UnityEngine.InputSystem.InputActionReference action = base.GetInputSystemActionReference(inputActionType);
//
// Legacy Input System
// string action = base.GetLegacyInputActionName(inputActionType);
//
// Rewired:
// Rewired.InputAction action = base.GetRewiredAction(inputActionType);
// or
// int actionId = base.GetRewiredActionId(inputActionType);
//
// Other Input Systems (integrated by you):
// T action = base.GetInputSystemActionType<T>(inputActionType);
//
// --
// With the mapped action you can lookup the actual button binding
// (please refer to the official documentation of the input system you are using).
//
// With the button binding you would need to lookup the corresponding sprite. This part would be your own logic.
// For consoles, you need to map different sprites per console.
}
// called by base class when the button-graphic should be shown or hidden
protected override void SetActivenessLogic(bool isCurrentlyActive)
{
// We enable / disable the image here.
// We could also set the images game object inactive, because the image is a child object.
// But be careful: If the image would be on the same game object as this component,
// it would not call Update() anymore when the game object becomes inactive.
this.image.enabled = isCurrentlyActive;
foreach (var obj in objectsToHide)
{
obj.SetActive(isCurrentlyActive);
}
}
// called by the base class to determine if the button-graphic should be shown currently
// Note that this is not the only source of truth: It is only shown when also the ButtonInteractionHandler allows it.
protected override bool IsAllowedToBeActive()
{
if (BetterNavigation.Current?.InputDetector != null) // prevent calling this too early
{
// only show when using gamepad.
return BetterNavigation.Current.InputDetector.LastInputWasGamepad;
}
return base.IsAllowedToBeActive();
}
// called every frame by unity
private void Update()
{
// Calling this in Update is maybe more than required.
// Often it is enough to create a callback when a change of input device was detected.
// However, calling this every frame makes sure hat the conditions of InputButtonTriggers are taken into account.
base.UpdateActiveness();
}
#if UNITY_EDITOR
// Validation called by Unity edior
private void OnValidate()
{
// the code below is just for convenience.
// It creates a child object with a BetterImage and assings it, if there is no image present.
if(image == null)
{
image = GetComponentInChildren<BetterImage>();
if (image == null)
{
// To prevent some warnings in the log, we do the creation at a time that unity likes.
EditorApplication.delayCall += () =>
{
var go = new GameObject("Visualization", typeof(RectTransform));
go.transform.SetParent(this.transform);
var rt = go.transform as RectTransform;
rt.ExpandToParentSize(true, true);
image = go.AddComponent<BetterImage>();
};
}
}
}
#endif
}