Add Listener with arguments?

My first noob thought was that I could have call functions using a variable, (still not 100% sure if this is impossible). Say, I have Skill1() & Skill2() methods in Script A, and I try to call the two methods in a loop function in Script B.

public class ScriptA{
Skill1(){
//some thing
}
Skill2(){
//some thing
}}
public class ScriptB{
for(int X = 1;X<3;X++){
ScriptA.SkillX();
}
}

But say that is impossible. I could create another method,

Skill(int i){
if(i==0){
Skill1;
} elseif(i==1){
Skill2;}
}

The only problem is that I don’t know how to implement this into AddListener, since you can’t have void functions in AddListener when adding a parameter.

The reason I’m doing this is because I will have multiple “players” with different “skills” and don’t want to create “buttons” for every individual skill and player. Instead I would like to have 4-5 buttons and then when that player’s turn comes up, to add his skills to the buttons depending on how many skills he has.

My current code looks like a sort of hybrid, not knowing which side of the fence it’s on, stuck in some nether between two worlds.

    public void PlayerTurn_PickSkillHUD(int i){
        playerid = i;
        for (int x = 0; x < player[i].skillName.Count; x++) {
            SkillButtonText [x].text = player [i].skillName [x]; //skillName.Count == skill.Count
//I would like to see something along the lines of this go in this spot >>
//SkillButton[x].GetComponent<Button>().onClick.AddListener(player[i].Skillx);
 }
        SkillButton [0].GetComponent<Button> ().onClick.AddListener (player[i].Skill1);
        SkillButton [1].GetComponent<Button> ().onClick.AddListener (player[i].Skill2);
        PlayerTurn_SkillSelection (true);
    }

What is the job you’re attempting to accomplish?

What are these buttons, and what should be happening when they’re clicked?

Rather than describing abstractly the syntax of the code… describe abstractly the actual thing the code aught to result in.

1 Like

He wants to change the listeners on the buttons depending on which player’s turn it is.
So, maybe there are always x number of buttons, but 2 players. Instead of having x * 2 - is that right?

Does the “hybrid” work? It doesn’t look that bad. I didn’t fully understand the rest of the stuff you were talking about, at the beginning 2 examples, in particular. :slight_smile: Sorry.

Currently I have two buttons. When clicked, the buttons call a method, which is a skill.
But since you have to choose the enemy before the skill actually happens, I have the method send information about the ability chosen to a Battle script which opens buttons labeled “enemy 1”, “enemy 2” etc.

So if it is Player 1’s Turn, the _PickSkillHUD method should open an “x” number of buttons (based on his number of skills) and assign the players skill methods to each button, in a loop, from 1 to “x”.

The reason I’m using a loop is because Player 1 might have 2 skills, and Player 2 might have 3 skills.

But since I can’t have an array/ist of skill functions (either because it’s impossible or I don’t know how to do it yet). It’s hard for me to use the AddListener method.

I guess, I could use multiple “if” statements, but it looks redundant.

if(playerSkill.Length==1){
SkillButton [0].GetComponent<Button> ().onClick.AddListener (player[i].Skill1);
} else if(playerSkill.Length==2){
SkillButton [0].GetComponent<Button> ().onClick.AddListener (player[i].Skill1);
SkillButton [2].GetComponent<Button> ().onClick.AddListener (player[i].Skill2);
}

In the first two examples, what I was trying to say, is the magic “cure” for this would be some way to have a method/function “array” or “list”, and the code would look something like this… Pay attention to the part that says “.Skill[×]”. “Skill” is a function.

for(x=0;x<playerSkill.Count;x++){
SkillButton [x].GetComponent<Button> ().onClick.AddListener (player[i].Skill[x]);
}

The hybrid works for now, with my one player, but what if I add players with 3 or more skills, or even just 1 skill? I would get so much errors and warnings.

What you’re describing is possible - “delegate” is the magic word you should Google if you want more information - but I am not sure it’s really what you want in this specific situation.

If you used a delegate, here’s about what it’d look like:

public delegate void SkillDelegate();
public SkillDelegate[] mySkills;
public void Skill1() { Debug.Log("1"); }
public void Skill2() { Debug.Log("2"); }

void Start() {
mySkills = new SkillDelegate[1];
mySkills[0] = Skill1; //note the lack of parentheses

//execute all skills
for (int s=0; s<mySkills.Length; s++) {
mySkills[s](); //and there are the parentheses!
}
}

The reason I think this is not the best approach is because I think inheritance or interfaces would be better. Basically, if you do it this way, you only have the function reference. But in most games skills have more data about themselves than that; their name, how much energy they use, the icon for their button, their recharge time, etc. Inheritance lets your skills have all those things, and you can override a method to do the thing you’re describing here.

I just finished writing an explanation on inheritance for a card game here, and you should be able to use exactly the same concept for your skills, just replace “card” with “skill”.

The reason I don’t like the idea of inheritance is because I would need to create a gameObject for each skill or I would have to add the skill scripts individually onto my player gameObject, and then reference TO that script, which is an extra step.

If what you mean is to make a class, “Skill”, then make another class “Attack : Skill”, “Heal : Skill”.

Ok to simplify my problem RN…

SkillButton [s].GetComponent<Button> ().onClick.AddListener (player[i].Skill(s));

is giving me an error. because the Skill function has a parameter :frowning: I don’t want to assign each skill by hand (delegating) because while I might not have many players or skills, they will be changing often.

The reason I have a parameter set up:

    public void Skill(int i){
        battleHUD.PlayerTurn_PickEnemyHUD (i);
    }

I want SkillButton[0] to carry out Skill[0] when clicked.
I want SkillButton[1] to carry out Skill[1] when clicked.

Edit:

I just went ahead and made an array of type “Skill” to my player. This way I can add more skills in the inspector.

            SkillButton [0].GetComponent<Button> ().onClick.AddListener (player[i].mySkills[s].Skill);

So this is the magic i was looking for… Hm… I could have swore I tried this last night and it didn’t work…

button.onClick.AddListener(delegate{SomeMethodName(SomeObject);});

for (int s = 0; s < player.skillName.Count; ++s) {
SkillButtonText .text = player .skillName ;
SkillButton .GetComponent ().onClick.AddListener (delegate{player.Skill(s);});
Debug.Log (s.ToString ());
}
Button[0] should have been assigned Skill(0) etc…

But when I click either button, the integer woefully changes to 2??? Why two?

I’m getting desperate now, this is all I want my code to do → If button 0 is clicked, ah nvm

     SkillButton [s].GetComponent<Button> ().onClick.AddListener (abilityid=s);

Not necessarily. A skill doesn’t need to be a MonoBehaviour.

Often, you can either use plain normal classes or another good approach are ScriptableObjects. SOs are nice since they act as assets in your projects and can still be used in the editor (it’s a handy tool). Which basically means you can create a huge collection of skills as assets. A skill usually doesn’t change itself, so you’d be fine with one instance for each skill you may need.

It’s a common thing that people run into.
When you use lambdas / delegates this way, you’ll be capturing the variable that you use, not its value.
Every created delegate refers to the same variable (as you may now it from reference types) and thus reads the value, whatever might have been assigned to it after the lambda’s/delegate’s creation.

You can avoid this using another local variable in the loop that you assign the counter to.

oh that kind of makes sense, because it doesn’t actually read “s” until the button is clicked.

I just went ahead and avoided the whole situation by making preset methods like this.

    public void PickS1(){
        PlayerTurn_PickEnemyHUD (0);
    }
    public void PickS2(){
        PlayerTurn_PickEnemyHUD (1);
    }
    public void PickS3(){
        PlayerTurn_PickEnemyHUD (2);
    }

    public void PlayerTurn_PickEnemyHUD(int i){
        abilityid = i;
        PlayerTurn_SkillSelection (false);
        PlayerTurn_EnemySelection (true);
    }