PlayerPrefs not saving unlocked levels - c#

I used this code to try and make buttons in my level select screen not interactable if the level has not been completed. By default, I want to set level one's button to be unlocked so I used this:
[SerializeField] Button[] levelBUttons;
private void Start()
{
int levelCurrentlyAt = PlayerPrefs.GetInt("levelCurrentlyAt", 2);
for (int i = 0; i < levelBUttons.Length; i++)
{
if(i+2 > levelCurrentlyAt)
{
levelBUttons[i].interactable = false;
}
}
}
The problem is when I press play, level one's button is also not interactable
The reason I have set the int to 2 and not 1 is that in the build settings the scene order is 1) Start Screen, 2) Level Select Screen 3) Level One.
In the inspector, level one's button is also the first button element in the button array.
I changed the for loop to this and it seems to work:
int levelAt = PlayerPrefs.GetInt("levelAt", 1);
for (int i = 0; i < levelBUttons.Length; i++)
{
if(i+1>levelAt)
{
levelBUttons[i].interactable = false;
}
}
But is this not wrong because level one has a build index of 2 and 1, I am supposed to use i+1 and not i+2.

Not sure I fully understand your question, but you might be getting confused on the order. The first scene in build order will have index of 0, as that's how indexing in most languages works. Thus level 1 will have index of 1, unless you have 2 scenes before it in the build menu. Hope I could help!

Related

Removing and re-adding same sprite to list Unity

I have a Unity script in c# where a public list of sprites is declared and then populated manually in the inspector.
The list consists of 19 sprites of cars.
These cars are available only if the player completed each level.
For example: When you open the game for the first time, you only have 1 available car but if you complete level 1, you unlock another car, when you complete level 2, you get another and so on.
My question is, being that I manually populated the list, how do I go about re-adding the same sprite back when a level is completed?
I was going to use cars.RemoveRange(1, 18) to remove them from the list but do not know a way to add them back without calling every sprite back manually. I believe there is a simpler, better way that I do not know about.
This is the script:
public class CarSelection : MonoBehaviour
{
public List<Sprite> cars; //store all your images in here at design time
public Image displayImage; //The current image thats visible
public Button nextCar; //Button to view next image
public Button previousCar; //Button to view previous image
private int i = 0; //Will control where in the array you are
void OnEnable()
{
//Register Button Events
nextCar.onClick.AddListener(() => NextCarButton());
previousCar.onClick.AddListener(() => PreviousCarButton());
}
private void Start()
{
cars.RemoveRange(1, 18);
if (FinishLineTouch.levelOneComplete == true) {
//Re-adding the same sprites here
}
}
public void NextCarButton()
{
if (i + 1 < cars.Count)
{
i++;
}
}
public void PreviousCarButton()
{
if (i - 1 >= 0)
{
i--;
}
}
void Update()
{
if (LevelSelect.isCarChosen == true) {
nextCar.interactable = false;
previousCar.interactable = false;
}
displayImage.sprite = cars[i];
}
}
Thanks in advance.
You have a small flaw in your logic here.
Saying that you add the car sprites in the inspector, removing all the sprites is not ideal.
You can have a list of all the sprites, and a list with available options (starts with sprite[0] and adds each time you complete the level).
However, if you want to know how to add them back after deleting them, you need to make a copy in a global variable, so it's more efficient to try what I am suggesting above.
You should have two different arrays, one for the sprites, and the other one for if the player unlocked it yet. Or you should create a custom class that has the sprite of the car, and if it is unlocked yet.
Sounds like all you really need to do is make sure the index doesn't exceed the unlocked cars
if(i + 1 < cars.Count && i + 1 <= amountOfFinishedLevels)
{
i++;
// and then you really should do this in both methods and not every frame
displayImage.sprite = cars[i];
}
and don't remove/readd any items at all.

FindObjectsWithTag bringing back objects in random order in Unity?

I'm attempting to make a level select that requires as little upkeep as possible as I intend on adding updates to add more levels (Unity Scenes) in unity.
To account for this I'm attempting to get the level select to create buttons for each level in the Unity Build settings, and then create a template object from a prefab in which it can map buttons it creates onto.
I have it mostly working, but for some reason, it's mapping the buttons in the wrong order, I'm trying to go down to up and Unity appears to be grabbing the Gameobjects is a random order.
Here is my code:
private Scene[] levels;
private int currentButtonId = 1;
public Transform buttonsHolder;
public GameObject buttonPrefab;
public GameObject buttonSlotsPrefab;
private GameObject[] levelButtonSlots;
private int currentLevelSlot = 0;
private int numSlotsToMove = 0;
private void Start()
{
var sceneCount = SceneManager.sceneCountInBuildSettings;
levels = new Scene[sceneCount];
for (var i = 0; i < sceneCount; i++)
{
// Beginning Setup
levels[i] = SceneManager.GetSceneByBuildIndex(i);
// Look for Level Placement Slots
levelButtonSlots = GameObject.FindGameObjectsWithTag("Level Slot");
// If there aren't enough Level Placement Slots make more by creating a template
if(levelButtonSlots.Length < levels.Length)
{
GameObject buttonSlots = Instantiate(buttonSlotsPrefab);
buttonSlots.transform.position = new Vector2(0, 10 * numSlotsToMove);
numSlotsToMove++;
}
// Go get those new placement slots
levelButtonSlots = GameObject.FindGameObjectsWithTag("Level Slot");
// Create Button
GameObject currentButton = Instantiate(buttonPrefab, buttonsHolder);
// Move it to the next slot
currentButton.transform.position = levelButtonSlots[currentLevelSlot].transform.position;
currentLevelSlot++;
// Add Text to a new button
TextMeshProUGUI buttonText = currentButton.GetComponentInChildren<TextMeshProUGUI>();
buttonText.text = (currentButtonId.ToString());
// Setup what which scene clicking a button will do
ButtonManager buttonScript = currentButton.GetComponentInChildren<ButtonManager>();
buttonScript.sceneToLoad = currentButtonId;
currentButtonId++;
}
}
The buttonsHolder variable is set in the editor and is the canvas.
The buttonPrefab is a TextMeshPro button prefab I set in the editor, it has the level Buttons tag and a simple script that loads the specified scene when clicked.
And the buttonSlotsPrefab is a gameobject prefab that I set in the editor, it has the Button Placement Slot tag and it contains 8 other empty gameobjects each with the level slot tag, I use these 8 objects as the guides as to where the buttons should be placed on runtime.
Again, my goal is to place the buttons going from bottom to top, but instead, Unity spits out this on runtime for no apparent reason:
I'm sorry about the naming convention some variables have, I'm tired and stressed as I've been at this 2 two days straight. I will fix those up once I get things working.
After further testing, I have noticed that when I first create buttonSlotPrefab, everything works perfectly, however, after restarting Unity (Not changing any files) when I run the game again after a restart the order gets randomized. Could this be a bug in the Unity Engine?
If i understant your problem, you have a problem between number of button and slot: you have slot spaced on axis y and you havent always the levelsolt[0] at the bottom of screen, so
you could order (ascending or descending i dunno) the levelslot on y axis before creating button: i am using Linq so add using System.Linq;
if (levelButtonSlots.Any())//Same thing than levelButtonSlots.Length > 0
{
levelButtonSlots = GameObject.FindGameObjectsWithTag("Level Slot").OrderBy(go => go.transform.position.y).ToArray();
}
or
levelButtonSlots = GameObject.FindGameObjectsWithTag("Level Slot").OrderByDescending(go => go.transform.position.y).ToArray();
I have added a test if no value in array, but maybe the error doesnt exist, i havent tested that

Loop sets color of the wrong object (Unity)

In my project, i have a Grid Layout Group, which contains buttons. Each button represents one level a player can choose to start. When i load the scene, in which the player has to choose the level, i want to disable and recolor all buttons that represent locked levels.
I am doing that with the following code:
public Button[] levelButtons;
int levelReached = 1;
for (int i = levelReached; i < levelButtons.Length; i++) {
levelButtons[i].interactable = false;
levelButtons[i].image.color = Color.red;
}
For some reason, he always recolors the first button (levelButton[0]), but changes the interactability on all other buttons (levelButton1 to levelButton[levelButton.length]).
Example
Why does Unity behave like that and how can i recolor the correct buttons?
I can't seem to find a solution for my problem and haven't found any relevant threads.
Any help would be appreciated.
The following code would be correct:
public Button[] levelButtons;
int levelReached = 1;
for (int i = levelReached; i < levelButtons.Length; i++) {
levelButtons[i].interactable = false;
levelButtons[i].GetComponent<Image>().color = Color.red;
}

Iterate through a list on button press

I've 3 objects in my list, but I only want the first element to be active. Then, when I press a button, I want the list to move forward one, so the next item in the list is now active and the first isn't.
I have the following code:
void OnClick()
{
for(int i = 0; i < activateTexture.Count; i++)
{
activateTexture[0].SetActive(true);
}
}
This only displays the first item in the list (which I want) but I'm stuck on working out how to move through the list.
Could someone please help me.
You're setting the initial texture active multiple times. Instead, keep track of the current one. Then, each time the code is triggered it can deactivate the one it's on, then move to the next one and activate it.
(Overly commented code below simply to make sure it's explained regarding this answer. I wouldn't normally put comments like this in my code)
void Start()
{
// Initialize all textures to be inactive
for(int i = 0; i < activateTexture.Count; i++)
{
activateTexture[i].SetActive(false);
}
// Activate the first texture
activateTexture[0].SetActive(true);
}
// Store the index of the currently active texture
private int activeTextureIndex = 0;
void OnClick()
{
// Disable the current
activateTexture[activeTextureIndex].SetActive(false);
// Increment the index
activeTextureIndex = (activeTextureIndex + 1) % activateTexture.Length;
// Activate a texture based upon the new index
activateTexture[activeTextureIndex].SetActive(true);
}
Note also that I've used the modulo operator % to cycle the list.
EDIT: Corrected due to concerns of integer overflow

Changing GameState based on an if statement?

I'll get to the point. Having an issue with a beginner level game im coding at the moment. I have 2 lists to store "Objects" that are in the game. One is for "diamonds" pushable blocks that are to be moved onto the "Goals". Once all diamonds are on the goals, the level should change. I'm currently using "GameStates" to load each level. Here is the snippet of my code I'm having issues with. What happens currently is the game will allow me to push the "diamonds" onto the "goals", but the gamestate will not change once I do this. Not sure what I am missing - any help is appreciated. Thankyou for your time!
void Level1Update(KeyboardState cKB, KeyboardState oKB)
{
for (int i = 2; i < diamondlist.Count; i++)
{
if ((Goallist[i].Position == diamondlist[i].Position))
{
CurrentGameState = GameState.Level2;
}
}
}
If I understood correctly, you want to set current game state to the next level only if all the diamonds and goals are on the same tiles. The following code ensures that.
void Level1Update(KeyboardState cKB, KeyboardState oKB)
{
int i;
for (i = 0; i < diamondlist.Count; ++i)
{
if (Goallist[i].Position != diamondlist[i].Position)
break;
}
// When breaked off the loop, i is never equal to list count
if (i == diamondList.Count)
CurrentGameState = GameState.Level2;
}

Categories

Resources