Search Unity

How to debug unsuccessful unsubscribing from events

Discussion in 'Input System' started by RoyorGames, Jun 3, 2020.

  1. RoyorGames

    RoyorGames

    Joined:
    Mar 14, 2017
    Posts:
    25
    Hi =)

    I have a local multiplater setup, where you can control one of many cannons at a time.
    A player walks over to a cannon and interacts with it and passes its input class. The movement part of the input system is disabled and the cannon part is enabled. The Player can demount the cannon and go over to another cannon and mount it. However now both the old and the new cannon now responds to the input from the player.

    The following is used to deactivate the settings from the player movement controls and activating the cannon controls:

    Code (CSharp):
    1. public void GrabCannon(MasterInput input)
    2. {
    3.    _input = input;
    4.    _input.Cannon.Aim.started += context => IsAiming = true;
    5.    _input.Cannon.Aim.performed += context => SetAimAmount(context.ReadValue<Vector2>());
    6.    _input.Cannon.Aim.canceled += context => IsAiming = false;
    7.    _input.Cannon.Shoot.performed += context => LightCannon();
    8.    Invoke("SetupDisable", 0.1f);//Delayed as the same button is used to mount and demount the cannon.
    9.  
    10.    _input.Player.Disable();
    11.    _input.Cannon.Enable();
    12. }
    13.  
    14. private void SetupDisable()
    15. {
    16.   _input.Cannon.Exit.performed += context => Disable();
    17. }
    18.  

    The following is used when disabling the cannon controls
    Code (CSharp):
    1. public void Disable()
    2. {
    3.    print("unsubcribing"); //Is displayed in the log when testing and no errors afterwards
    4.  
    5.    _input.Cannon.Aim.started -= context => IsAiming = true;
    6.    _input.Cannon.Aim.performed -= context => SetAimAmount(context.ReadValue<Vector2>());
    7.    _input.Cannon.Aim.canceled -= context => IsAiming = false;
    8.    _input.Cannon.Shoot.performed -= context => LightCannon();
    9.    _input.Cannon.Exit.performed -= context => Disable();
    10.  
    11.    _input.Cannon.Disable();
    12.    _input.Player.Enable();
    13. }
    14.  

    I do not know how to investigate this problem further. Maybe it is something I do incorrectly is in the setup.
    Do anybody have any ideas on how to investigate further?

    Best regards Povl
     
  2. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    This won't quite work. To unsubscribe, you need to pass a C# delegate that compares equal to the one you used to register. Here, you are creating a new delegate in the right side expression.

    One way to fix this is by storing the delegates you create in GrabCannon in fields and then using the values of those fields when unsubscribing.
     
  3. RoyorGames

    RoyorGames

    Joined:
    Mar 14, 2017
    Posts:
    25
    Hi Rene-Damm

    Thank you very much.
    I tried the following and it worked =)

    General in the class
    Code (CSharp):
    1.  private Action<InputAction.CallbackContext> fireAction;
    In the subcribe part
    Code (CSharp):
    1.  fireAction = context => LightCannon();
    2.         _input.Cannon.Shoot.performed += fireAction;
    In unsubscribe part
    Code (CSharp):
    1.  _input.Cannon.Shoot.performed -= fireAction;
    I can understand why the old method did not work for the anonymous methods (e.g. context => IsAiming = false;) , but I am not quite certain with this type: context => LightCannon();
    My guess is they are also created as anonymous methods, even though I feel like they are not (might be some missing information on my part).

    Do you have a better way to manage the delegates than my idea? I will end up with five delegates on this controller alone.

    Best wishes and thank you for helping me =)

    Povl Eller
     
  4. It is because this basically stores the value of your anonymous function.
    I generally don't like this syntactic sugar, because it hides multiple method calls. But seems popular among people, so I'm just a grumpy old man with my closer-to-the-metal viewpoint. :)

    The thing is, when you subscribe/unsubscribe in this case, you subscribe/unsubscribe the fireAction, not the LightCannon or an anonymous function. That's why it works.

    I still like more the
    private void LightCannon(Context[whatever]...) {}
    and simply _input.Cannon.Shoot.performed [+|-]= LightCannon;

    You always can call the context.ReadValue inside your method, so much more clear this way and you never have the problem with the unsubscribe part.
     
    berdanka likes this.
  5. RoyorGames

    RoyorGames

    Joined:
    Mar 14, 2017
    Posts:
    25
    Hi Lurking-Ninja,

    I just tried your version and it also works!
    I think I will use this method, to avoid too many extra properties.
    Thank you =)
    And I don't find you grumpy =)

    For reference for others with the same problem
    Method that is subscribed/unsubscribed
    Code (CSharp):
    1.     public void SetAimAmount(InputAction.CallbackContext context)
    2.     {
    3.         _aimAmount = context.ReadValue<Vector2>();
    4.     }
    Subscribing
    Code (CSharp):
    1. _input.Cannon.Aim.performed += SetAimAmount;
    Unsubscribing
    Code (CSharp):
    1. _input.Cannon.Aim.performed -= SetAimAmount;