[How To] Unbind an action from the input component

The Input Component offers us only one way of removing an action binding: by index.

Here’s the naive (and buggy) way to do this
[SPOILER]
I put it in a spoiler so people just trying to find the answer don’t copy and paste this without reading, because this does not work.



InputComponent->BindAction("MyAction", IE_Pressed, this, &AMyActor::MyFunction);
int32 bindingIndex = InputComponent->GetNumActionBindings() - 1;


Because we just added the binding, naturally, its index will be the last index in the bindings array, so we just get the length of the array and subtract 1 from it. So far so good.
Now to remove the binding, we would do:



InputComponent->RemoveActionBinding(bindingIndex);


This “works”. The problem is: if another binding gets removed before we remove our binding, our binding’s index will be shifted by -1, so we will actually end up removing the wrong binding.

PS: I also have previously tried to locate it by storing a pointer to the created FInputActionBinding, but this will lead to problems because the way dynamic arrays work, when an element is removed the others are shifted in memory so you might end up having an invalid pointer or pointing to a different element.
[/SPOILER]
The correct way to do it
So to circumvent this problem we do this:




FInputActionBinding myActionBinding; //This should be declared in your class!




myActionBinding = FInputActionBinding("MyAction", IE_Pressed);
myActionBinding.ActionDelegate.BindDelegate(this, &AMyActor::MyFunction);

&InputComponent->AddActionBinding(myActionBinding);


and to remove it, we loop through all bindings in our input component until we find a binding that has the same action name and same delegate as the one we’re looking for:



for (int i = 0; i < InputComponent->GetNumActionBindings(); i++)
{
   FInputActionBinding binding = InputComponent->GetActionBinding(i);
   if (CompareInputActionBindings(binding, myActionBinding))
   {
      playerController->InputComponent->RemoveActionBinding(i);
      i--;
      continue;
    }
 }


Here’s the function I used to compare two FInputActionBindings



bool CompareInputActionBindings(FInputActionBinding lhs, FInputActionBinding rhs)
{
    return lhs.ActionDelegate.GetDelegateForManualSet().GetHandle() == rhs.ActionDelegate.GetDelegateForManualSet().GetHandle() &&
        lhs.GetActionName() == rhs.GetActionName();
}


I hope to have helped someone

4 Likes

Actually your second example has a similar and even worse problem than the first. Since you are storing a pointer to the FInputActionBinding, but the actual value pointed to is in the list of action bindings. When an element is removed from the list, it may (and probably will) shift elements in memory to fill the now free space in the list, thereby making you pointer invalid (point to the wrong thing, or invalid memory). Generally it is very unsafe to store a reference or pointer to an element in contained in any kind of dynamic memory container such as a list. When elements are added to the list, the list may allocate entirely new memory for the entire list and move everything over (this is how dynamic memory works), so in this case all your pointers would become invalid and not just some. This creates extremely difficult to debug memory errors that don’t seem to make sense.

Oh God. When I wrote this post I had no idea about this (I just learned about a week ago how dynamic arrays work). You probably saved me and a bunch other people from a lot of headaches, thank you!
I guess I’ll have to find another way of removing a binding from the input component… If I find another way to do it I’ll update my post.

Yeah, I couldn’t really find a good way to do it. Personally I’m just binding it then putting a Boolean in the callback for when it shouldn’t be called. You can also compare the action name if you really want to remove it. If you have multiple callbacks bound to the same action name however, this might not be what you want.

Ok I figured it out (it was pretty easy actually). Here’s how I did it:

Instead of storing a pointer to the FInputActionBinding returned from AddActionBinding, I stored the FInputActionBinding and then searched for it in the loop using a comparison. I had to make my own comparison function for FInputActionBinding and had to do some digging in the engine code to find out how to compare two delegates, but I eventually found a way to do it. I’ve updated the original post with the code and everything.

Again, thanks a LOT for pointing this out. :slight_smile:

This is awesome! I had a lot of callback,wrapper, and boolean checks to disable a bunch of input. I think this is much more efficient and cleaner. You could just remove it from the Input Binding when you don’t need it. Do you also know how to handle Input Axis Binding? It seems different from the action bindings.

Thank you very much! :slight_smile:

when you remove a certain input and binding it back again won’t work anymore. so I guess I go back to boolean checks and callbacks. I hope unreal fixes this where we can bind and unbind input dynamically. It makes the code much neater that way. although binding axis action is much easier. You just add and remove from the InputComponent->AxisBinding array. Upon adding it to the array it automatically overrides the input of the character.

As an update for anyone looking, You don’t need to loop through the action bindings anymore to unbind, you can just do:


InputComponent->RemoveActionBindingForHandle(myActionBinding.GetHandle());

For this approach to work you need to assign the return value of AddActionBinding:

myActionBinding = &InputComponent->AddActionBinding(myActionBinding);