2
\$\begingroup\$

I have an input action in my project that is performed after being held for a length of time. By default it begins after 0.2 seconds, but I'm trying to add a slider to increase or decrease it in my options menu.

In other words, I want to get the float value of "Hold Time" for the Hold interaction of an InputAction. I could create the input action within a script and create its interactions there, but it's much easier to use the input actions editor in the engine to create the actions and access them in a script.

How do I take an InputAction created in the editor and get/modify its Hold Time float value?

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

You can get or even modify the HoldInteraction.duration using the InputAction.CallbackContext, like this:

void OnEnable() {
  _holdAction.started += OnHoldActionStarted;
  _holdAction.canceled += OnHoldActionCanceled;
}

void OnDisable() {
  _holdAction.started -= OnHoldActionStarted;
  _holdAction.canceled -= OnHoldActionCanceled;
}

protected void OnHoldActionStarted(InputAction.CallbackContext context) {
  var holdInteraction = context.interaction as HoldInteraction;
  holdInteraction.duration = 2f;
}

// ... restore the original hold time
\$\endgroup\$
0
\$\begingroup\$

The disadvantage of Charlie's answer is that you have to wait until the action has been performed to change the value - so you'd need to set the slider value to the hold duration you want, cache that value somewhere, then wait until the next time the control is held and inject the cached value there.

Unfortunately the input binding API is all string-based, but nothing a little regex can't tackle:

static readonly Regex HoldDurationFinder = new Regex(@"Hold(\([^\)]*?(duration=(\d+\.?\d*))?[^\)]*\))?", RegexOptions.Compiled);

public static bool TryGetHoldDuration(InputBinding binding, out float duration) {
    var interactions = binding.effectiveInteractions;

    var match = HoldDurationFinder.Match(interactions);

    if (!match.Success) {
        duration = -1;
        return false;
    }

    if (match.Groups[3].Success) {
        duration = float.Parse(match.Groups[3].Value);
    } else {
        duration = InputSystem.settings.defaultHoldTime;
    }

    return true;
}

public static string ModifyHoldDurationString(InputBinding binding, float duration, bool add = false) {
    var interactions = binding.effectiveInteractions;

    var match = HoldDurationFinder.Match(interactions);

    // Hold interaction with specified duration - change the duration value.
    if (match.Groups[3].Success) {
        var group = match.Groups[3];
        return $"{interactions.Substring(0, group.Index)}{duration}{interactions.Substring(group.Index + group.Length)}";
    }

    // Hold interaction with other parameters - insert duration at the front of the list.
    if (match.Groups[1].Success) {
        var group = match.Groups[1];
        return $"{interactions.Substring(0, group.Index)}(duration={duration},{interactions.Substring(group.Index + 1)}";
    }

    // Hold interaction with no parameters - replace with parametrized version.
    if (match.Success) {            
        var group = match.Groups[0];
        return $"{interactions.Substring(0, group.Index)}Hold(duration={duration}){interactions.Substring(group.Index + group.Length)}";
    }

    if (!add) return null;
    // No hold interaction - add one.
    string addition = $"Hold(duration={duration})";
    if (string.IsNullOrWhiteSpace(interactions))
        return addition;
    return $"{addition},{interactions}";
}

public static void ChangeAllHoldDurations(InputAction action, float duration) {
    var bindings = action.bindings;
    for (int i = 0; i < bindings.Count; i++) {
        var binding = bindings[i];
        var newInteractions = ModifyHoldDurationString(binding, duration);

        if (newInteractions != null) {
            binding.overrideInteractions = newInteractions;
            action.ApplyBindingOverride(i, binding);
        }
    }
}

This lets you fetch and manipulate the hold duration from the input action directly, even when the action is not being performed.

By using overrideInteractions, these modifications persist only in play mode, and the asset's configuration will revert to what you set up in the editor when you exit play mode - ensuring that changes you make in testing don't "leak" into the game's default settings.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .