- Home /
Random Instantion From Array with Weighted Frequency
Hi Everyone,
I hope I'm using terms that make sense. I'm a veteran 3D artist learning programming for Unity. Basically what I've got is an object that randomly selects an object from an array and instances it. That part works perfect, the next step I want is to give each object in the array a frequency value so I can make that object more or less likely to be selected.
Ideally in the inspector it would look something like this:
Element 0 "gameObjectA"
Element 0 Frequency "0.25"
Element 1 "gameObjectB"
Element 1 Frequency "0.40"
Element 2 "gameObjectC"
Element 2 Frequency "0.05"
Element 3 "gameObjectD"
Element 3 Frequency "0.30"
In the above example, gameObjectA would have a 25% chance of being selected for instancing while gameObjectC would only have a 5% chance, etc.
I'm really not sure where to start on this one. It needs to be something that I can easily expand as I'm not really sure how many elements I'll need in the end. I'm hoping what I'm looking for is just an expansion of the array function and I'm just using the wrong terms in my search. I really just need a push in the right direction.
By the way, I'm working in JavaScript. Please don't reply telling me to do this in C#. I realize and appreciate C# is generally considered a more robust language but what I'm working on is using JavaScript.
Thanks for any help.
Answer by Owen-Reynolds · Nov 30, 2011 at 05:01 AM
Add up the total frequencies once and roll that. You then convert the frequencies into a table (ex: Element1 0.25 - 0.65) or don't and slice off until you get a hit. This is C#, translated to JS where I know how, but same idea:
// do this once:
var totalFreq : float = 0;
for(int i=0; i<F.Length; i++) totalFreq += F[i];
var roll : float = Random.Range(0,totalFreq);
// Ex: we roll 0.68
// #0 subtracts 0.25, leaving 0.43
// #1 subtracts 0.4, leaving 0.03
// #2 is a hit
var index : int = -1;
for(int i=0; i<F.Length; i++) {
if(roll<=F[i]) { index=i; break; }
roll -= F[i];
}
// just in case we manage to roll 0.0001 past the highest:
if(index==-1) index=F[i].Length-1;
Converting to a table is the subtraction method, backwards. It turns your numbers into 0.25 0.65 0.7 1.0. You only have to do it once. It makes the lookups go faster (use the first item greater than your roll):
var last : float = 0;
for(int i=0; i<F.Length; i++) {
F[i]+=last;
last=F[i];
}
This worked, thanks! It took me a while to wrap my head around what's going on, but now that I've figured out what's going on, it makes total sense!
How would I go about addressing the value of a single item in the array? Let's say I want a button that when clicked, it increases the frequency value of item #2.
Buttons are made in code. If you have 4, you're probably using a loop to make them. Easy enough, then, to have button#2 change Frequency2. Would need to redo the totalFreq
computation after each change. $$anonymous$$ethod#2 destroys the original percents, so go with #1 (or save originals in another array for #2, and also recompute the table on each freq change.)
Your answer
![](https://koobas.hobune.stream/wayback/20220613060636im_/https://answers.unity.com/themes/thub/images/avi.jpg)