12

Trying to repeat the function function OnAttack() continuously while a button is being held down.

Basically I'm looking for an equivalent to Update() { GetKeyDown() {//code }} But with the input system.

Edit: using a joystick, cant tell what button is being pressed.

leonheess
  • 16,068
  • 14
  • 77
  • 112
Sabishī
  • 373
  • 1
  • 2
  • 11
  • you can tell if a key is down, you can then use a normal unity idea of set a value += delay between repeats, if >= maxwait then set value to 0 and do thing – BugFinder Jan 21 '20 at 09:14

7 Answers7

14

Okay I solved it by using "press" in the interactions and giving that The trigger behavior "Press and release", then did

bool held = false;
Update()
{
    if(held)
    {
        //animation
    }
    else if(!held)
    {
        //idle animation
     }
}
OnAttack() {
    held = !held;
}

This way if I press the button held goes to true so it repeats the animation every frame, letting go makes "held" untrue and does the idle animation

Jérémie Bertrand
  • 3,025
  • 3
  • 44
  • 53
Sabishī
  • 373
  • 1
  • 2
  • 11
  • I would recommend using the `CallbackContext.ReadValueAsButton` which is passed to your `Performed` function to determine if the value is pressed. This could avoid potential bugs with `held = !held` from weird IO reads – Felipe May 31 '22 at 14:00
6

Essentially, the function you assign to the button will be triggered twice per button press, once when it is pressed (performed), and once when it is released (canceled). You can pass in this context at the beginning of your function, just make sure you are using the library seen at the top of this script. Now you can toggle a bool on and off stating whether or not the button is pressed, then perform actions during update dependent on the state of the bool

using static UnityEngine.InputSystem.InputAction;

bool held = false;
Update()
{
    if(held)
    {
        //Do hold action like shooting or whatever
    }
    else if(!held)
    {
        //do alternatice action. Not Else if required if no alternative action
     }
}
//switch the status of held based on whether the button is being pressed or released. OnAttack is called every time the button is pressed and every time it is released, the if statements are what determine which of those two is currently happening.
OnAttack(CallbackContext ctx) {
    if (ctx.performed)
            held= true;
    if (ctx.canceled)
            held= false;
} 
  • There is no cancelled callback available for press and release interactions, according to the docs: https://docs.unity3d.com/Packages/com.unity.inputsystem@1.0/manual/Interactions.html#press – GrayedFox Dec 14 '20 at 16:18
  • 1
    Thanks ! You saved my day @Oliver Philbrick ! I used this for move my player with WASD. And for keep press key for move. – Echo Dec 26 '20 at 14:46
2

This is paraphrasing a solution I created for a click to move arpg mechanic.

using System.Threading.Tasks;
using UnityEngine;

[SerializeField] private InputAction pointerClickAction;

private bool pointerHeld;

void Start()
{
    
    pointerClickAction.canceled += ClickMouseMove;
    pointerClickAction.started += PointerHoldBegin;
    pointerClickAction.performed += ClickMouseMove;
    pointerClickAction.canceled += PointerHoldEnd;
    
}

private void OnEnable()
{
    pointerClickAction.Enable();
    pointerPositionAction.Enable();
}

private void OnDisable()
{
    pointerClickAction.Disable();
    pointerPositionAction.Disable();
}

public async void ClickMouseMove(InputAction.CallbackContext context)
{
    while (pointerHeld)
    {
        DoSomething();

        await Task.Delay(500);
    }

}

public void PointerHoldBegin(InputAction.CallbackContext context)
{
    pointerHeld = true;
}

public void PointerHoldEnd(InputAction.CallbackContext context)
{
    pointerHeld = false;
}

public void DoSomething()
{
    //Your Code
}

In Task.Delay() you can insert your own polling rate in milliseconds, using Task.Yield() seems to be faster than Update so I don't recommend that, you should poll at the minimum the same delay as your physics/fixed update, having a higher delay gives a performance boost if you don't need a high amount of repetitions per loop. I set mine to 500 since I don't need my character to plot its navigation that often. In regards to TC, you would set the delay to something sensible e.g the attack's animation length, or whatever the delay rate would be for how many attacks can be performed per second.

Grigory Zhadko
  • 1,484
  • 1
  • 19
  • 33
1

If you want your project to scale you might want to avoid as much as possible assertions(i.e if functions) in your Update/FixedUpdate/LateUpdate functions as they are executed constantly. I recommand you to read this article about coroutines https://gamedevbeginner.com/coroutines-in-unity-when-and-how-to-use-them/

You can build coroutines which act as local update() functions which are executed only when needed. This will lead you to a better organization of your code and might boost performance in some cases.

For exemple in your case you could use something like this.

bool held = false;

Update()
{
    /* Whatever you want but the least assertion possible */
}

IEnumerator RenderHeldAnimation()
{
    while (held)
    {
        // held animation 
        yield return new WaitForFixedUpdate(); /* Will block until next fixed frame right after FixedUpdate() function */
        // yield return null /* Will block until next next frame right after Update() function */
    }
}

IEnumerator RenderIdleAnimation()
{
    while (!held)
    {
        // idle animation 
        yield return new WaitForFixedUpdate(); /* Will block until next fixed frame right after FixedUpdate() function */
        // yield return null /* Will block until next next frame right after Update() function */
    }
}

OnAttack() {
    held = !held;
    if (held) {
        StartCoroutine(RenderHeldAnimation());
    } else {
        StartCoroutine(RenderIdleAnimation());
    }
}
osbor
  • 51
  • 2
1

As mentioned in another answer, the context.canceled is not called when using the Press And Release interaction. As a follow up for documentation purposes as this is a top Google result, to correctly use a bool held without doing a blind toggle (held = !held) which may end up with drift, you can access context.control.IsPressed() like the following:

void OnAttack(CallbackContext context)
{
    held = context.control.IsPressed();
}
NibblesMK
  • 21
  • 3
1

I encountered the same issue and this was the method that seemed to work for me

private float _moveSpeed = 3f;
private float _moveDirection;

private void Update()
{
    transform.Translate(_moveSpeed * _moveDirection * Time.deltaTime * transform.forward);
}

public void Move(InputAction.CallbackContext ctx)
{
    _moveDirection = ctx.ReadValue<float>();
}

For some odd reason,the hold interaction works properly in reading the input, but I still need the update function to implement the actual logic.

Can't really complain though, it works. Although I'd love to know why it happens this way.

Hopefully this can be of help to someone.

-1

You can use a timer for that purpose, in combination with events KeyUp and KeyDown. Please look at the following link. It is pretty much similar to your problem. Link

Nenad
  • 119
  • 1
  • 12