Making waves spawn randomly

I was wondering how to make the game to spawn waves in random order and loop that, for example instead of wave 1,2,3,4 to have it randomized, but in a way that all the different waves will be spawn once for example wave 2,4,3,1 loop 4,1,3,2 loop etc. and not that it randomizes completely. I don’t want it that for example the waves spawn completely random and that one wave doesn’t spawn because it “doesn’t get picked” by the randomizer.
It’s a bit hard for me to explain, since English isn’t my main language but I hope you understand what I mean

Hi Kederin,

You could create two List variables: A and B. A contains the available indices, B is empty.

In a loop, you call the Random.Range method with all available indices in A. Then you add the element at the returned index to B and remove the element from A. If necessary, look the List class up in the C# API or elsewhere on the internet.

And for spawning your waves, you use your loop, but instead of accessing the waves list directly, you use the indices stored in B.

To visualise the idea behind A and B:

A[] {
 [0]: 0,
 [1]: 1,
 [2]: 2,
 [3]: 3
}

B[] {
 [0]: 3,
 [1]: 1,
 [2]: 0,
 [3]: 2
}

Bear in mind that Lists and arrays start at index 0, and the last element in the array is therefore (n - 1) where n is the number of elements/slots.

Good luck! :slight_smile:


See also:

1 Like

Thanks, I’m going to give it a try after I finish the lessons until the next quiz. When run into problems or I got it to work I will let it know.
Looks like a difficult challenge for me to be honest but I want to try it

It’s not as difficult as it looks. You already have 90% of the knowledge you need to make your idea work. And the remaining 10%, the Remove method of the List class, can be found on the internet. The challenging part is mainly to develop the concept. If you are able to draw the logic on a sheet of paper, I am sure that you will also be able to translate it into code. :slight_smile:

I kinda figured it out, or at least the randomness, that was the easy part of course. all I had to do is change a line in SpawnAllWaves coroutine:

 var currentWave = waveConfigs[waveIndex)];

changed into:

 var currentWave = waveConfigs[Random.Range(0, waveConfigs.Count)];

But I still don’t really know how to implement it in a way as described above. I get the idea on paper and in my head. I also looked on the internet which made me understand lists more. But I still have no idea how exactly on how to approach my idea.
as I’m writing this I’m in section 118. I’ll continue finishing this chapter and after I will try to personalize my game some more including this idea.

Your approach looks fine so far, and you are definitely on the right track. The remaining problem is: Random.Range “randomly” returns an index between 0 and waveConfigs.Count. There is no restriction which tells it to return each number/index only once, and this option is not built-in, so you’ll have to write it yourself.

Try what I suggested with the two List objects. Focus on the lists and their output only. That’s the challenging part where you generate the index list, which you use at a later juncture. Don’t mix this task by trying to make the spawning happen simultaneously. Otherwise, this challenge will become unnecessarily complex, and the key to complete a complex solution is to complete a bunch of simple “modules” which form the complex solution.

Once your code is able to generate a List with indices in a “random” order, all you have to do is to iterate over that List with a loop and use the value from the List in your waveConfigs array.

Hi,

I made some progress regarding the lists. I made the 2 extra lists called availableWaves and usedWaves. then I changed the coroutine a bit. From:

 private IEnumerator SpawnAllWaves()
    {
        
        for (int waveIndex = startingWave; waveIndex < waveConfigs.Count; waveIndex++)
        {
            var currentWave = waveConfigs[Random.Range(0, waveConfigs.Count)];
            yield return StartCoroutine(SpawnAllEnemiesInWave(currentWave));
        }
    }

To this:

 private IEnumerator SpawnAllWaves()
    {
        
        for (int waveIndex = startingWave; waveIndex < availableWaves.Count; waveIndex++)
        {
            var currentWave = availableWaves[Random.Range(waveIndex, availableWaves.Count)];
            yield return StartCoroutine(SpawnAllEnemiesInWave(currentWave));
            availableWaves.Remove(currentWave);
            usedWaves.Add(currentWave);
            Debug.Log(currentWave);

            if(availableWaves.Count <=0)
            {
                availableWaves = waveConfigs;
                usedWaves.Clear();
            }
        }
    }

I changed the original List to the “availableWaves” list and I’m removing the used waves from the availableWaves list and then add those waves to the “usedWaves”.
Then when the availableWaves list is empty I use the original List to add all the waves back and then also clear the usedWaves list, with an if statement

The only problem is that when I use the line availableWaves = waveConfigs; that after the second loop the waveConfigs List also removes the used waves, but that List needs to keep all the waves. I’m still trying to find out a good way to refill the availableWaves list with all the waves. Any tips?

In the link there is a video of me testing the script, in the inspector you can see the problem I’m encountering.

Thanks a lot for taking the time

Your implementation is slightly different from what I had in mind but I like it. :slight_smile:

That’s why I suggested two List objects. One which contains the available waves, and one which you use in SpawnAllWaves. What you need to do is to copy the first wave and populate the second list with the data from List #1.

Do not assign List #1 to the variable for List #2. We are dealing with reference types here, so you would assign a reference to the object to the variable, not a new List. If you removed elements from the list via variable #2, the original list would get manipulated as well.

Make two lists:

  • A List assigned to wavePool, which you don’t manipulate during runtime. No “Remove” method call here!
  • A List assigned to availableWaves, which you populate (Add()) with the elements from wavePooland which you may manipulate by removing (Remove()) used elements.
1 Like

From here I don’t seem to understand what to do. So in total I have 3 lists. waveConfigs, availableWaves, and usedWaves

So like you said waveConfigs is the list I don’t want to manipulate. the availableWaves list is the list where I want to remove the elements from and when it’s empty I want to ‘refill’ the elements using the waveConfigs. the usedWaves list is just to visualize the elements being (re)moved.

I tried using the CopyTo method. to copy the elements from waveConfigs back to the availableWaves list when it gets empty but I don’t really understand it. the explanation confuses me a bit too. Since there they seem to make use of arrays and lists with strings. Am I overlooking something?

Thanks in advance

I’m sorry for the confusion. I should have been more precise. You are right. We have 3 Lists. When I talked about 2, I meant the 2 which you need for your “random indices”.

Here is the potential solution, which I wrote from scratch and didn’t test:

The SpawnAllWaves method
private IEnumerator SpawnAllWaves()
{
    int numOfElements = waveConfigs.Count;
    int[] indices = GetRandomIndices(numOfElements);

    foreach (int index in indices)
    {
        var currentWave = waveConfigs[index];

        Debug.Log("current index: "+ index);
        Debug.Log("currentWave: "+ currentWave);

        yield return StartCoroutine(SpawnAllEnemiesInWave(currentWave));
    }
}
The GetRandomIndices method
private int[] GetRandomIndices (int numOfElements)
{
    // Create index pool and polulate it with { 0, 1, 2, 3, … }
    List <int> indexPool = new List<int>(numOfElements);
    for (int i = 0; i < numOfElements; i++) { indexPool[i] = i; }

   // Create target array for random indices
   int[] randomIndices = new int[numOfElements];

   // Populate 'randomIndices' with elements from 'indexPool'
   for (int j = 0; j < numOfElements; j++)
   {
       int randomIndex = Random.Range(0, indexPool.Count);
       int elementFromIndexPool = indexPool[randomIndex];
       randomIndices[j] = elementFromIndexPool;

       // Remove element from 'indexPool' at index 'randomIndex'
       indexPool.RemoveAt(randomIndex);
   }

   return randomIndices;
}

You do not need a GenerateRandomIncides method. I created one for making the code more readable. I also decided against a list and opted for an int array as we don’t add or remove things anyway, and they are more performant. For this little operation, it’s a pure matter of personal preference, though.

I will try to test of this works. So I should add the 2 methods above to my code? And do I need to replace my SpawnAllWaves method with the one above. I only tried out the SpawnAllWaves method so far. But trying to put in GetRandomIndices results in more errors.

I can show the errors:

these errors results from putting in the 2 methods above by the way
Sorry, I didn’t have much time to dive more into it, but is there something else that needs to be done? I will look into it more later.

Thanks a lot for taking the time

There is only one new method in my solution. The other one is more or less a modified version of yours.

I made a little mistake in my method declaration. The parameter must be defined with a type. The type is int. The same happened in the foreach loop. I accidentally deleted my original code before I was able to post it. When I rewrote it, I was a bit in a hurry. I’m editing my previous post to fix these two issues.

Before you proceed, I’d suggest to read my code and try to understand it. Maybe there are more typos. Maybe there is a flaw in the logic. If you grasp the idea behind the code, you’ll be able to fix and modify it. :slight_smile:

Sorry for the late reaction.

As of now I seem to get an error.

I assume the index list is still empty and needs to be filled somewhere in the code, Is this where the used waves are stored, or am I misunderstanding?
I think I understand most of the code. Just not this error, I looked it up and the answers said mostly that it is because there is an empty list. Am I going in the right direction with this? I’ve never worked so much with lists and arrays so this is all still very new to me :sweat_smile:

ArgumentOutOfRange / IndexOutOfRange exception means that the code tried to access a non-existent index in an array, List or another collection. The first index in an array and List is 0, hence the last index is the length minus 1. For example, if the array has got a length of 2, and the code tries to access index 2, you will get this error message because there are only two “slots” in the array: at index 0 and at index 1.

I think so. Maybe you have already completed your solution while I’m typing this. And if not, there should not be much missing anymore to make your idea work. :slight_smile:

Yes, the Lists and Arrays start with 0, I’ve read to code more carefully and I can’t quite yet make the connection, but I still think it has to do with the index that is empty. I don’t really know how to rewrite the code. I do mostly understand the code, even more with the help of the comments, although I get a bit lost with the indexPool.

Another thing is that the lists I made are not really used so I was also thinking that I might need to change the code in that way, but again I’m not sure of that.

The thing is that I seem to understand the code and the problem mostly but I never really know how to tackle the problem.

Have you already tested my code “as it is”? I wrote it from scratch, and I don’t know if I added a typo somewhere or if there is a flaw in the logic.

I never really know how to tackle the problem.

From my experience, that’s the difficult part in programming, game development and basically in all fields. Not typing code. Writing C# or something else is just a way to communicate one’s concepts/ ideas/ solutions.

That’s why I suggested to break the problem down into single tasks which are easy to solve and whose results can be evaluated quickly.

If you found my suggested idea difficult to grasp, imagine the following: There is a group of five people. Everybody has got a name. And the default order of these people is alphabetical.

  • Anthony
  • Ben
  • Chris
  • Dan
  • Edgar

A name is not a person. In programming, we would say that a person is mapped to a name or vice versa.

Now you want to meet these people in a random order, one person at a time. Instead of inviting the entire group and pushing the people around, you put their names on little sheets of paper and shuffle those sheets to get your random order. Each name still refers to the same person as before.

If this makes sense, take another look at my first reply (#2). The elements in Lists and Arrays don’t have names, they have numbers. However, for the concept, it does not matter if I put numbers or names on the sheets of papers.

This is very likely not the only potential solution, and I have to admit that I am sometimes thinking in a roundabout way. For this reason, don’t worry if you cannot fully understand what I’m trying to say. :wink: