Assigning different buttons click events to the same function with a parameter dynamically - unity3d

I've faced with the problem and been trying to solve it for almost an hour. I'm sharing this just in case anyone may face with the same problem. To explain the question and answer more clearly here is an example:
1) Let's say you create some button objects dynamically and add pile them up in a List:
private void CreateButtons(int length)
{
for (int i = 0; i < length; i++)
{
var newButton = Instantiate(buttonPrefab);
buttonList.Add(newButton);
}
}
2) Then you want to assign same function to different buttons but with different parameters:
Here is the assigned method:
private void Test(int a)
{
print(a);
}
And here is the assigning loop:
private void AssignClickEvents()
{
for (int i = 0; i < buttonList.Count; i++)
{
buttonList[i].GetComponent<Button>().onClick.AddListener(() => { Test(i); });
}
}
The problem with the above code is that when a button is clicked it won't give you 0,1,2... etc. All buttons will give you the same value which is last assigned value of loop parameter 'i'. Check answer for solution:

I don't know the exact reason behind this but to get things work you need to use a local variable for function parameter. Here is the code:
private void AssignClickEvents()
{
for (int i = 0; i < buttonList.Count; i++)
{
int a = i;
buttonList[i].GetComponent<Button>().onClick.AddListener(() => { Test(a); });
}
}
I hope it helps!

Related

How to does constructors in provider works?

class Brain with ChangeNotifier {
List<List<IconData>> icon = [];
Brain() {
for (int i = 0; i < 10; i++) {
List<IconData> temp = [];
for (int j = 0; j < 10; j++) {
temp.add(Icons.square_outlined);
}
icon.add(temp);
}
[...]
}
}
After some testing it seems like provider is calling it's constructor every widget state, but my icon list doesn't reset after changing some of the icons. How is it possible?
Well you need to use notifyListeners(); when you need to refresh your Ui, use it at the end of your Brain constructor after your process finished and see the result
Brain() {
...
notifyListeners();
}
Its better to use a function outside the constructor and use that, the constructor will create once when you provide the provider,

How to use selection sort in objects and classes

I'm creating two classes called stop watch and random numbers, which I have already done, but I needed to create a test program that would measure the execution time of sorting 100,000 numbers using selection sort. I know how to create a selection sort, I just don't know how to take the random numbers class and put it together with the selection sort, I get the error message "incompatible types random numbers cannot be converted to int" I hope someone can help me.
My random numbers class
import java.util.Random;
public class randomnumbers {
Random ran1 = new Random();
private int size;
public randomnumbers(){
size = 100000;
}
public int getSize(){
return size;
}
public void setSize(int newSize){
size = newSize;
}
public int [] createArray(int [] size){
for (int i = 0; i < size.length; i++){
size[i] = ran1.nextInt();
}
return size;
}
public static void printArray (int [] array){
for (int i = 0; i < array.length; i++){
if (i < 0){
System.out.println(array[i] + " ");
}
}
}
}
My test Program
public static void main (String [] args){
// Create a StopWatch object
StopWatch timer = new StopWatch();
//create random numbers
randomnumbers numbers = new randomnumbers();
//Create the size of the array
numbers.getSize();
// Invoke the start method in StopWatch class
timer.start();
//sort random numbers
selectionSort();
// Invoke the stop method in StopWatch class
timer.stop();
// Display the execution time
System.out.println("The execution time for sorting 100,000 " +
"numbers using selection sort: " + timer.getElapsedTime() +
" milliseconds");
}
// selectionSort performs a selection sort on an array
public static void selectionSort(int[] array) {
for (int i = 0; i < array.length - 1; i++) {
int min = array[i];
int minIndex = i;
for (int j = i + 1; j < array.length; j++) {
if (array[j] < min) {
min = array[j];
minIndex = j;
}
}
if (i != minIndex) {
array[minIndex] = array[i];
array[i] = min;
}
}
}
}
Where exactly are you getting "incompatible types random numbers cannot be converted to int" error?
There are multiple issues with the code:
Unconventional naming
size field is in randomnumbers class is used as actual array size in constructor but in createArray it's overshadowed with a parameter of the same name but different type and meaning.
You are not passing any array to selectionSort in Main. This is where I get compile error on your code.
printArray has if (i < 0) condition that is false for all ran1.nextInt() numbers so it will not print anything.
Feeding selectionSort with numbers.createArray(new int[numbers.getSize()]) compiles and ends up sorting the array.

C# Instances share same values

programming student here, quite new to what i am about to ask but i'm sure you people will know. I have to make a game where several pictureboxes are created using an array. I also have to make a class that has a health variable of 5. When you click on one of the pictureboxes, its health has to go down by 1. I am as far as this, but the problem is that the health variable is shared by all pictureboxes, where in reality I want every picturebox to have it's own health.
This is my code:
public partial class Form1 : Form
{
Invader monster; // Invader is the name of the class
Random rand = new Random();
PictureBox[] pb = new PictureBox[5];
private void Spawner()
{
for (int i = 0; i < 5; i++)
{
this.monster = new Invader();
this.pb[i] = new PictureBox();
this.pb[i].Name = "pb" + i.ToString();
this.pb[i].Location = new Point(rand.Next(10, 300), monster.LocY);
this.pb[i].BackgroundImageLayout = ImageLayout.Stretch;
this.pb[i].BackgroundImage = Image.FromFile(#"Path");
this.pb[i].BackColor = Color.Transparent;
this.pb[i].Size = new System.Drawing.Size(40, 30);
this.Controls.Add(this.pb[i]);
this.pb[i].Click += this.Form1_Click;
}
}
private void Form1_Click(object sender, EventArgs e)
{
PictureBox currentpicturebox = (PictureBox)sender;
this.monster.HealthDown();
if (this.monster.Health == 0)
{
currentpicturebox.Dispose();
}
}
and my class:
class Invader
{
// Fields
private int health;
// Properties
public int Health
{
get { return this.health; }
}
// Constructor
public Invader()
{
this.health = 5;
}
// Methods
public void HealthDown()
{
this.health -= 1;
}
Lets say i click 1 picture box 4 times, and click another one 1 time. With this code the picturebox last clicked on will be disposed. Any ideas on how to fix this?
Your Invader monster is an instance variable of Form1 and in your method Spawner() inside the for loop you are reassigning it again every time: this.monster = new Invader();
Basically when you click on a picturebox (isn't different what) in your Form1_Click method happen that is everytime your last monster istance that get it's health down and not the supposed one.
In order to fix this you can:
transform monster in an array of Invader object instead of an Invader object, the number of elements must be the same as the number of pictureboxes
foreach picturebox assign as a Tag the index of the corrispondent Invader on the monster array
Here an example:
public partial class Form1 : Form
{
// EDIT - Become an array
Invader[] monster = new Invader[5]; // Invader is the name of the class
Random rand = new Random();
PictureBox[] pb = new PictureBox[5];
private void Spawner()
{
for (int i = 0; i < 5; i++)
{
this.monster[i] = new Invader(); // EDIT
this.pb[i] = new PictureBox();
this.pb[i].Name = "pb" + i.ToString();
this.pb[i].Location = new Point(rand.Next(10, 300), monster.LocY);
this.pb[i].BackgroundImageLayout = ImageLayout.Stretch;
this.pb[i].BackgroundImage = Image.FromFile(#"Path");
this.pb[i].BackColor = Color.Transparent;
this.pb[i].Size = new System.Drawing.Size(40, 30);
this.Controls.Add(this.pb[i]);
this.pb[i].Click += this.Form1_Click;
this.pb[i].Tag = i; // EDIT - Added tag assignation
}
}
private void Form1_Click(object sender, EventArgs e)
{
PictureBox currentpicturebox = (PictureBox)sender;
this.monster[(int)currentpicturebox.Tag].HealthDown(); // EDIT
if (this.monster[(int)currentpicturebox.Tag].Health == 0) //EDIT
{
currentpicturebox.Dispose();
}
}

How to add value on button click function? [duplicate]

This question already has answers here:
How to detect click/touch events on UI and GameObjects
(4 answers)
Closed 5 years ago.
In Unity I have the following code:
public Button[] SearchPlayers;
void Start()
{
for(int i = 0; i < 10; i++)
{
// SearchPlayers[i].onClick.AddListener(Click(i));//here error
SearchPlayers[i].onClick.AddListener(Click);//not error
}
}
void Click(int i)
{
print(i+":button was clicked")
}
The problem is if you see my code there is an integer i. So that value gives an error.How can I solve this error?
The problem is the context. Calling Click(i) returns void. AddListener does not accept void.
You could solve this problem by creating an appropriate context with which to provide the argument.
public Button[] SearchPlayers;
void Start()
{
for(int i = 0; i < 10; i++)
{
//Cache the value
int index = i;
SearchPlayers[i].onClick.AddListener(() => Click(index));
}
}
void Click(int i)
{
print(i+":button was clicked")
}

Processing Code does not work

I started programming with Processing today and wrote a little programm that creates 10 random rectangles
Now I like to make them disappear when the mouse is over them, but my actual code is not working
I would apprechiate some tipps ...
import java.awt.Rectangle;
Rectangle rect[] = new Rectangle[10];
int xpos[] = new int[10];
int ypos[] = new int[10];
int size = 25;
boolean visible[] = new boolean[10];
void setup()
{
size(640,480);
frameRate(60);
smooth();
background(0);
stroke(255);
fill(255);
textAlign(CENTER);
textSize(200);
text("Catch", width/2, 280);
textSize(100);
text("them", width/2, 380);
// 10 Random positions for the rectangles
for (int i=0; i < 10; i++) {
xpos[i] = int(random (615));
ypos[i] = int(random (455));
visible[i] = true;
}
for (int i=0; i < 10; i++) {
rect[i] = new Rectangle(xpos[i],ypos[i],size,size);
}
}
void draw()
{
for (int i=0; i < 10; i++) {
if (visible[i] == true){
fill(255,0,0);
rect(rect[i].x,rect[i].y,rect[i].width,rect[i].height);}
else if (rect[i].contains(mouseX,mouseY)){
visible[i] = false; }
}}
Why else if? The way it is written, it will only check to see if the mouse is over a rect if visible[i] == false. They are all visible so it never gets executed.
Also to see the effect, you must call background(0); at the top of your draw method. Otherwise you never clear the screen to see the results.
You should also consider cleaning up your indentation and braces {} to make sure you are formatting the code in a consistent way. That would make it easier to read.