Iterate through a list on button press - c#

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

Related

PlayerPrefs not saving unlocked levels

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!

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

Unity3d(Editor): Opening Multiple files using EditorUtility.OpenFilePanel()

I was watching this tutorial and did complete it, now I want to extend it.
I want to open multiple images using FileExplorer in unity and then able to show the images based on the slider's value as seen in this image:
Any help appreciated.
This doesn't answer the question but what you asked in the comments
Can you give me a little code for "just create a list and have the opened images in it, when the value of the slider changes, just set the Raw Image's sprite to that of the image in the list by using an int to get the image from the list."?
[RequireComponent(typeof(RawImage))]
public class ImageSwitcher : MonoBehaviour
{
private RawImage image;
public Slider SliderComponent;
// Get the textures somehow
public List<Texture>() textures = new List<Texture>();
private void Awake()
{
image = GetComponent<RawImage>();
if(!SliderComponent)
{
Debug.LogError("No SliderComponent referenced!", this);
return;
}
// Make the slider accept only whole numbers
SliderComponent.wholeNumbers = true;
SliderComponent.value = 0;
SliderComponent.minValue = 0;
// Index is 0 based so can maximal be list count -1
SliderComponent.maxValue = textures.Count - 1;
// Register a listener for onValueChanged
// Remove the listener first to avoid multiple listeners added
SliderComponent.onValueChanged.RemoveListener(OnSliderChanged);
SliderComponent.onValueChanged.AddListener(OnSliderChanged);
}
private void OnDestroy ()
{
// Always clean up listeners when not needed anymore
SliderComponent.onValueChanged.RemoveListener(OnSliderChanged);
}
// Use this to change the Texture list
// and Max value of the slider afterwards
public void UpdateSlider(List<Texture> textures)
{
// Update the texture list
this.textures = textures;
// Update the max value of the slider
SliderComponent.maxValue = textures.Count - 1;
// Unity might automatically clamp the slider value
// after the maxValue was changed
// But just to be sure we can do it as well
SliderComponent.value = Mathf.Clamp(SliderComponent.value, 0, textures.Count - 1);
}
// Called when the slider value is changed
private void OnSliderChanged()
{
// Get the value as int
int index = Mathf.RoundToInt(SliderComponent.value);
if(index < 0 || index > textures.Count - 1)
{
// Should actually be impossible but just in case log it
Debug.Log("Slider produced impossible index: " + index, this);
return;
}
// Get according texture from list
var texture = textures[index];
// Set texture
image.texture = texture;
}
}
However this doesn't completely solve your question
Unity3d(Editor): Opening Multiple files using EditorUtility.OpenFilePanel()
as mentioned in this thread it is not possible since EditorUtility.OpenFilePanel returns only one single filepath as string.
So short answer would be: It's (currently) not possible.
There is an open vote for adding that functionality for multiple selections so you might want to vote there.
One idea of mine would be to try to select a folder path instead and load all textures from that folder but that is only a workaround and not really what you requested.
But I hope the rest helps you a bit :)

Unity - Logic to pick up objects and drop them one by one at different positions

Please look at example - http://www.mathplayground.com/mancala.html
Can anyone suggest the logic to :
1) spawn objects at positions
2) Pick up all objects on click and distribute them one by one.
3) Is it better to create all objects or instantiate them on the fly. ?
I tried code below but it just instantiates all objects at once.
if (HoleHandler.gemCount_Hole1_Update_Flag == true)
{
foreach (GameObject g in gemList1)
{
Destroy(g);
//want to add a time delay of 2 secs here
}
if (gemCount_Hole1 > 0)
{
for (int i = 0; i < gemCount_Hole1; i++)
{
int Gem_prefabIndex = UnityEngine.Random.Range(0, 9);
gemList1.Add(Instantiate(Gem_prefabList[Gem_prefabIndex], new Vector2((xPos_Hole1 + (Random.Range(-20, 20))) * 2.0F, (-229 + (20 * i))), Quaternion.identity));
}
}
}
I'm not 100% sure of what you're trying to achieve but I will answer as best I can.
For a start, any gameobject you are going to be instantiating (spawning) at run time should ideally be done so from a prefab.
Secondly, to spawn them at random intervals you want to be checking if they should be spawned at different time frames. This can be achieved through a co-routine or the Update function. I would recommend Update if this is new to you.
Update is called every frame.. and it's with this that you can achieve timed events. You can use a variety of helper methods to determine the time since the last frame or the real time elapsed.
For example
public class MyGameObject : Monobehaviour {
void Start() {
//This is called first, use it to set up whatever you want.
}
void Update() {
//This will be called every frame.
//Each frame or time lapse will determine if I should spawn
// a new gameobject.
}
}
Update
After looking at the game you have linked in your post I can offer the following advice.
Something like the following may point you in the right direction.
public int[] gemsInCups = new int [] {4,4,4,4,4,4,0,4,4,4,4,4,4,0};
public void Distribute(int position){
int gems = gemsInCups[position];
for(int i = position + 1; gems > 0; i++){
gemsInCups[position] ++;
gems --;
//Check the end of the array has not been reached.
//If it has, start distributing again from the first position provided
// there are still gems to distribute.
}
}
You will need some additional logic to finish this.
What you should remember is, I usually find it much more manageable keeping my data and my view (gameobjects) under different scopes... but the view will change to reflect the data and does not directly alter it. Now you know how many gems there are in each cup, you can simply update this each frame.

Categories

Resources