Search Unity

Sending a Ref on button click

Discussion in 'Scripting' started by khanstruct, Mar 28, 2015.

  1. khanstruct

    khanstruct

    Joined:
    Feb 11, 2011
    Posts:
    2,869
    I'm working on the code in my RPG that will deal with increasing the character's stats. When they level up, they are given points, and + and - buttons appear next to each trait. Clicking on these will increase or decrease the stat.

    Now, Unity's ButtonClick can only accept an int or string as an argument. I could just have it pass a string name for the stat, then run a switch/case to determine which stat its talking about:
    Code (CSharp):
    1. switch(statToChange):
    2. {
    3.     case "strength":
    4.     //change character.strength
    5.     break;
    6. }
    However, what I would like to do is pass a reference of the character's particular stat into the function.
    Code (CSharp):
    1. public void ChangeStat(ref int statToChange)
    2. {
    3.     //change character.statToChange
    4. }
    Is there some way I can do this? I've considered using the AddListener, rather than the simple buttonClick list, but I still can't see a way to pass the reference to the function. Halp!
     
  2. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    I would be quite surprised if an interface that only supports a few specific data types nonetheless supported a byVal/byRef distinction. But even supposing that it worked in principle, how would you specify the reference? If you're adding listeners to onClick using the Unity inspector, I'm pretty sure you can only specify the integer to send as a literal.

    And even if it did work, you wouldn't pass in "ref int statToChange" and then do something with "character.statToChange". The argument "statToChange" would need to already be a reference to a specific integer on a specific character.

    Have you considered storing the character's stats in an array, and specifying the stat to change using an array index? If you still want to be able to write "character.strength" in code, you could always create a property called "strength" that redirects to the array.
     
  3. khanstruct

    khanstruct

    Joined:
    Feb 11, 2011
    Posts:
    2,869
    I probably should just use an array. I just don't like how unreadable that would be (having to remember that character.Stat[0] is their strength. How would I redirect character.strength to an index?
     
  4. passerbycmc

    passerbycmc

    Joined:
    Feb 12, 2015
    Posts:
    1,741
    Why not a struct
     
  5. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    One way would be to add your own listener in the form of a script implementing the IPointerClickHandler interface. Then you could sent whatever parameter you felt like exposing.

    As to parameter types you can send a bunch of stuff. Most of your primitives work. Anything that inherits from UnityEngine.Object is fair game. That opens up to using pretty much anything you like. You are still limited to a single parameter with the default implementation.
     
  6. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    Something like this:

    Code (CSharp):
    1. using System;
    2.  
    3. public enum Stat
    4. {
    5.     Strength = 0,
    6.     Dexterity,
    7.     Intelligence
    8. }
    9.  
    10. public class Character
    11. {
    12.     int[] stats;
    13.  
    14.     public Character()
    15.     {
    16.         stats = new int[Enum.GetValues(Stat).Length];
    17.         strength = 5; // example initialization
    18.     }
    19.  
    20.     public int strength
    21.     {
    22.         get
    23.         {
    24.             return stats[Stat.Strength];
    25.         }
    26.         set
    27.         {
    28.             stats[Stat.Strength] = value;
    29.         }
    30.     }
    31.     public int dexterity
    32.     {
    33.         get
    34.         {
    35.             return stats[Stat.Dexterity];
    36.         }
    37.         set
    38.         {
    39.             stats[Stat.Dexterity] = value;
    40.         }
    41.     }
    42.     // etc.
    43. }
    If you're not familiar with properties, they're a kind of method that looks like a field. There's a Unity tutorial on them here (I have not watched it).
     
    khanstruct likes this.
  7. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    How would that help with the problem of making the button onClick event pass a parameter that indicates which stat should be affected?
     
  8. khanstruct

    khanstruct

    Joined:
    Feb 11, 2011
    Posts:
    2,869
    Alright, I took a slightly different approach. Do you foresee any issues with this?
    Code (CSharp):
    1. public int[] attribute = new int[7];
    2.  
    3. public int Strength
    4.     {
    5.         get { return attribute[0]; }
    6.         set { attribute[0] = value; }
    7.     }
     
  9. khanstruct

    khanstruct

    Joined:
    Feb 11, 2011
    Posts:
    2,869
    Apparently I was. I just didn't know that's what they were called. I really should get around to watching those Unity tutorials; lots of good stuff in there.

    Thanks!

    EDIT: Also, aren't enums assigned an int by default (0 through whatever). I seem to remember reading that somewhere... and then I forgot apparently, because that also would have worked.
     
  10. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    enums are assigned int values by default, but any time I care which values are assigned, I like to specify them explicitly just to make that clear. In my example above, it's important that they start at 0 because arrays are zero-indexed, so I wrote "= 0" after the first one just to clarify that I was relying on that.

    Your approach of putting a literal "0" in the strength property works too, but it will be slightly harder to change if you ever decide to add or remove attributes from your game. My example puts them in an enum, and then sets the size of the array based on the number of values in the enum, so that if you ever want to add or remove stats, you only need to change the definitions for the stats you're changing, and the compiler will automatically resize the array and renumber the other stats as necessary.

    Using an enum also means that if you want to pass an attribute index as a parameter--for example, if your abilities have a "relies on stat" field that could be set to strength, intelligence, or whatever--you can write that something depends on something like "Stat.Intelligence" rather than "3", which will hopefully make your code easier to understand.

    Of course, if you're setting an onClick callback in the inspector, you can't use an enum as the parameter you're passing (AFAIK), so for your original example of making a different UI button for each stat, you're probably going to end up using integer literals anyway. The enum only helps within your own code.
     
  11. khanstruct

    khanstruct

    Joined:
    Feb 11, 2011
    Posts:
    2,869
    True, but because I made my array and the property public, I can use either one in my code. So I'll still be able to say character.Strength, or character.attribute[0] (when I can only use integers, as with the case of buttons).

    I'll definitely have to remember the numbering of enums though, as that will be handy. So far, I've only been using them as easily labeled arrays.