How to treat Input.GetAxis as Input.GetButtonDown?

The triggers on XBox controller are axis, and when switching between mouse triggers and XBox, they do not behave the same way, because the XBox triggers are basically Input.GetButton, which causes the firing of weapons to shoot repeatedly.

I tried Input.GetAxisRaw and nothing changed.

With the GetAxisRaw function you get either 0 or 1/-1 (depending on axis). So it does work like the GetButton function. If you want it to work as the GetButtonDown function, you need to add little more code.

bool m_isAxisInUse = false;
  
void Update()
{
    if( Input.GetAxisRaw("Fire1") != 0)
    {
        if(m_isAxisInUse == false)
        {
            // Call your event function here.
            m_isAxisInUse = true;
        }
    }
    if( Input.GetAxisRaw("Fire1") == 0)
    {
        m_isAxisInUse = false;
    }    
}

In this very simple example, you see that the event function will be called once,
eventhough the axis-button keeps being pressed down. And when the axis-button is no longer pressed, the check gets restored, so that next input it will fire your event function again. I hope this example helps you.

Good luck!

I wasn’t satisfied with this answer, so I devised this solution which I think is cleaner and modular and uses event & delegate:

//left trigger
bool _lt;
public delegate void LeftTrigger();
public event LeftTrigger onLeftTrigger;
  
void Update ()
{
    _lt = Input.GetAxis("360_Triggers")<0f;
    if( _lt && onLeftTrigger != null )
        onLeftTrigger();
}

I saw both answers recently, but wasn’t happy with any of the given answers, in the documentation I found the value anyKeyDown

So I managed to get a cleaner solution for me:

void Update()
{
    if( Input.anyKeyDown )
    {
        float horizontal = -Input.GetAxis("Horizontal");
    }
}

I was not so happy with those solutions, and I think that could exist one that is clear to read and easy to reuse. So I did the following code:

using UnityEngine;
  
public abstract class BaseJoystickInputs : ScriptableObject
{  
    bool _lastInputAxisState;
  
    /// <summary>
    /// Gets the axis input like an on key down event, returning <c>true</c> only 
    /// on the first press, after this return <c>false</c> until the next press. 
    /// Only works for axis between 0 (zero) to 1 (one).
    /// </summary>
    /// <param name="axisName">Axis name configured on input manager.</param>
    protected bool GetAxisInputLikeOnKeyDown(string axisName)
    {
        var currentInputValue = Input.GetAxis(axisName) > 0.1;
  
        // prevent keep returning true when axis still pressed.
        if (currentInputValue && _lastInputAxisState)
        {
            return false;
        }
  
        _lastInputAxisState = currentInputValue;
  
        return currentInputValue;
    }
}

Please let me know any doubts/suggestions.

You can create a custom class for this:

[System.Serializable]
public class InputProperty
{
    public string stringValue;
    public int indexValue;
  
    private bool InputDown { get { return Input.GetButtonDown(stringValue) || Input.GetAxisRaw(stringValue) != 0; } }
    private bool lastDownCheck = false;
    private bool lastUpCheck = true;
  
    public bool GetInput()
    {
        return InputDown;
    }
  
    public bool GetInputDown()
    {
        if (InputDown)
        {
            if (InputDown != lastDownCheck)
            {
                lastDownCheck = InputDown;
                return true;
            }
  
        }
        lastDownCheck = InputDown;
        return false;
    }
  
    public bool GetInputUp()
    {
        if (!InputDown)
        {
            if (!InputDown != lastUpCheck)
            {
                lastUpCheck = !InputDown;
                return true;
            }
  
        }
        lastUpCheck = !InputDown;
        return false;
    }
}
using System.Collections.Generic;
using UnityEngine;

public class MyInput
{
    private static Dictionary<string,float> lastDict = new Dictionary<string, float>();
    public enum Dir { POSITIVE, NEGATIVE }
    public static float wait = 0.4f; //min time to wait between returning true 400ms
    public static float switchvalue = 0.8f; //how deep do we need to press
    public static  bool repeat=true;

    //shortcuts
    public static bool GetAxisAsButtonNegative(string axisName) => GetAxisAsButton(axisName, Dir.POSITIVE);
    public static bool GetAxisAsButtonPositive(string axisName) => GetAxisAsButton(axisName, Dir.NEGATIVE);

    public static bool GetAxisAsButton(string axisName, Dir direction)
    {
        float value = Input.GetAxis(axisName);
        string key = axisName + "." + direction; //key for the dictionary
        float last = 0;
        lastDict.TryGetValue(key, out last); //last time we returned "true" for this query
        float dur = Time.time - last; //time since we returned "true" for this query
        //not pressed, reset wait time to be able to "press" this button repeatedly and faster as wait
        if (Mathf.Abs(value) < switchvalue/2f)
        {
            lastDict[key] = 0;
            return false;
        }
        //waiting time between pressed already passed (repeat mode) or button already had been released (no repeat mode)?
        if ((dur > wait && repeat) || (!repeat && last==0)) {
            //retrun true axis is in the matching direction
            if (direction == Dir.NEGATIVE && value < -switchvalue || direction == Dir.POSITIVE && value > switchvalue)
            {
                //save time
                lastDict[key] = Time.time;
                return true;
            }
        } else
        {
            //must still wait, call again in next loop
        }
        return false;
    }
}

@KelvinRodri How would you use it? (noob question I know)

I edited @CoderMasterMike’s reply to avoid stupid sensitivity issues the Axis button has :slight_smile:
Now the script will work only when the button is pushed all the way down. I achieved this by rounding the Input values using Mathf.Round(MyValue) :wink:

if (Mathf.Round(Input.GetAxisRaw("Fire1")) != 0)
{
    if (m_isAxisInUse == false)
    {
       // Call your event function here.
        m_isAxisInUse = true;
    }
}

if (Mathf.Round(Input.GetAxisRaw("Fire1")) == 0)
{
    m_isAxisInUse = false;
}

I have come with the same solution though I use this in a game object and read the public bools instead of reading the input axes.

Is like an input axis translator that will send a value only for the rest of the frame so it works as an Input.GetAxisDown().

You have to put it in a game object and from the player read the output of this script.
This works for a DPad PS4 controller on a windows machine.

It looks like in macOS and Linux we can read the input directly from the PS4 controller mapped as a button but I’m not sure about this, please let us know if someone has a better approach which do not rely in the approach of all the posts here. Cheers!

public class DPadManager : MonoBehaviour
{
    public int up_buttonDown;
    public int up_buton;
    public float up_axis; 
    [SerializeField] private bool up_chkDown;
  
    public int down_buttonDown;
    public int down_buton;
    public float down_axis; 
    [SerializeField] private bool down_chkDown;
  
    public int right_buttonDown;
    public int right_buton;
    public float right_axis; 
    [SerializeField] private bool right_chkDown;
  
    public int left_buttonDown;
    public int left_buton;
    public float left_axis; 
    [SerializeField] private bool left_chkDown;
  
    private void Update() {
        if(Input.GetAxis("vertical") > 0 && !up_chkDown) {
            up_chkDown = true;
            StartCoroutine(Up_ButtonDownCo());
            
        } else if(Input.GetAxis("vertical") == 0) {
            up_chkDown = false;
        }
  
        if(Input.GetAxis("vertical") < 0 && !down_chkDown) {
            down_chkDown = true;
            StartCoroutine(Down_ButtonDownCo());
            
        } else if(Input.GetAxis("vertical") == 0) {
            down_chkDown = false;
        }
  
        if(Input.GetAxis("horizontal") > 0 && !right_chkDown) {
            right_chkDown = true;
            StartCoroutine(Right_ButtonDownCo());
            
        } else if(Input.GetAxis("horizontal") == 0) {
            right_chkDown = false;
        }
  
        if(Input.GetAxis("horizontal") < 0 && !left_chkDown) {
            left_chkDown = true;
            StartCoroutine(Left_ButtonDownCo());
            
        } else if(Input.GetAxis("horizontal") == 0) {
            left_chkDown = false;
        }
  
    }
  
    IEnumerator Up_ButtonDownCo() {
        up_buttonDown = 1;
        yield return new WaitForEndOfFrame();
        up_buttonDown = 0;
    }
    IEnumerator Down_ButtonDownCo() {
        down_buttonDown = 1;
        yield return new WaitForEndOfFrame();
        down_buttonDown = 0;
    }
    IEnumerator Right_ButtonDownCo() {
        right_buttonDown = 1;
        yield return new WaitForEndOfFrame();
        right_buttonDown = 0;
    }
    IEnumerator Left_ButtonDownCo() {
        left_buttonDown = 1;
        yield return new WaitForEndOfFrame();
        left_buttonDown = 0;
    }
  
}

all these are disgusting decisions from unprofessional programmers - if you need stateless a step change in the axis, for example, gradually raise the muzzle of a tank or switch the gearbox on next speed… then don’t use axis reading - you have to read the additional trigger buttons Lt and Lb of the joystick because Unity can read them in two modes like axis GetAxis(9) and like buttons GetKeyDown(KeyCode.JoystickButton6) and JoystickButton8. but if you strongly want receive a long program :blush: then you must use additional state variable previouse_state and then do comapration (like proposed @KelvinRodri _lastInputAxisState) - but it’s called best crutches

@neopaul78 You’re necro-ing a super old thread with not much helpful info.

Especially when in 2024 we have the new Input-System that makes it trivial to treat the triggers as buttons.