How to save boolean properly on Playerprefs? - unity3d

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

Related

How do I you images as a score into my game?

I have been trying to make a game that's a parody off of minesweeper in unity. I have gotten most of the stuff done including the timer. One problem I have been having is the score count. The score count consists of 3 images. What I am looking is a way so that I can change each image based on how many mines have been caught. Here's the code for it:
public int scorecount = 0;
int i = 0;
int a;
public int amountOfMines;
public Image faceImage;
public Sprite sprite;
private Timer timer;
public ItemDrag itemDrag;
public List<Image> scoreCountImages;
public Sprite[] digitSprites;
void Start()
{
timer = GetComponent<Timer>();
}
void Update()
{
if(scorecount >= i)
{
if(scorecount == amountOfMines) //If the score equals the amount of mines, stop timer, change face sprite.
{
Debug.Log("All the Mines have been cleared");
faceImage.sprite = sprite;
timer.isStop = true;
itemDrag.thereAreStillMines = false;
}
}
}
Your question has a simple solution. Easily use multiple if, In the following code, there is a Sprite coordinator with points that you can match the desired Sprites with the desired conditions.
public void SyncSprite()
{
if (score >= amountOfMines) faceImage.sprite = faceSprites[2]; // total mines
else if (score >= 7) faceImage.sprite = faceSprites[1]; // for e.g after 7 score change to second sprite
else if (score >= 0) faceImage.sprite = faceSprites[0]; // default sprite
}
For Example, at the bottom, then click on a box and there is no mine there. The score goes higher and the sync code is executed at the end of the score calculation and syncs the sprite with the score.
public void OnClickedBox()
{
// do something...
if (noMineWasHere) score++;
SyncSprite();
}
TLDR; Your mistake was rendering 3-digits as a whole thing; You should have 3-sprite renderer, each displaying a single digit.
Set in the inspector, an array of Sprites for the digits, ordered properly.You will use the array as a Dictionary to easily access any digit-sprite by a corresponding number:
[SerializeField, Tooltip("Your number 0-9 sprites should be here, ordered correctly.")]
private Sprite[] numberSprites;
// ......
numberSprites[4] // will output '4' sprite;
Next, rather than have 1 big whole renderer to represent all 3 digits, we can split it into 3 renderer, and expose it to the inspector as an array:
[SerializeField]
private SpriteRenderer[] scoreSpriteRenderers;
Each of this renderer should only render 1 single-digit, something like this:
Now we can easily set-up a loop, to loop through each number in the score, and change the sprite based on the number.
The full result:
[SerializeField, ToolTip("Your number 0-9 sprites should be here, ordered correctly.")]
private Sprite[] numberSprites;
[SerializeField]
private SpriteRenderer[] scoreSpriteRenderers;
private int scoreCount;
// Converts the '123' into a {1, 2, 3}
// See: https://stackoverflow.com/questions/829174/is-there-an-easy-way-to-turn-an-int-into-an-array-of-ints-of-each-digit
public int[] NumbersIn(int value) {
var numbers = new Stack<int>();
for(; value > 0; value /= 10)
numbers.Push(value % 10);
return numbers.ToArray();
}
// Call this whenever score is updated
private void UpdateScoreUI() {
int[] arrScore = NumbersIn(scoreCount);
for (int i = 2; i > 0; --i) {
// If the number was say, 11, the list will only be { 1, 1 }
// So we will need to handle the last digit that is out-of-index.
if (arrScore.Length - 1 <= i){
int currNumber = arrScore[i];
scoreSpriteRenderers[i].sprite = scoreSpriteRenderers[currNumber];
} else {
scoreSpriteRenderers[i].sprite = scoreSpriteRenderers[0];
}
}
}
Edit
Yes, this method works with Image objects as well, since they have a sprite property, and we are only interacting with that property.

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

Unity3d Sprite change with prefabs

Ive a question about how to change spirte images during runtime for a bunch of objects.
So i made a tiny racer 2d game, and therefore you can choose differend themes. You have this option in an integraded menu (not a seperate scene).
My question:
Can i switch the sprites easy during the runtime? Ive made prefabs for each track element - and i changed the sprites of those prefabs, but the change only gets visible, after the scene is reloaded. So i would need to avoid this.
Has someone a solution or a hint how i could do that?
Thanks in advance!
Code:
public class Background_Controller : MonoBehaviour {
public Camera mainCamera;
public Color colorNormal;
public GameObject[] Prefabs;
public Sprite[] normalSprites;
public Sprite[] tronSprites;
// Use this for initialization
void Awake () {
SwitchBackgroundFunction();
}
public void SwitchBackground(string Theme)
{
switch(Theme)
{
case "GreenHell":
PlayerPrefs.SetString("Theme", "Normal");
break;
case "NeonCity":
PlayerPrefs.SetString("Theme", "Tron");
break;
}
SwitchBackgroundFunction();
}
private void SwitchBackgroundFunction()
{
int prefabCount = Prefabs.Length;
if (PlayerPrefs.GetString("Theme") == "Normal")
{
mainCamera.backgroundColor = colorNormal;
for (int i = 0; i <= prefabCount - 1; i++)
{
Prefabs[i].GetComponent<SpriteRenderer>().sprite = normalSprites[i];
}
}
if (PlayerPrefs.GetString("Theme") == "Tron")
{
mainCamera.backgroundColor = Color.black;
for (int i = 0; i <= prefabCount - 1; i++)
{
Prefabs[i].GetComponent<SpriteRenderer>().sprite = tronSprites[i];
}
}
}
// Update is called once per frame
void Update () {
}
}
You can do something along the following lines to swap in a sprite from within your resources folder during runtime.
Sprite spr;
spr = Resources.Load<Sprite>("mysprite"); //insert name and file path to sprite within Resources folder
GetComponent<SpriteRenderer>().sprite = spr;

Unity2D: PlayerPrefs.HasKey

I'm using playerprefs to save data through out scenes. Although I'm having troubles with saving this data when the application is closed. You see I have a IAP shop that gives the player a boomerang when they purchase one, the boomerang effect (done inside my script) is activated through a button. My problem is, is that playerprefs.haskey isn't saving my boomerang effect when I close the game and then reopening it. Although it does save my boomerang effect when through scenes. This is my script:
public bool forceActive = false;
public GameObject BoomerangOn, BoomerangOff;
public static int buttonCount = 0;
static int timesActivated = 0;
void Start()
{
if (PlayerPrefs.HasKey ("boomerangbutton")) {
buttonCount = PlayerPrefs.GetInt ("boomerangbutton");
BoomerangEffect();
}
}
void Update()
{
PlayerPrefs.SetInt("boomerangbutton", buttonCount);
}
public void Activated ()
{
if(timesActivated < BoomeerangText.score)
{
timesActivated++;
StartCoroutine(BoomerangEffect());
}
}
IEnumerator BoomerangEffect()
{
BoomerangOn.SetActive (true);
yield return new WaitForSeconds (10.0f);
BoomerangOn.SetActive (false);
BoomerangOff.SetActive (true);
yield return new WaitForSeconds (1f);
BoomerangOff.SetActive (false);
forceActive = false;
}
Second Edit
Okay I research a bit and linked up boomerang effect script with my boomerang text script. When the user purchase a boomerang from my IAP store, they will get 5 boomerangs, once clicked on, the boomerang text int will go down (like 5, 4, 3, 2 and 1 ) and so will my buttoncount int(that is why the timesactivaed is needed). However I change the Activated function to:
public void Activated ()
{
if (timesActivated < BoomeerangText.score) {
timesActivated++;
StartCoroutine (BoomerangEffect ());
}
}
So far it works regarding activating my boomerang effect when the application is closed, but when it gets to the last int (1) nothing happens, my effect doesn't takes place, so far this is my only problem.
Above is an updated version of what my code looks like now. And below is my Boomerang text script:
public static int score = 0; // The player's score.
public static int click = 1;
public GameObject button;
Text text; // Reference to the Text component.
// Use this for initialization
void Start()
{
if (PlayerPrefs.HasKey ("boomerangTextInt")) {
score = PlayerPrefs.GetInt("boomerangTextInt");
}
}
void Awake()
{
text = GetComponent<Text>();
}
public void Update()
{
SetScoreText();
PlayerPrefs.SetInt("boomerangTextInt", score);
}
void SetScoreText()
{
text.text = " " + score;
if (score <= 0)
{
text.text = "None";
button.GetComponent<Button>().interactable = false;
}
else if (score >= 1)
{
button.GetComponent<Button>().interactable = true;
}
// Set the displayed text to be the word "Score" followed by the score value.
}
public void MinusBoomerangText()
{
score -= click;
text.text = " " + score;
}
}
And in my purchasing script I have this:
public int scoreValue = 5;
if (String.Equals(args.purchasedProduct.definition.id, PRODUCT_5_BOOMERANG, StringComparison.Ordinal))
{
BoomerangEffect.buttonCount += 5;
BoomerangText.score += scoreValue;
Debug.Log("Purchase successfull");
}
Thank you.:)
You are not calling .Save() which means all changes to PlayerPrefs are only in memory and are not persisted to disk, which means the next time you start the application all previous changes are lost.
Try the following in your save function.
void Update()
{
PlayerPrefs.SetInt("boomerangbutton", buttonCount);
PlayerPrefs.Save();
}
Disclaimer : I am not suggesting this is something you should do in your Update at all, as this in inefficient, but this is the root cause of your problem

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];