- Home /
What is the most efficient way to spawn objects with probability weights in this scenario?
In my game, I currently have a list of gameobjects containing fruits. These fruits currently spawn randomly, but with no probability weights attaches, so any fruit can be spawned at any time using C#.
But I want certain fruits to spawn more than others with a fixed probability. For example, I have oranges, apples, bananas, strawberries and pomegranates, where I want these fruits to spawn with these weights: Oranges: 40%, Apples: 30%, Bananas: 10%, Pomegranates: 15%, Strawberries: 5%. So oranges will be spawned 40% of time, apples will be spawned 30% of time, with strawberries and pomegranates being spawned rarely.
I've heard of using cumulative frequency, but I don't know how to implement that, nor do I know if its efficient in the long term, since I want to add more fruits later on and give them a fixed probability to spawn at as well.
Thanks
Answer by jstopyraIGG · Apr 04, 2019 at 06:36 PM
I usually use Weighted Random for stuff like this. With weighted random, 100% is total of all chances combined, so you dont have to care about making the fruit add up to 100%.
In weighted random, you:
1) Add up all the weights to a sum
float sum = 0;
for(var fruit in fruits)
{
sum += fruit.spawnRate;
}
2) Generate a random number between 0 and sum(exclusive)
float randomWeight = 0;
do
{
//No weight on any number?
if(sum == 0)
return null;
randomWeight = Random.Range(0, sum);
}
while(randomWeight == sum)
3) Go through all items and check if the random number is less than the weight of that item. If it is not, then subtract that weight.
for(var fruit in fruits)
{
if(randomWeight < fruit.spawnRate)
return fruit;
randomWeight -= fruit.spawnRate;
}
Your code should never go this far
return null;
Thank you for your help!! I will definitely try this out and let you know if I have any issues or questions.
Also you said that with this solution I don't have to care about making the fruits add up to 100%, so does this mean I can add as many fruits as I like with their different weights using this solution?
Correct! If you have just 1 fruit, and set weight to any number to let's say 5, then 5 is 100%.
If you add another fruit, and make it 10, then the first fruit becomes 33% and the second one becomes 66% and you don't have to calculate any of it.
If you add another fruit that has 5 again, then the first fruit will become 25%, second one 50%, and the third 25%. It's very nice to use this method because it automatically adjusts scales of everything for you :)
Answer by Tsaras · Apr 04, 2019 at 03:24 PM
You can easily do what you want with 2 parallel arrays. Let's say you have an array of probabilities for each fruit like the ones you mentioned. You also have an array of gameobjects that correspond to the probabilities index by index. You can then create a cumulative array at runtime to do your business:
GameObject[] fruit;
float[] probability; //40% could be 0.4f
float[] cumulative;
void MakeCumulative() //this function creates the cumulative array
{
float current = 0;
int itemCount = probability.Length;
for(int i = 0; i <= itemCount; i++)
{
current += probability[i];
cumulative[i] = current;
}
if (current > 1.0f)
{
Debug.Log("Probabilities exceed 100%");
}
}
GameObject GetRandomFruit()
{
float rnd = Random.Range(0, 1.0f);
int itemCount = cumulative.Length;
for(int i = 0; i <= itemCount; i++)
{
if (rnd <= cumulative[i])
{
return fruit[i];
}
}
return null;
}
Thank you for your response!! Would this solution be effective in the long term, since I want to add more fruits later on?
It is as long as the sum of all probabilities is 1.
Your answer
Follow this Question
Related Questions
Multiple Cars not working 1 Answer
Distribute terrain in zones 3 Answers
C# GameObject Lists 2 Answers
Getting the position of all game objects with the same tag 1 Answer
I want to create a pause in between spawning objects in a row 2 Answers