Making a certain Element of an array interactable using a PlayerPrefs float while all of the +1 elements remain locked - c#

So basically I have a Score/Highscore system and I also have an array which contains some of the buttons which change the color of the player's character. So my question is that if I have highscoreCount >= 20, I want a certain button to become interactable.
ScoreManager Script
using UnityEngine;
using UnityEngine.UI;
public class ScoreManager : MonoBehaviour
{
public Transform player;
public Text scoreText;
public Text highScoreText;
public float scoreCount;
public float highscoreCount;
public float pointsPerSecond;
public bool scoreIncreasing;
public Button[] CustomizeButtons;
void Start()
{
if (PlayerPrefs.HasKey("Highscore"))
{
highscoreCount = PlayerPrefs.GetFloat("Highscore");
}
int CustomizationButtonReached = PlayerPrefs.GetInt("CustomizationButtonReached", 1);
{
for (int i = 0; i < CustomizeButtons.Length; i++)
{
if (i + 1 > CustomizationButtonReached)
CustomizeButtons[i].interactable = false;
}
}
}
void Update()
{
if (scoreIncreasing)
{
scoreCount += pointsPerSecond * Time.deltaTime;
}
if(scoreCount > highscoreCount)
{
highscoreCount = scoreCount;
PlayerPrefs.SetFloat("Highscore", highscoreCount);
}
scoreText.text = "Score: " + Mathf.Round(scoreCount);
highScoreText.text = "Highscore: " + Mathf.Round(highscoreCount);
}
}
CustomizeColors Script
using UnityEngine;
public class CustomizeColors : MonoBehaviour
{
public Color[] Colors;
public Material Mat;
public void Start()
{
if (PlayerPrefs.HasKey("HeadColor"))
{
Mat.color = Colors[PlayerPrefs.GetInt("HeadColor")];
}
}
public void ChangeColor(int colorIndex)
{
Mat.color = Colors[colorIndex];
PlayerPrefs.SetInt("HeadColor", colorIndex);
PlayerPrefs.Save();
}
}
Essentially this is the code that I have made (in the ScoreManager) to make an array and to disable all of the buttons. I just want to get a specific element to be interactable when highscoreCount >= "number"
public Button[] CustomizeButtons;
void Start()
{
int CustomizationButtonReached = PlayerPrefs.GetInt("CustomizationButtonReached", 1);
{
for (int i = 0; i < CustomizeButtons.Length; i++)
{
if (i + 1 > CustomizationButtonReached)
CustomizeButtons[i].interactable = false;
}
}
}

You could go multiple ways about this, for example storing a reference for that specific button and when the condition you mentioned is true just set that one button to be interactable
[SerializeField] Button button; //Set this in the inspector
public void AddToScore(int score)
{
//Do your score adding stuff
if (highscoreCount >= number)
{
button.interactable = true
}
}

Related

Trying to find all of the objects in my scene by a custom tag script I have created, however i continue getting a null reference exception

The Tag script has been working for me so far as an alternate to the unity tags up to this point, allowing me to assign multiple tags to an object at once. Now I want to create a method that will get all of the objects in the scene, filter them by the tag, and then return it as an array. The null reference exception refers to line 41 of the Tag.cs script. How do I fix this?
Tags.cs file
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Tags : MonoBehaviour
{
public string[] startTags;
private string[] tags;
private void Start()
{
tags = startTags;
}
public bool FindTag(string search)
{
bool results = false;
for (int i = 0; i < tags.Length; i++)
{
if(search == tags[i])
{
results = true;
break;
}
}
return results;
}
//Find objects by custom script tags
//HERE IS WHERE THE METHOD IS CREATED
public static GameObject[] ObjectsByTag(string search)
{
//Get all objects in scene
GameObject[] allObjects = FindObjectsOfType<GameObject>();
GameObject[] storedObjects = new GameObject[allObjects.Length];
GameObject[] finalObjects;
//Filter
int count = 0;
for (int i = 0; i < allObjects.Length; i++)
{
if (allObjects[i].GetComponent<Tags>().FindTag(search)) //line 41
{
storedObjects[count] = allObjects[i];
count++;
}
}
//Assign final length
finalObjects = new GameObject[count];
//Reassign to final array
for (int i = 0; i < count; i++)
{
finalObjects[i] = storedObjects[i];
}
return finalObjects;
}
}
GameController.cs file (How it is being used
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameController : MonoBehaviour
{
//SCREEN START
//Get Screen Size
private float sHeight;
private float sWidth;
//Intended Screen Size
readonly float iH = 695f;
readonly float iW = 1540f;
//Convert
private float cH;
private float cW;
public float ConvertedHeight => cH;
public float ConvertedWidth => cW;
//SCREEN END
//MOUSE CAM START
//mousePostion
private float mX;
private float mZ;
public float MouseX => mX;
public float MouseZ => mZ;
//MOUSE CAM END
//EnemySpeedModifier
private float esm;
public float ESM
{
get { return esm; }
set { esm = value; }
}
//GameOver
private bool gameOver = false;
public bool GameOver
{
get { return gameOver; }
set { gameOver = value; }
}
//game speed
public float speed;
/*
//projectile list
private GameObject[] projectiles;
public GameObject[] Projectiles()
{
return projectiles;
}
public void Projectiles(GameObject value)
{
GameObject[] tempArray = projectiles;
tempArray[projectiles.Length] = value;
projectiles = tempArray;
Debug.Log("Projectile Count: " + projectiles.Length);
}
*/
//HERE IS WHERE IT IS USED
public GameObject[] ProjectilesInScene
{
get
{
return Tags.ObjectsByTag("projectile");
}
}
// Start is called before the first frame update
void Start()
{
//CONVERT SCREEN SIZES START
sHeight = Screen.height;
sWidth = Screen.width;
cH = iH / sHeight;
cW = iW / sWidth;
//CONVERT SCREEN SIZES END
}
// Update is called once per frame
void Update()
{
if (gameOver)
{
speed /= 1 + 0.5f * Time.deltaTime;
}
//Update mose position
mX = Input.mousePosition.x;
mZ = Input.mousePosition.y;
}
}
It seems that not all of your GameObject objects have the Tags component. Per the GameObject.GetComponent documentation
Returns the component of Type type if the game object has one attached, null if it doesn't.
If you know that some objects won't have the Tags component, your line 41 can use a simple null conditional operator:
if (allObjects[i].GetComponent<Tags>()?.FindTag(search) == true)
{
...
}
Note the ? after GetComponent<Tags>().

Making an Int increase every second by x value

I'm making a game similar to the 2013 style Cookie Clicker. I'm stuck with making the auto generate over time script. I want to be able to access a script that makes "Muffins" automatically.
I've tried to make the script multiple times but I can't seem to get the muffin count to change.
public bool MakingMuffins = false;
public static int MuffinIncrease = 1;
public int InternalIncrease;
void Update () {
InternalIncrease = MuffinIncrease;
if (MakingMuffins == false)
{
MakingMuffins = true;
StartCoroutine(MakeTheMuffin());
}
}
IEnumerator MakeTheMuffin ()
{
GlobalMuffins.MuffinCount += InternalIncrease;
yield return new WaitForSeconds(1);
MakingMuffins = false;
}
}
and my other main file to start the method.
public void StartAutoMuffin()
{
if (InternalPlayerMuffins >=bakerycost){
playSound.Play();
StartBakery.SetActive(true);
InternalPlayerMuffins -= bakerycost;
bakerycost *= 2;
turnOffButton = true;
themps += 1;
thelevel += 1;
}else{
Debug.Log("Cant do anything");
}
}
StartBakery is a Game Object that should start the auto make after I press the button. It is set inactive as default.
Here is more code from my main script to help understand.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GlobalGameMechanics : MonoBehaviour
{
public static bool turnOffButton = false;
public AudioSource muffinSound;
public AudioSource playSound;
public static int GlobalPlayerDiamonds;
public int InternalPlayerDiamonds = 1200;
public GameObject PlayerDiamondDisplay;
public GameObject TotalMPS;
public GameObject Cost;
public GameObject Level;
public GameObject MPS;
public GameObject FakeCost;
public GameObject FakeLevel;
public GameObject FakeMPS;
public int InternalPlayerMuffins = 15;
public int bakerycost = 25;
public int thelevel = 0;
public int themps = 0;
public int currentMuffins;
public GameObject ShopPanel;
public GameObject fakeButton;
public GameObject realButton;
public GameObject StartBakery;
void Start()
{
}
void Update()
{
GlobalPlayerDiamonds = InternalPlayerDiamonds;
PlayerDiamondDisplay.GetComponent<Text>().text = "" + InternalPlayerDiamonds;
TotalMPS.GetComponent<Text>().text = "/Sec " + themps;
GlobalMuffins.MuffinCount = InternalPlayerMuffins;
Cost.GetComponent<Text>().text = "" + bakerycost;
Level.GetComponent<Text>().text = "Level " + thelevel;
MPS.GetComponent<Text>().text = "MPS " + themps;
FakeCost.GetComponent<Text>().text = "" + bakerycost;
FakeLevel.GetComponent<Text>().text = "Level " + thelevel;
FakeMPS.GetComponent<Text>().text = "MPS " + themps;
currentMuffins = InternalPlayerMuffins;
if (currentMuffins >= bakerycost)
{
fakeButton.SetActive(false);
realButton.SetActive(true);
}
if (turnOffButton == true)
{
realButton.SetActive(false);
fakeButton.SetActive(true);
turnOffButton = false;
}
}
So far I have created 1 button that I want to increase the "InternalPlayerMuffins" By 1 every second. And then I want to have Button 2 to increase by +5 /sec. I'm just having trouble and been stuck for a few days.
I cant make the muffins increase at all on its :/
Your are close, it seems like you forgot to loop your coroutine endlessly like so:
IEnumerator MakeTheMuffin() {
while (true) {
GlobalMuffins.MuffinCount += InternalIncrease;
yield return new WaitForSeconds(1);
MakingMuffins = false;
}
}
Though I may be wrong on that, since I am not so sure if GlobalMuffins.MuffinCount is the integer that you want to increase every second.
But generally the idea is the same, you want would want a coroutine, a target interval, a target integer and the value you want to increase by, like so:
public class YourMuffinMaker : MonoBehaviour {
public float intervals;
public int increment;
private int muffinCount;
void Start() {
muffinCount = 0;
StartCoroutine(CreateMuffinEveryIntervals());
}
private IEnumerator CreateMuffinEveryIntervals() {
while (true) {
muffinCount += increment;
yield return new WaitForSeconds(intervals);
}
}
}
This answer that was posted is an alternative to Coroutine as well.
i can use InvokerRepeating whick calls method every time u need, for example
void Start()
{
InvokeRepeating("CreateNewMuffin", 0f, 1f);
}

How do I fix this Idle-Car Game Script

I am trying to have a game, in which everyone can buy cars (and I save that data to playerprefs). So I have 9 trails for the cars in my game and I am trying to write some code so that when you press a button the car & the trail for that car will show up.
When the button next to it is clicked, it saves that data so when people restart the game, they will still have the car & trail open and won't need to press the button again.
Here's my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine; using UnityEngine.UI;
public class GameManager : MonoBehaviour
{
public Button[] TrailLevel;
public GameObject[] Cars, Trails;
public Text text;
public int CurrentCarToSpawn = 0;
private void Start()
{ }
private void FixedUpdate()
{
UpdateCar();
}
public void InstantiateCar()
{
TrailLevel[CurrentCarToSpawn].gameObject.active = false;
MineLevel[CurrentCarToSpawn+1].interactable = true;
PlayerPrefs.SetInt("TrailCountA", PlayerPrefs.GetInt("TrailCountA") + 1);
PlayerPrefs.Save();
CurrentCarToSpawn++;
UpdateCar();
}
void UpdateCar()
{
int TrailCountA= PlayerPrefs.GetInt("TrailCountA", 1);
for (int i = 0; i < TrailLevel.Length; i++)
{
if (i + 1 > TrailCountA)
{
TrailLevel.interactable = false;
}
if (TrailLevel.interactable)
{
Trains[CurrentCarToSpawn].gameObject.active = true;
Mines[CurrentCarToSpawn].gameObject.active = true;
}
}
text.text = PlayerPrefs.GetInt("TrailCountA").ToString();
}
}
From what I can see with your code, this is how I would approach it:
using System.Collections;
using System.Collections.Generic;
using UnityEngine; using UnityEngine.UI;
public class GameManager : MonoBehaviour
{
public Button[] TrailLevel;
public GameObject[] Cars, Trails;
public Text text;
public int CurrentCarToSpawn = 0;
private void Start()
{
// Load the current car. ADDED
CurrentCarToSpawn = PlayerPrefs.getInt("savedSelection", 0);
// Since we are loading the last selection, we need to call our
// instantiation method so it can activate the appropriate
// GameObjects.
InstantiateCar();
}
private void FixedUpdate()
{
UpdateCar();
}
public void InstantiateCar()
{
TrailLevel[CurrentCarToSpawn].gameObject.active = false;
MineLevel[CurrentCarToSpawn+1].interactable = true;
PlayerPrefs.SetInt("TrailCountA", PlayerPrefs.GetInt("TrailCountA") + 1);
// Save that this is our current selection.
PlayerPrefs.SetInt("savedSelection", CurrentCarToSpawn);
PlayerPrefs.Save();
CurrentCarToSpawn++;
UpdateCar();
}
void UpdateCar()
{
int TrailCountA= PlayerPrefs.GetInt("TrailCountA", 1);
for (int i = 0; i < TrailLevel.Length; i++)
{
if (i + 1 > TrailCountA)
{
TrailLevel.interactable = false;
}
if (TrailLevel.interactable)
{
Trains[CurrentCarToSpawn].gameObject.active = true;
Mines[CurrentCarToSpawn].gameObject.active = true;
}
}
text.text = PlayerPrefs.GetInt("TrailCountA").ToString();
}
}

How can i use ui button and ui toggle in a script to create/destroy objects on another script?

In the Hierarchy i have a Canvas and under it as childs i have a ui button and ui toggle. On the button i attached a script with a onclick event.
The idea is when i click the button depending on the toggle state false/true decide if to just create more gameobjects or destroy first all the old gameobjects and create new.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GenerateObjectsButton : MonoBehaviour
{
private InstantiateObjects instantiateobjects;
private bool toggleOnOf;
public Toggle toggle;
private void Start()
{
instantiateobjects = new InstantiateObjects();
toggle.onValueChanged.AddListener((value) =>
{
MyListener(value);
});
}
public void MyListener(bool value)
{
if (value)
{
//do the stuff when the toggle is on
toggleOnOf = true;
}
else
{
//do the stuff when the toggle is off
toggleOnOf = false;
}
}
public void OnButton()
{
if (toggleOnOf == false)
{
instantiateobjects.generateObjectOnTerrain();
}
else
{
instantiateobjects.DestroyObjects();
instantiateobjects.generateObjectOnTerrain();
}
}
}
The InstantiateObjects script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//[ExecuteInEditMode]
public class InstantiateObjects : MonoBehaviour
{
public GameObject prefab;
public Terrain terrain;
public float yOffset = 0.5f;
public int objectsToInstantiate;
public bool parent = true;
public bool randomScale = false;
public float setRandScaleXMin, setRandScaleXMax;
public float setTandScaleYMin, setTandScaleYMax;
public float setTandScaleZMin, setRandScaleZMax;
public bool generateNew;
private float terrainWidth;
private float terrainLength;
private float xTerrainPos;
private float zTerrainPos;
private int numberOfObjectsToCreate;
private GameObject objInstance;
private GameObject[] createdObjects;
private string objname;
public void Start()
{
//Get terrain size
terrainWidth = terrain.terrainData.size.x;
terrainLength = terrain.terrainData.size.z;
//Get terrain position
xTerrainPos = terrain.transform.position.x;
zTerrainPos = terrain.transform.position.z;
numberOfObjectsToCreate = objectsToInstantiate;
objname = prefab.name;
MyCustomEditor.TagsAndLayers.AddTag(objname);
generateNew = false;
generateObjectOnTerrain();
}
public void Update()
{
}
public void DestroyObjects()
{
if (createdObjects != null && createdObjects.Length > 0)
{
for (int i = 0; i < createdObjects.Length; i++)
{
DestroyImmediate(createdObjects[i]);
}
createdObjects = new GameObject[0];
}
}
public void generateObjectOnTerrain()
{
for (int i = 0; i < objectsToInstantiate; i++)
{
//Generate random x,z,y position on the terrain
float randX = UnityEngine.Random.Range(xTerrainPos, xTerrainPos + terrainWidth);
float randZ = UnityEngine.Random.Range(zTerrainPos, zTerrainPos + terrainLength);
float yVal = Terrain.activeTerrain.SampleHeight(new Vector3(randX, 0, randZ));
//Generate random x,y,z scale on the terrain
float randScaleX = Random.Range(setRandScaleXMin, setRandScaleXMax);
float randScaleY = Random.Range(setTandScaleYMin, setTandScaleYMax);
float randScaleZ = Random.Range(setTandScaleYMax, setRandScaleZMax);
//Apply Offset if needed
yVal = yVal + yOffset;
//Generate the Prefab on the generated position
objInstance = Instantiate(prefab, new Vector3(randX, yVal, randZ), Quaternion.identity);
if (randomScale == true)
objInstance.transform.localScale = new Vector3(randScaleX, randScaleY, randScaleZ);
if (parent)
objInstance.transform.parent = this.transform;
objInstance.tag = objname;
}
createdObjects = GameObject.FindGameObjectsWithTag(objname);
}
}
The problems i'm facing now are:
In the InstantiateObjects when i click the button i'm getting null exception on the line:
createdObjects = GameObject.FindGameObjectsWithTag(objname);
createdObjects is null. Except the first time i'm running the game then it's not null but then each time when i click the button it's null.
Second problem is that i need to change the toggle state to false then click the button true click the button false click the button and only then it's getting the DestroyObjects method.
It might not be updating the array as the reference are the same. Reset the array to null at the start of each iteration and see if that works.

Interactable button UNITY

I'm trying to make a simple condition:
If the value I have, is less than the price it costs the item, the button is disabled.
If the value I have, is greater than or equal to the price it costs the item the button is enabled and I can buy it.
But when I test, I have some problems.
First, if I have less than the item cost the button is enabled, and only when I click on it it is when it disables.
Second, if I have less than the item cost and I click on it it disables, but if I get enough to purchase the item, the button is not enabled again.
How do I to be checked these variables all the time? If I have enough the button is enabled if you do not have it disables.
Bellow my scrip:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class BuySkin : MonoBehaviour {
public int price;
public Button buyBee1;
void OnEnable ()
{
//Register Button Events
buyBee1.onClick.AddListener (() => buySkin (buyBee1));
}
public void buySkin(Button button)
{
if (BeeCoinScore.coin >= price) {
BeeCoinScore.coin -= price;
buyBee1.interactable = false;
}
if (BeeCoinScore.coin < price) {
buyBee1.interactable = false;
}
}
void OnDisable ()
{
//Un-Register Button Events
buyBee1.onClick.RemoveAllListeners ();
}
}
Try this out with some prefabs!
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
public class GameController : MonoBehaviour
{
public int coins;
private int spherePrice = 100, cubePrice = 50;
public GameObject player;
public GameObject[] availablePrefabs;
public List<GameObject> mySkins;
public Button btnSphere, btnCube;
public Text txtSphere, txtCube;
void Start ()
{
string serializedMySkins = PlayerPrefs.GetString ("skins", "");
string serializedPlayer = PlayerPrefs.GetString ("player", "");
// skins desserialization
if (serializedMySkins == "")
mySkins = new List<GameObject> ();
else {
var a = serializedMySkins.Split (',');
for (int i = 0; i < a.Length; i++) {
if (a [i] == "Sphere") {
mySkins.Add (availablePrefabs [0]);
}
if (a [i] == "Cube") {
mySkins.Add (availablePrefabs [1]);
}
}
}
// player desserialization
if (serializedPlayer != "") {
if (serializedPlayer == "Sphere") {
player = availablePrefabs [0];
}
if (serializedPlayer == "Cube") {
player = availablePrefabs [1];
}
} else {
player = mySkins [0];
}
coins = PlayerPrefs.GetInt ("coins", 0);
coins = 1000;
}
void Update ()
{
if (mySkins.Contains (availablePrefabs [0])) {
txtSphere.text = "Usar esfera";
} else {
btnSphere.interactable = coins >= spherePrice;
}
if (mySkins.Contains (availablePrefabs [1])) {
txtCube.text = "Usar cubo";
} else {
btnCube.interactable = coins >= cubePrice;
}
}
public void play ()
{
player = (GameObject)Instantiate (player, new Vector2 (0, 0), Quaternion.identity);
}
public void verifySkin (GameObject skinPrefab)
{
if (mySkins.Contains (skinPrefab)) {
useSkin (skinPrefab);
} else if (coins >= priceOf (skinPrefab)) {
buySkin (skinPrefab, priceOf (skinPrefab));
}
}
public void buySkin (GameObject skinPrefab, int price)
{
mySkins.Add (skinPrefab);
coins -= price;
string skinsHash = "";
for (int i = 0; i < mySkins.Count; i++) {
skinsHash += mySkins [i].name + ",";
}
Debug.Log (skinsHash);
PlayerPrefs.SetInt ("coins", coins);
PlayerPrefs.SetString ("skins", skinsHash);
PlayerPrefs.Save ();
}
public void useSkin (GameObject skinPrefab)
{
player = skinPrefab;
PlayerPrefs.SetString ("player", player.name);
PlayerPrefs.Save ();
}
private int priceOf (GameObject skinPrefab)
{
if (skinPrefab == availablePrefabs [0])
return spherePrice;
else if (skinPrefab == availablePrefabs [1])
return cubePrice;
else
return 0;
}
}
OnEnable()is called when the object becomes enabled and active.
you need Update() as it is getting called every frame it will check whether your value is less than or greater than price of item.You may also try like this.
// I think that you are making an buymenu, so you can disable and enable your menu with ui button and check money you have
using System.Collections;
using UnityEngine.UI;
public class BuySkin : MonoBehaviour
{
public int price;
public static int money;// money you have
public Button thisbuyBee1;
public void buychkr()
{
if(price>= money)
{
thisbuyBee1.interactable = false;
}
else
{
thisbuyBee1.interactable = true;
}
}
void Update()
{
buychkr();
}
}

Categories