C# Instances share same values - class

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();
}
}

Related

How to save boolean properly on Playerprefs?

I want to make a shop system if I buy the items, the items will be saved on Playerprefs, no need to buy again when I restart or re-open the game, for that I already make it to setInt but I don't know when I call getInt. I tried to call it in the start method with a new int index but it doesn't work. This my script
for store the itemsType
[System.Serializable]
public class m_ShopItem
{
public Sprite theIcon;
public int price;
public bool isBuyed;
}
Then I make a list
public List<m_ShopItem> ShopItemsList = new List<m_ShopItem>();
[SerializeField] GameObject ShopPanel;
[SerializeField] Transform theContent;
GameObject g;
Button buyBtn;
public GameObject itemTemplate;
private void Start()
{
for (int i = 0; i < ShopItemsList.Count; i++)
{
g = Instantiate(itemTemplate, theContent);
g.transform.GetChild(0).GetComponent<Image>().sprite = ShopItemsList[i].theIcon;
g.transform.GetChild(1).GetComponent<TextMeshProUGUI>().text = ShopItemsList[i].price.ToString();
buyBtn = g.transform.GetChild(2).GetComponent<Button>();
if (ShopItemsList[i].isBuyed)
{
DisableBuyButton();
}
ShopItemsList[i].isBuyed = PlayerPrefs.GetInt("isBuyed") == 1 ? true : false;// i call it Get here
Debug.Log(ShopItemsList[i].isBuyed);
buyBtn.AddEventListener(i, OnShopItemBtnClicked);
}
}
void OnShopItemBtnClicked(int itemIndex)
{
if (m_shopManager.Instance.HasEnoughCoins(ShopItemsList[itemIndex].price))
{
m_shopManager.Instance.UseCoins(ShopItemsList[itemIndex].price);
//purchase Item
ShopItemsList[itemIndex].isBuyed = true;
PlayerPrefs.SetInt("isBuyed", ShopItemsList[itemIndex].isBuyed ? 1 : 0); // SetThe Int here when button clicked and buy the item
//disable the button
buyBtn = theContent.GetChild(itemIndex).GetChild(2).GetComponent<Button>();
DisableBuyButton();
//add avatar
m_Profile.Instance.AddBallType(ShopItemsList[itemIndex].theIcon);
}
else
{
//NoCoinsAnim.SetTrigger("NoCoins");
Debug.Log("You don't have enough coins!!");
}
}
void DisableBuyButton()
{
buyBtn.interactable = false;
buyBtn.transform.GetChild(0).GetComponent<Text>().text = "PURCHASED";
}
but the result is the same I have to rebuy when going to the main menu or restart the game.
Can anyone tell me what's wrong with my script?
Your current problem is that you create only one PlayerPrefs Object with the value being equal to 1 or 0.
So if you buy only one of your shop items all of them get unlocked. Because you only Check if the PlayerPrefs is true(1) or false (0).
To fix that we would need to create a List of unique PlayerPrefs equal to the amount of shop items. We can do that if we add the index in the ShopItemsList to the isBuyed name.
Create initial Playerprefs:
// Called before the Start Function
private void Awake() {
// Loop through all shopping Items
for (int i = 0; i < ShopItemsList.Count; i++) {
// Check if the Item was already bought
bool bought = PlayerPrefs.GetInt("isBuyed"+i) == 1 ? true : false;
// If it was we can't reset the score.
if (!bought) {
// Create a different PlayerPrefs for each Item in the Shop.
PlayerPrefs.SetInt("isBuyed"+i, ShopItemsList[i].isBuyed ? 1 : 0);
}
}
}
...
Now we can check for that PlayerPrefs instead of checking for just once in Start().
Check if Items were bought:
private void Start() {
// Loop through all shopping Items
for (int i = 0; i < ShopItemsList.Count; i++) {
...
// Actualize our values so already bought items can't be bought again
ShopItemsList[i].isBuyed = PlayerPrefs.GetInt("isBuyed"+i) == 1 ? true : false;
// Check if the item was bought
if (ShopItemsList[i].isBuyed) {
DisableBuyButton();
}
...
}
}
We also need to make sure that we set the right PlayerPrefs when we buy a Product from the Shop. To ensure that we can just add the itemIndex parameter we get from the function and add that to the isBuyed name.
Buying Items:
void OnShopItemBtnClicked(int itemIndex) {
...
// Buy the Item.
ShopItemsList[itemIndex].isBuyed = true;
PlayerPrefs.SetInt("isBuyed"+itemIndex, ShopItemsList[itemIndex].isBuyed ? 1 : 0);
...
}

Item in ScrollView is not seen in Scene, but it shows in hierarchy

I am trying to display a list, generated dinamically. I created a prefab with the things I need in it (a TextView, 3 TMP_InputFields and 2 Buttons.)
To manage the different list items, I created a script (SkillManager, since the items represents skill the player can choose), which I attached to the prefab.
Then, I add every item (currently I am adding only one for testing purposes) to a List, iterate that list, and add the prefab to the Content of a ScrollView:
for(int i = 0; i < listaSkills.Count; i++)
{
GameObject listItem = Instantiate(SkillPrefab) as GameObject;
listItem.GetComponent<SkillManager>().skill = listaSkills[i];
//listItem.transform.SetParent(SkillsContent.transform, false);
listItem.transform.parent = SkillsContent.transform;
}
When I run this, no item is seen in the ScrollView, but I can see the SkillItem added to the hierarchy:
If I move to Scene tab after playing, I see a square with red lines crossing it:
Why is my item not displaying? Why the red cross? How can I populate my ScrollView?
EDIT:
This is the code of SkillManager, the script added to SkillPrefab:
public class SkillManager : MonoBehaviour
{
public TMP_InputField toSpend;
public TMP_InputField rangos;
public TMP_InputField modificadores;
public TMP_InputField total;
public Button plusButton;
public Button minusButton;
public TMP_Text nombre;
public Skill skill;
private int modificador;
private int pointsToSpend;
private int totalPoints;
// Start is called before the first frame update
void Start()
{
print("Start");
if(total!=null)
total.text = "0";
if(modificadores!=null)
modificadores.text = "0";
if (toSpend != null)
{
toSpend.GetComponent<TMP_InputField>().text = GetSkillPoints();
totalPoints = int.Parse(total.GetComponent<TMP_InputField>().text);
pointsToSpend = int.Parse(toSpend.GetComponent<TMP_InputField>().text);
}
else
{
GameObject GameObjectToSpend = GameObject.FindGameObjectWithTag("tospend");
toSpend = GameObjectToSpend.GetComponent<TMP_InputField>();
if (toSpend == null)
{
print("Sigue siendo nulo");
}
else
{
toSpend.text= GetSkillPoints();
//totalPoints = int.Parse(total.GetComponent<TMP_InputField>().text);
if(total!=null)
totalPoints = int.Parse(total.text);
if(toSpend!=null)
pointsToSpend = int.Parse(toSpend.text);
}
}
if (skill != null)
{
modificador = GetModificador(skill);
string sModificador = modificadores.GetComponent<TMP_InputField>().text;
int iModificador = int.Parse(sModificador);
modificadores.GetComponent<TMP_InputField>().text = iModificador.ToString();
}
if (plusButton != null)
{
plusButton.onClick.AddListener(PlusButtonClicked);
minusButton.onClick.AddListener(MinusButtonClicked);
}
}
private string GetSkillPoints()
{
return "1";
}
public void MinusButtonClicked()
{
string ranks = rangos.GetComponent<TMP_InputField>().text;
int ranksInt = int.Parse(ranks);
if (ranksInt > 0)
{
int newRank = ranksInt - 1;
pointsToSpend += 1;
rangos.GetComponent<TMP_InputField>().text = newRank.ToString();
toSpend.GetComponent<TMP_InputField>().text = pointsToSpend.ToString();
total.GetComponent<TMP_InputField>().text = (newRank + modificador).ToString();
skill.Puntos = newRank;
}
}
public void PlusButtonClicked()
{
string ranks=rangos.GetComponent<TMP_InputField>().text;
int ranksInt = int.Parse(ranks);
Character character = Almacen.instance.Character;
int level = character.CharacterLevel;
if (ranksInt < level && pointsToSpend > 0)
{
int newRank = ranksInt + 1;
rangos.GetComponent<TMP_InputField>().text = newRank.ToString();
pointsToSpend -= 1;
toSpend.GetComponent<TMP_InputField>().text = pointsToSpend.ToString();
total.GetComponent<TMP_InputField>().text = (newRank + modificador).ToString();
skill.Puntos = newRank;
}
}
private int GetModificador(Skill skill)
{
int retorno=0;
if (skill.Clasea)
{
retorno += 3;
}
else
{
retorno += 0;
}
retorno += GetModificadorCaracteristica();
return retorno;
}
private int GetModificadorCaracteristica()
{
Utils utils = new Utils();
int retorno = 0;
int characteristic=0;
switch (skill.Caracteristica)
{
case "Fue":
characteristic = Almacen.instance.Character.EffectiveStr;
break;
case "Des":
characteristic = Almacen.instance.Character.EffectiveDex;
break;
case "Con":
characteristic = Almacen.instance.Character.EffectiveCon;
break;
case "Int":
characteristic = Almacen.instance.Character.EffectiveInt;
break;
case "Sab":
characteristic = Almacen.instance.Character.EffectiveWis;
break;
case "Car":
characteristic = Almacen.instance.Character.EffectiveCha;
break;
}
retorno = utils.GetCharModifier(characteristic);
return retorno;
}
}
it looks like you instantiate the object as a GameObject. but this will not be seen in the canvas because it isn't a UI component. you may want to add a sprite or image to the component and instantiate that into the Canvas. it will look something like this:
public class SkillPrefab
{
//put all your variables here!!!
public Sprite skillSprite;
}
public class YourClassName : MonoBehaviour
{
[SerializeField]
public List<SkillPrefab> skills = new List<SkillPrefab>();
private void Update()
{
Sprite listItem = Instantiate(skills[0].skillSprite); //the index is the skill you want to spawn in the list.
}
}
this does take into account that you have made your skills into a list of skills that you can acces.

How to display win panel once the item left equal to zero?

I want to display win panel in the game once itemLeft = 0. But still cant figure out how and what is the error about. Below shows my getScore coding:-
public GameObject scoretext;
public GameObject itemlefttext;
public GameObject finalScore;
public static float score = 0;
public GameObject winPanel;
private void Start()
{
scoretext.GetComponent<Text>().text = "0";
setscore(0);
}
private void Update()
{
itemlefttext.GetComponent<Text>().text = "" + GameObject.FindGameObjectsWithTag("draggableobject").Length;
if (GameObject.FindGameObjectsWithTag("draggableobject").Length == 0)
{
winPanel.SetActive(true);
}
}
public void setscore(float scoretoadd)
{
score += scoretoadd;
scoretext.GetComponent<Text>().text = score.ToString("F0");
finalScore.GetComponent<Text>().text = score.ToString("F0");
}
There are many ways to implement this.
With your current code structure:
private void Update()
{
itemlefttext.GetComponent<Text>().text = ""+GameObject.FindGameObjectsWithTag("draggableobject").Length;
//itemLeftTxt = GameObject.FindGameObjectWithTag("Text").GetComponent<Text>();
itemLeftTxt.text = gameObject.GetComponent<Text>().text;
if (itemLeftTxt.text == "0")
{
winPanel.SetActive(true);
}
}
Minor Improvement:
private void Update()
{
itemlefttext.GetComponent<Text>().text = "" + GameObject.FindGameObjectsWithTag("draggableobject").Length;
//itemLeftTxt = GameObject.FindGameObjectWithTag("Text").GetComponent<Text>();
itemLeftTxt.text = gameObject.GetComponent<Text>().text;
if (GameObject.FindGameObjectsWithTag("draggableobject").Length == 0)
{
winPanel.SetActive(true);
}
}
If those draggable objects are not spawned on runtime then you can create a public variable and assign a reference to them through the inspector OR
New way:
public GameObject[] DraggableObjects;
Add this to the start function:
DraggableObjects = GameObject.FindGameObjectsWithTag("draggableobject");
itemLeftTxt = gameObject.GetComponent<Text>();
You can delete extra line of codes:
Final Update function:
private void Update()
{
itemlefttext.text = "" + DraggableObjects.Length;
if (DraggableObjects.Length == 0)
{
winPanel.SetActive(true);
}
}
Final Start Function:
private void Start()
{
DraggableObjects = GameObject.FindGameObjectsWithTag("draggableobject");
itemLeftTxt = GetComponent<Text>();
}
PS: Calling Gameobject.FindGameObjectsWithTag inside the update would be heavy on processor. Let me know if it helps.

Foreach loop not working in android for custom object

I am trying to store the count of number of times an item from a list view is clicked in a custom array list but in my code the quantity of only the first clicked item is increasing and if i click on any other item then it gets added to the list view instead of increasing the quantity. Please tell me if you know any other method to get the count. Thanks
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Flowers item = (Flowers) parent.getAdapter().getItem(position);
if(item == null)
{
Toast.makeText(getBaseContext(),"item is null",Toast.LENGTH_LONG).show();
}else {
selectedItems items = new selectedItems();
items.setName(item.getName());
items.setCategory(item.getCategory());
Counter counter = new Counter();
if(selectedItemsArrayList.isEmpty())
{
counter.CartItemPosition = 0;
counter.MenuItemPosition = position;
counter.quantity = 1;
items.setQuantity(counter.quantity);
count.add(counter);
selectedItemsArrayList.add(items);
}
else
{
for(Counter c : count)
{
if(c.MenuItemPosition == position)
{
c.quantity = c.quantity + 1;
int i = c.CartItemPosition;
selectedItemsArrayList.get(i).setQuantity(c.quantity);
break;
}
else
{
Counter obj = new Counter();
obj.MenuItemPosition = position;
obj.CartItemPosition++;
obj.quantity=1;
count.add(obj);
items.setQuantity(obj.quantity);
selectedItemsArrayList.add(items);
return;
}
}
}
}
}
});
and my Counter class is
public class Counter {
public int CartItemPosition;
public int MenuItemPosition;
public int quantity;}

Unity 3d - Material Selection for Rendering

I am trying to change the Material of wall at run time. I import the model of house from Google Sketchup, which has different materials all in one object (this is shown in the inspector). Whenever I click the next button (>>), it changes the first material of the object. How do I get the references to the other elements? This is what I have so far:
public class Material_GUI : MonoBehaviour {
public Material[] mats;
public GameObject go;
private int index = 0;
// Use this for initialization
void Start () {
go.renderer.material= mats[index];
}
// Update is called once per frame
void Update () {
}
void OnGUI(){
GUILayout.BeginArea(new Rect(Screen.width/2-100,Screen.height-60,200,50));
GUI.Box (new Rect(10,10,190,40),"");
GUI.Label(new Rect(62,20,100,20),"Wall Testing"+(index +1));
if(GUI.Button (new Rect(15,15,30,30),"<<")){
index--;
if(index<0){
index = mats.Length - 1;
}
go.renderer.material = mats[index];
}
if(GUI.Button (new Rect(165,15,30,30),">>")){
index++;
if(index > mats.Length -1){
index = 0;
}
go.renderer.material = mats[index];
}
GUILayout.EndArea();
}
}
If you want to change the other material of the renderer, you can use
go.renderer.materials
http://docs.unity3d.com/Documentation/ScriptReference/Renderer-materials.html?from=MeshRenderer
For example:
go.renderer.materials[0] = mats[0];
go.renderer.materials[1] = mats[1];