I have a QuestionGenerator.cs script which at the start, creates a question that is a comparison between two randomly generated numbers that can be up to 3 digits. When the user clicks on the correct option button, another random question is generated.
Let's say the numbers are a
and b
. I pass a - b
and the array { "<", "=", ">" }
as the arguments to the method SetButtonListeners()
. The logic goes like this:
- When
a - b
is less than0
, the sign should be<
- When
a - b
is equal to0
, the sign should be=
- When
a - b
is greater than0
, the sign should be>
Actual question:
Every time SetButtonListeners()
is called, listeners are added to the button, hence when it's called the second time, a listener already exists to that button. I want to remove the previously added listener if there's any and then add the new listener. As a hack, I am currently removing all the listeners the button has
buttons[i].onClick.RemoveAllListeners();
How do I go with only removing the previously set button listener and not all the listeners the button has?
A minimal reproducible code to depict my problem:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class QuestionsGenerator : MonoBehaviour {
static public QuestionsGenerator instance { get => return s_Instance; }
static private QuestionsGenerator s_Instance;
public Text questionText;
public Button[] buttons; // length is 3
private void Awake() {
if (s_Instance == null) {
s_Instance = this;
} else {
Destroy(gameObject);
}
}
private void Start() {
GenerateQuestion();
}
private void GenerateQuestion() {
ComparisionUpto3Digits();
}
private void ComparisionUpto3Digits() {
int a = Random.Range(1, 1000);
int b = Random.Range(1, 1000);
questionText.text = a " ___ " b;
int ans = a - b;
string[] options = { "<", "=", ">" };
SetButtonListeners(ans, options);
}
private void SetButtonListeners(int answer, string[] options) {
// length of both buttons[] and options[] is the same
for (int i = 0; i < buttons.Length; i ) {
buttons[i].GetComponentInChildren<Text>().text = options[i];
// removing all listeners here
buttons[i].onClick.RemoveAllListeners();
if ((answer < 0 && i == 0) || (answer == 0 && i == 1) || (answer > 0 && i == 2)) {
buttons[i].onClick.AddListener(() => {
HandleCorrectAnswer();
});
} else {
buttons[i].onClick.AddListener(() => {
HandleWrongAnswer();
});
}
}
}
private void HandleCorrectAnswer() {
GenerateQuestion();
}
private void HandleWrongAnswer() {
Debug.Log("Game Over");
}
}
CodePudding user response:
Constantly adding new listeners does not seem like a good solution. Handle correct/wrong answer in the same listener instead by passing a different argument to the listening function. You can pass parameters like this: https://answers.unity.com/questions/1288510/buttononclickaddlistener-how-to-pass-parameter-or.html
EDIT: To be more precise, you can simply do something like this:
private int a,b;
private void Awake(){
for(int buttonIndex = 0; buttonIndex < buttons.Length; buttonIndex ){
int closureIndex = buttonIndex;
buttons[closureIndex].onClick.AddListener(delegate{HandleAnswer(closureIndex);});
}
}
private void HandleAnswer(int buttonIndex){
int answer = a-b;
if ((answer < 0 && buttonIndex == 0) || (answer == 0 && buttonIndex == 1) || (answer > 0 && buttonIndex == 2)) {
[good answer]
}else{
[wrong answer]
}
}
EDIT: Added closure index according to comment (https://answers.unity.com/questions/1376530/add-listeners-to-array-of-buttons.html?childToView=1376656#answer-1376656)