How to activate next Object in list?

Hi!
I’m trying to figure out how to update my “QuestBlog” when picking up “Blogs”…
I can only activate one object, but I want to update a list/array (I don’t know the difference) of gameobjects everytime I collect a “Blog”

Any easy way to make a list and how to access this list? I can’t find anything like this online :S

        if (collider.tag == "Blog")
        {
            GameManager.Instance.Blog++;
            Destroy(collider.gameObject);
        }

The GameManager Script:

    public int txtBlog;
    public GameObject nextBlog; //I only know I to activate 1 Object... :S

    public int Blog
    {
        get
        {
            return txtBlog;
        }
        set
        {
            txtBlog= value;   
            PlayerPrefs.SetInt("txtBlog", PlayerPrefs.GetInt("txtBlog") + 1); //This update the Blogs collected.

            Instance.nextBlog.SetActive(true);  //I can only figure out how to Activate ONE gameobject, thus I'm asking for help on how to turn this into a list and keep activating one gameobject at the time every time I collect a Blog.
        }
    }

A List<> is basically an array of some type with a dynamic length - or rather the size of the internal array is managed automatically for you when you insert new objects and require more size.
An array is a linear reservation of memory based on some type. So an int[ ] of size 10, is the same as declaring 10 int variables int0, int1, …, int9. Instead you can access the array (let’s call it intArray) by using an indice like so: intArray[0], intArray[1], … and so on. This has advantages since it allows you to iterate over many related pieces of data in a loop, doing something for all intArray elements, like adding them together to get the sum for example.
Lists work very similar, the main difference is that they are (wrapper) objects, containing an array and managing its size.

While i dont mind explaining basics like the above, if you dont know how to work with arrays or lists i highly recommend you look that up and do some tutorials and exercised to practice with them (and loops). Arrays and loops are probably the most important basic tools a programmer should know about. Without them you are severely crippled in what you can do or cant do.

As for your problem, if you want a list of Blogs, do just that. Define a List blogList = new List();
Now you can add elements of type Blog to it by calling blogList.Add(someNewBlogObject), and remove them the same way using Remove(), or removing the blog at a specific index by calling RemoveAt() instead.
You can then access the size of the list with blogList.Count, and access the element at index i with blogList[ i] just like you would do it with arrays. For example, to disable the last element you could write blogList[blogList.Count-1].gameObject.SetActive(false). The -1 is required, since the first element is placed at index 0, so with 10 items in an array/list, the last element is at index 9.

Hope this helps. If you have further questions feel free to ask, but again i’d highly recommend you to do some exercises and readups on arrays and lists, as well as loops in general. You will want to have a really solid understanding of these topics.

Thank you, I’ve always found it to hard to understand a text-explanation like yours, but it was well explained and I think I know what I need. First of I will try to find a good list-tutorial with “visuals” and proper examples - hopefully I can make it work. (Many tutorials only show How to make the list, but not how to use it in-game.
I’ve already made one game without any lists, and I agree with you, it really sucks not to know this stuff, and now that you pointed it out I think its time to give it a go.
THanks for the advice, I think I List blogList = new List(); is what I’m looking for!

I still cant find the type of tutorial I need… I made it work in the most inconvineint way though.
But this is the function I need but I want to use a List/array instead… Not sure what Im looking for…

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HealthManager : MonoBehaviour
{
    public GameObject health100;
    public GameObject health90;
    public GameObject health80;
    public GameObject health70;
    public GameObject health60;
    public GameObject health50;
    public GameObject health40;
    public GameObject health30;
    public GameObject health20;
    public GameObject health10;
    private static int healthCheck;
    // Use this for initialization
    void Start ()
    {
        health100.SetActive(true);
        health90.SetActive(false);
        health80.SetActive(false);
        health70.SetActive(false);
        health60.SetActive(false);
        health50.SetActive(false);
        health40.SetActive(false);
        health30.SetActive(false);
        health20.SetActive(false);
        health10.SetActive(false);
    }
   
    // Update is called once per frame
    void Update ()
    {
        healthCheck = PlayerStatus.health;  //  Update our score continuously.
        if (healthCheck == 90)
        {
            health100.SetActive(false);
            health90.SetActive(true);
            health80.SetActive(false);
            health70.SetActive(false);
            health60.SetActive(false);
            health50.SetActive(false);
            health40.SetActive(false);
            health30.SetActive(false);
            health20.SetActive(false);
            health10.SetActive(false);
        }
        else if (healthCheck == 80)
        {
            health100.SetActive(false);
            health90.SetActive(false);
            health80.SetActive(true);
            health70.SetActive(false);
            health60.SetActive(false);
            health50.SetActive(false);
            health40.SetActive(false);
            health30.SetActive(false);
            health20.SetActive(false);
            health10.SetActive(false);
        }
        else if (healthCheck == 70)
        {
            health100.SetActive(false);
            health90.SetActive(false);
            health80.SetActive(false);
            health70.SetActive(true);
            health60.SetActive(false);
            health50.SetActive(false);
            health40.SetActive(false);
            health30.SetActive(false);
            health20.SetActive(false);
            health10.SetActive(false);
        }
        else if (healthCheck == 60)
        {
            health100.SetActive(false);
            health90.SetActive(false);
            health80.SetActive(false);
            health70.SetActive(false);
            health60.SetActive(true);
            health50.SetActive(false);
            health40.SetActive(false);
            health30.SetActive(false);
            health20.SetActive(false);
            health10.SetActive(false);
        }
        else if (healthCheck == 50)
        {
            health100.SetActive(false);
            health90.SetActive(false);
            health80.SetActive(false);
            health70.SetActive(false);
            health60.SetActive(false);
            health50.SetActive(true);
            health40.SetActive(false);
            health30.SetActive(false);
            health20.SetActive(false);
            health10.SetActive(false);
        }
        else if (healthCheck == 40)
        {
            health100.SetActive(false);
            health90.SetActive(false);
            health80.SetActive(false);
            health70.SetActive(false);
            health60.SetActive(false);
            health50.SetActive(false);
            health40.SetActive(true);
            health30.SetActive(false);
            health20.SetActive(false);
            health10.SetActive(false);
        }
        else if (healthCheck == 30)
        {
            health100.SetActive(false);
            health90.SetActive(false);
            health80.SetActive(false);
            health70.SetActive(false);
            health60.SetActive(false);
            health50.SetActive(false);
            health40.SetActive(false);
            health30.SetActive(true);
            health20.SetActive(false);
            health10.SetActive(false);
        }
        else if (healthCheck == 20)
        {
            health100.SetActive(false);
            health90.SetActive(false);
            health80.SetActive(false);
            health70.SetActive(false);
            health60.SetActive(false);
            health50.SetActive(false);
            health40.SetActive(false);
            health30.SetActive(false);
            health20.SetActive(true);
            health10.SetActive(false);
        }
        else if (healthCheck == 10)
        {
            health100.SetActive(false);
            health90.SetActive(false);
            health80.SetActive(false);
            health70.SetActive(false);
            health60.SetActive(false);
            health50.SetActive(false);
            health40.SetActive(false);
            health30.SetActive(false);
            health20.SetActive(false);
            health10.SetActive(true);
        }
    }
}

Instead of defining the GameObjects variables health10, health20, …, health90, health100, define a GameObject[ ].

[SerializeField] private GameObject[] healthObjects = new GameObject[10];

You can assign the array values in the inspector in a similar way as you did with the normal variables. Also, as a small note: [SerializeField] can be used if you want to see it in the inspector, but the variable should stay private. Which is mostly the case.

In Start() you can now replace, for example, health100.SetActive(true) with healthObjects[0].SetActive(true). You could potentially just replace all 10 lines in that way, but since we use an array, we can also use loops for convenience now!
So create a simple for-loop over i, and then only write healthObjects[ i].SetActive(false). To turn one of them true, do that manually afterwards or do it conditionally in the loop. For 10 items this may not seem like that much less effort, however imagine you had 1000 or 1.000.000 items in your array. You wouldnt want to do it manually!

Before discussing your update, i got to ask: can your health be between, for example, 10 and 20? If that’s the case you dont want to check for healthCheck == 10. Imagine you had 11 health and took 2 damage. Health would never be equal to 10 then, thus not triggering the effect. Instead you’d want to check if it’s over 0, but below 10 or something along those lines. Further, thinking about it as heart symbols, you probably dont want to disable the health100 object only if health falls below 90 - you’d probably want to disable it as soon as health is smaller than 100. Even tho, if it was hearts, then you’d enable every object below some value, so i’m not quite sure what you are doing there^^
If you cant have values between the full 10’s, then i recommend just working with 0-10 health instead.

As for Update(), i’d start by writing a method that disables all of your GameObjects (in the array). For that you’d use a loop, which is basically what i described above. You can even call your method in Start() and only manually activate the max-health object then.
Update() can be done rather easily now. However, for performance reasons i would recommend you introduce a variable “previousHealth” to keep track of the last health value you observed (in the last update check). Set it to PlayerStatus.health in Start() and then always reassign it to PlayerStatus.health at the bottom of Update().
In each Update you can now check if previousHealth != healthCheck. We dont need to do anything if the health did not change, so all of the following only happens when they are not equal. Now we do the following:

  • Call your method to disable all objects
  • Calculate int index = (int) (healthCheck / 10);
  • healthObjects[index].SetActive(true);

Now we enabled the correct healthObject. You may need to adjust the index calculation based on what i described above. I can also post code examples if you want me to, but i thought it would be better if you followed your own little text-guide and figured the rest out yourself.

Hope this helps, and always keep having fun :slight_smile:

1 Like

Thanks! Yeah, the script looks cleaner now with an Array (correct?) but the Update function remains the same…

   [SerializeField]
    private GameObject[] healthObjects = new GameObject[10];
    private static int healthCheck; 

    // Use this for initialization
    void Start ()
    {
        healthObjects[0].SetActive(true);
        healthObjects[1].SetActive(false);
        healthObjects[2].SetActive(false);
        healthObjects[3].SetActive(false);
        healthObjects[4].SetActive(false);
        healthObjects[5].SetActive(false);
        healthObjects[6].SetActive(false);
        healthObjects[7].SetActive(false);
        healthObjects[8].SetActive(false);
        healthObjects[9].SetActive(false);
    }
   
    // Update is called once per frame
    void Update ()
    {
        healthCheck = PlayerStatus.health;
        if (healthCheck == 90)
        {
            healthObjects[0].SetActive(false);
            healthObjects[1].SetActive(true);
            healthObjects[2].SetActive(false);
            healthObjects[3].SetActive(false);
            healthObjects[4].SetActive(false);
            healthObjects[5].SetActive(false);
            healthObjects[6].SetActive(false);
            healthObjects[7].SetActive(false);
            healthObjects[8].SetActive(false);
            healthObjects[9].SetActive(false);
        }
        else if (healthCheck == 80)
        {
            healthObjects[0].SetActive(false);
            healthObjects[1].SetActive(false);
            healthObjects[2].SetActive(true);
            healthObjects[3].SetActive(false);
            healthObjects[4].SetActive(false);
            healthObjects[5].SetActive(false);
            healthObjects[6].SetActive(false);
            healthObjects[7].SetActive(false);
            healthObjects[8].SetActive(false);
            healthObjects[9].SetActive(false);
        }
..............................

The old objects remain active unless I de-activate them like this SetActive(false);… Is that where the Loop thing comes in? I don’t know what a Loop is and I tried to Google. I found this “for(int i = 0; i < numEnemies; i++)” is that a loop? But that seem to be an Int, not an array of Objects? And how do I implement that to my script? :S

Ah, so the Player has 100 points in Health and he can loose 4, 7, 10 ,14 points of health depending on how he get hurt. BUT, this function I’m trying to do is similar to DOOM (imagine the small window with the Doom-Guys face, the more damage he take the bloodier he becomes.) So on my game, if you go into the start menu you can see a picture of the character, 100% = Healthy looking 20% = looking bad. Thats why my script is fetching “PlayerStatus.health;” from another script and I will update this “Graphics Menu” with new picture of the hero based on how much health he has. Id that make sense?

Yes Pls! ^^
You mentioned “healthObjects[ i].SetActive(false)” and I guess that the [ i ] stands for “inforamtion inside the array”
But how I locate it and update it I dont know :S So i basaclly want all objects in the arraw to remain False unless the players health is at 90 etc. (Ah, you’re right, I should change ==90 to <= 90 or something like that, otherwise the image does not update.)

Yes that makes sense, but what i said is still the case. Let’s imagine i lose 7 health every time. My health values will be, in that order, 100 → 93 → 86 → … → 30 → 23 > … and so on. The first time the number is actually equal to a multiple of 10, is 30. So you are visually completely healthy, and then beaten to a pulp if the condition is right. If at that specific instance i get damaged for 4 instead, and then 7 again, the image may never update but you’d still end up dead, while visibly at full health. Seeing your last sentence, i believe you realised this, but i wanted to make sure.

if (health == 90) // only triggers if it's exactly 90, thus can be missed!
if(health < 100 && health >= 90) // triggers in the range of 90 to <100 (so 99 for integers and 99.99.. for floats)

Yes that’s a loop, and you basically already answered your own question :wink:
The i in the loop is a counter. The i in healthObjects[ i] refers to the index at which position we want to access the item. You already did that yourself when you called, for example, healthObjects[0].SetActive(true). Now just replace the 0 with i, with i being an integer variable. The loop simply calls the body of the function and counts up the i counter variable. You can read it out loud as follows: “for all int i starting at 0; execute the loop body while it is true that i < someNumber; afterwards increment i by one and repeat”. So it basically executes the body with a different i, for all i between 0 and (excluded) someNumber.
That’s the shape of most normal for-loops. Like literally 99% of them. However, the loop itself is rather flexible, so you could fill the 3 areas with something else, as long as it’s a definition (or multiple), a condition and a final action after each loop iteration.

I will help you out by posting the function you need in a bit, but (!) you definitely must look up how to use arrays and loops properly. These two things make up one of the most basic, yet some of the most powerful tools you have as a programmer. Working without them can be compares to working without powertools in reallife.
So do yourself a favor and do some exercises on that. Any basic C# tutorial should cover it. Or even better yet, i can recommend the tutorial series “Introduction to Game Development (Unity and C#)” by Sebastian Lague on youtube. It goes over everything that’s important, and while arrays and loops only come up in edisode 19 or so, following the series offers you ton of practical examples and so on.
Video link

Let’s get to your problem. As i described in my previous post, you’ll want to create a function that takes care of simply disabling all the elements by using a loop:

private void DisableAll(){
    // complete the body once for each number between 0 and <length of our object array
    for(int i = 0; i < healthObjects.length; i++){ // btw it's < no <= because length of our array may be 10, but the last object is at index 9 since we start at 0!
        healthObjects[i].SetActive(false); 
        // note that I only write [ i] with a space in plain text since it otherwise makes text italic on the forum. The space is not needed
    }
}

Now you can call this method to disable all of your objects and then only enable the correct one based on the current health in Update(), making the whole thing already a lot smaller. However, as this gets called every frame, we can do something to do both, improve performance, as well as make it even smaller code-wise.

I basically wrote it in my last comment, so i’ll keep it short here. We want to keep track of the previous health value, so introduce a variable for that and set it to the current health value at the bottom of Update(). At the top of update we want to check if the previous health is not equal to the current health. If that’s the case, then it obviously changed between last frame and now, meaning only now it is needed to actually disable objects and reenable the right one to update the image. And that’s what we will do inside the if body, we call the function to disable all objects and then reenable the right one. We can get the index of the right one by calculating int index = (int) (healthCheck / 10); and that’s basically it. Now only use the index to access the right array element and enable it.

Hope this helps :slight_smile:

OMG, its working.
I watched the videos you recommended but most of those tutorials are to difficult to understand, I need tons of visuals and preferably “screen capture-tutorials” so I can see where he type the functions, and how it translate into Unity. In some of the videos he jump back and forth from Visual Studio to Unity which was great and what I need, in order to understand but in the Loop/Array video he only showed some code on a grey screen and it was too hard to grasp for me (imagine if I zoomed-in to a random Forest on GoogleMaps and ask you “Where is this?”… That’s how I feel when I see random scripts without context. :stuck_out_tongue: (we all learn differently I suppose :P) Either way I bookmarked the videos cuz most of them were really good. I really need to learn to Loop stuff before I start my next project, but for now I think what you taught me is all I need in this game.

Here is the final script and it works exactly how I want it to, in case anyone else need this!

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HealthManager : MonoBehaviour
{
    [SerializeField]
    private GameObject[] healthObjects = new GameObject[10];
    private static int healthCheck; 
    // Use this for initialization
    void Start ()
    {
        healthObjects[0].SetActive(true);
    }
   
    // Update is called once per frame
    void Update ()
    {
        healthCheck = PlayerStatus.health;
        DisableAll();
        if (healthCheck < 100 && healthCheck >= 90)
        {
            healthObjects[0].SetActive(true);
        }
        else if (healthCheck < 90 && healthCheck >= 80)
        {
            healthObjects[1].SetActive(true);
        }
        else if (healthCheck < 80 && healthCheck >= 70)
        {
            healthObjects[2].SetActive(true);
        }
        else if (healthCheck < 70 && healthCheck >= 60)
        {
            healthObjects[3].SetActive(true);
        }
        else if (healthCheck < 60 && healthCheck >= 50)
        {
            healthObjects[4].SetActive(true);
        }
        else if (healthCheck < 50 && healthCheck >= 40)
        {
            healthObjects[5].SetActive(true);
        }
        else if (healthCheck < 40 && healthCheck >= 30)
        {
            healthObjects[6].SetActive(true);
        }
        else if (healthCheck < 30 && healthCheck >= 20)
        {
            healthObjects[7].SetActive(true);
        }
        else if (healthCheck < 20 && healthCheck >= 10)
        {
            healthObjects[8].SetActive(true);
        }
        else if (healthCheck < 10 && healthCheck >= 0)
        {
            healthObjects[9].SetActive(true);
        }
    }
    private void DisableAll()
    {
        // complete the body once for each number between 0 and <length of our object array
        for (int i = 0; i < healthObjects.Length; i++)
        { // btw it's < no <= because length of our array may be 10, but the last object is at index 9 since we start at 0!
            healthObjects[i].SetActive(false);
            // note that I only write [ i] with a space in plain text since it otherwise makes text italic on the forum. The space is not needed
        }
    }
}

I’m sure it can be cleaner but… I’m happy it works and thank you for taking time helping!

Again you can replace the entire update function content with something along those lines:

int curHealth = PlayerStatus.health;
if(prevHealth != curHealth){
    DisableAll();
    int index = (int)(curHealth / 10);
    healthObjects[index].SetActive(true);
}
prevHealth = curHealth; // prevHealth is an int variable outside of Update()
1 Like

It took me ages to understand how the prevHealth thing worked. Didn’t understand it should a private/public Int in the top of the script :stuck_out_tongue: and then I was messing around with the order of the Gameobjects in the inspector FOREVER :stuck_out_tongue: But finally understood what you mentioned before about Gameobjet[0] is the first one and then its counting from object [9] in the list… This is how the list should look like this:

healthObjects100% //This should be on top
healthObjects10%
healthObjects20%
healthObjects30%
healthObjects40%
healthObjects50%
healthObjects60%
healthObjects70%
healthObjects80%
healthObjects90% //and then the script will count from here… (In case anyone need help)

I keep on inserting them in correct value-order order and Thus, the health value and the Image did not match.
Anyway, thank you soo much for the help! I think i can use this script in a ´different place as well!

Sorry, i thought my comment about prevHealth was enough. Was too lazy to introduce a class and Update() function for one variable declaration hehe. I wish the insert-code textfield made it easier for us to indent code lines manually.

But:

This doesnt seem right. But i guess that’s my fault. The order should be from 10% at index 0 to 100% at index 9. What made the images not match is that i made a small mistake in the index calculation, since there is no index 10. So when you are at full health (by being healed for example) that code would even throw an error.
It should be:

int index = (int)(curHealth / 10) - 1;

Now the max health properly gets mapped to the last index. When in doubt, always go through some examples in your head to make sense of a calculation (where i should eat my own advice :p). So if we are at 100 health, 100/10 = 10 -1 = 9, with 9 being our last index, containing the “full health” image. If our health drops to, for example 99, then 99/10 = 9 (because it’s integers, not floats!), 9 - 1 = 8, which is the index of the 90% health image and so on.
However, we run into a problem when health falls below 10% (9 / 10 = 0 - 1 = -1), since no image exists for that, so we would effectively end up at index -1, causing an exception. It makes sense that this happens, since you have 10 states, but “100%” only covers a single value, while everything else covers a range of 10 values, so we are effectively missing coverage for 10 values - in this case the ones between 0 and 9. To prevent this, either add a new image for “0%” health, which triggers below 10%, or add “&& curHealth >=10” to the if condition.

Also there is no “right” or “wrong” insertion order to arrays in general. As long as you can insert the elements in some way that makes sense to you, which allows you to find the correct element again, that’s fine. The problem here was just that i made a mistake in the index calculation (even tho, there is still the problem described above). Thinking in right vs wrong insertion orders for arrays may cause problems for you later on, which is why i mentioned this. Rather think about if things fit the way you intended them to work.

So, after (hopefully) clearing this misunderstanding, i wish you best of luck with the rest of your project :slight_smile:

1 Like

Ah, that little details made it work! Now I can insert them in “correct” order that “make sense”!