Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Workaround for button onClick

Discussion in 'Scripting' started by Nefisto, Oct 7, 2020.

  1. Nefisto

    Nefisto

    Joined:
    Sep 17, 2014
    Posts:
    324
    As far I know, we cannot insert methods with custom/multiple parameters in button on click or event triggers. For example, if I want to change some behavior with a button I need to create this behavior
    Code (CSharp):
    1. public void ChangeActionToDefense()
    2.     => currentAction = BatleActions.Defend;
    3. public void ChangeActionToCritical()
    4.     => currentAction = BatleActions.Critical;
    Instead of
    Code (CSharp):
    1. public void ChangeAction(BatleActions action)
    2.    => currentAction = action;
    Because the second one doesn't show by default, so a workaround that I usually do is getting ref to buttons and manually add listeners, for example
    Code (CSharp):
    1. .
    2. .
    3.    public Button defenseButton;
    4.    public Button criticalButton;
    5.  
    6.    private void Start()
    7.    {
    8.        defenseButton.onClick.AddListener(() => ChangeAction(BatleActions.Defend));
    9.        criticalButton.onClick.AddListener(() => ChangeAction(BatleActions.Critical));
    10.    }
    11. .
    12. .
    What I want is only to know what u guys think about it, does u suggest a different workaround?
     
  2. Meishin

    Meishin

    Joined:
    Apr 12, 2019
    Posts:
    26
    Hiho @Nefisto ! This is a valid method. However you'd usually use it to set up variable list of buttons, or if you're lazy :D (cause well, it adds some work on start).

    If you want to do similar (lazy) stuff directly in editor you can use stuff like those ;
    Code (CSharp):
    1. // Enter as a string, or an enum if you don't want any error
    2. public void ChangeActionButton(string stringToInputInButtonEditor)
    3. {
    4.     switch(stringToInputInButtonEditor)
    5.     {
    6.         case defense:
    7.             ChangeAction(BatleActions.Defend));
    8.             break;
    9.         case action:
    10.             ChangeAction(BatleActions.Critical);
    11.             break;
    12.         default:
    13.     }
    14. }
    15.  
    16. // If you're even more lazy :D use the button's name (name them as ButtonDefense, ButtonAction
    17. public void ChangeActionButton()
    18. {
    19.     switch(this.name)
    20.     {
    21.         case ButtonDefense:
    22.             ChangeAction(BatleActions.Defend));
    23.             break;
    24.         case ButtonAction:
    25.             ChangeAction(BatleActions.Critical);
    26.             break;
    27.         default:
    28.     }
    29. }
    Of course you could use Reflection but i'd encourage you not to (it's performance costly and not compatible with a lot of stuff)

    Lastly you can create a fake button on top (or even without if you dont want built in button UI) with a custon OnClick event taking your BattleAction directly as input :
    https://answers.unity.com/questions/1335277/how-to-make-a-custom-onclick-event.html
     
    Last edited: Oct 9, 2020
  3. Nefisto

    Nefisto

    Joined:
    Sep 17, 2014
    Posts:
    324
    Hi @Meishin, thanks for sharing ur ideas

    But this way will cause some problems IMO when the actor and buttons aren't the same. I don't feel good working with strings, any change in code can break flow and enum does not appear on the event, and in the second case we need a ref to the player, in this case, we'll create a hard connection.

    This is a nice solution, but creating a unique event for each custom method with diferent parameters is a costly path.

    It's only my opinions, but I'll try ur suggestions \o/
     
  4. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,833
    What you suggest in the OP is what I would usually do.

    The main alternative is that you accept a parameter of one of the few types that the Unity inspector specifically supports (int, float, string, MonoBehaviour) and then convert that into the type you actually want (which I'm assuming is an enum in this case). But as you already pointed out, this is unsafe in the sense that the inspector can't guarantee that the value you enter will be valid.

    You could also consider adding another component that serves as a level of indirection, like:
    Code (CSharp):
    1. public class ActionChanger : MonoBehaviour
    2. {
    3.     public BattleActions action;
    4.     public TypeWhoseActionYouWantToChange someOutsideReference;
    5.  
    6.     public void DoChange()
    7.     {
    8.         someOutsideReference.ChangeAction(action);
    9.     }
    10. }
    You can attach this component to the button, and configure the button to call DoChange when clicked, and then DoChange changes the action based on some public variables that you can edit through the inspector.

    Warning: When Unity serializes the enum "public BattleActions action", it does that by saving the integer value of the selected enum value. If you modify the definition of the enum in some way that changes which integer is assigned to which value, this reference will break! To avoid this, your enum definition should explicitly assign a number to each value, and you should never change those numbers even if you add/remove values.
     
    Nefisto and eses like this.
  5. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,756
    I'll take that one step further and say "explicitly assign strings," since if this is for UI, that will NEVER cause a performance bottleneck, and the debugging ease you create by having meaningful values serialized rather than "3" is priceless. The value only goes up as the code gets older and you forget wtf you were doing in the first place.
     
    Nefisto likes this.