Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Impact Random.Range ? Modify probability of outcome ?

Discussion in 'Scripting' started by RemDust, Feb 4, 2016.

  1. RemDust

    RemDust

    Joined:
    Aug 28, 2015
    Posts:
    431
    Hi guys,
    In my game, I'm building a list of items, and then randomly spawn them.
    It's pretty easy to do by generating a random number inside the numbers of items contained in the list and using this rng as the item ID.
    But of course that means that every item has the same outcome probability...
    You know where I'm going right ?

    Is there an elegant way of impacting this probability ? Let's say I want the item 1 to be spawn 2x more often than the others ?Of course I could duplicate it in the first place and have it 2 times in my list but this seems pretty ugly from a performance standpoint !

    Any idea ? Thank you all ! :)
     
  2. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    Short answer: f(x) where x is Random.Range and result depends on dispersion you want.

    Explanation:
    Let's say we use Random.Range(0f,1f) to get number anywhere from 0 to 1 in float.
    To get value int res=0 two times on average when we get value int res=1 one time on average (2:1 proportion) we make
    Code (CSharp):
    1. int f(float x)
    2. {
    3.     if(x<=0.6666)return 0; //item 0
    4.     else return 1; //item 1
    5. }
    6.  
    7. //In code we call it
    8. int randNum = f(Random.Range(0f,1f));
    Note: actually it should be 0.(6) in this case so it'll be slightly lower than 2:1 proportion. You can add more 6 to 0.6666 to get it closer to that exact proportion.

    In general case, f(x) can be arbitrary allowing any resulting disperse you want as long as you can find it.
    Visually in y=f(x) it's represented as x being random number generated(all happen with same probability) and y being resulting random number. So you can use any graph drawer to estimate how good this function is for your case.

    P.S. don't forget fs in (0f,1f) - they specify it's float random, not int. It behaves differently.
     
    Last edited: Feb 4, 2016
  3. Mich_9

    Mich_9

    Joined:
    Oct 22, 2014
    Posts:
    118
    Another way to do it:
    Code (CSharp):
    1. int probability = Random.Range(0, 3);//3 is excluded
    2. switch (probability) {
    3.     case 0://33%
    4.     case 1://66%
    5.     //item 1 chosen
    6.     break;
    7.     case 2://33%
    8.     //item 2 chosen
    9.     break;
    10. }
     
  4. exiguous

    exiguous

    Joined:
    Nov 21, 2010
    Posts:
    1,749
    The provided answers require that you specificially hardcode each item. I would try a more general approach where you can define the probability directly in data and let a function pick one. There are some on the Asset Store if you want to pay for it:
    https://www.assetstore.unity3d.com/en/#!/content/36065
    https://www.assetstore.unity3d.com/en/#!/content/38858
    https://www.assetstore.unity3d.com/en/#!/content/38220
    https://www.assetstore.unity3d.com/en/#!/content/39134

    If you want to do it yourself just add an int or float value for the relative probability of each item. Then sum up all those values of each item. Then write a function which generates a random number between zero and the sum. Then iterate over the items and "locally" sum the probability up as long as its smaller than the random number. The item where the sum goes above the random number is the desired one.

    Example:
    Item A Prob 2
    Item B Prob 1
    Item C Prob 3
    Sum = 6
    If random number between 0 and 6 (exclusive) = 0 or 1 A is picked (2/6 = 33% chance), if its 2 B is picked (1/6 17% chance) if its 3,4 or 5 C is picked (3/6 50% chance). This is easily extendible when you add more items and does not require code changes. When you display the list you can also calculate the relative probability of each item to ease balancing.
     
  5. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    Nope. You can write your own function parser or compile a function and make a function for each random if you do my version with f(x). I didn't say it needs to be hard-coded.

    Your way is of course better in simple cases. But what about distribution for bullets from gun?
     
  6. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    Well the most elegant way I can think of doing this is with a normal distribution curve. There isn't anything built into Mathf, like a Gaussian curve annoyingly.. You can get something from the asset store (https://www.assetstore.unity3d.com/en/#!/content/15873), or there's examples of it being made here:

    http://answers.unity3d.com/questions/421968/normal-distribution-random.html
    http://stackoverflow.com/questions/...ox-mueller-random-number-generator-in-c-sharp

    Though I think to be honest that's overkill for your situation. I'd copy one of the guy's above examples :).
     
  7. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,199
    If you want a curve, just grab an animationCurve, and evaluate it at the time of your random value:

    Code (csharp):
    1. public AnimationCurve probCurve; //assign in inspector
    2.  
    3. public float GetRandomWeighted() {
    4.     return probCurve.Evaluate(Random.value);
    5. }
    then you can fiddle with your random distribution through fiddling with the curve.
     
    Teravisor and Nigey like this.
  8. passerbycmc

    passerbycmc

    Joined:
    Feb 12, 2015
    Posts:
    1,739
    just a big hack, but you could re-purpose the AnimationCurve as a way of graphing out the distribution of random numbers here is a example with floats, but you could just as easily make the tangents hard and parse to int when done for ints.

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class RandomTest : MonoBehaviour {
    4.     [SerializeField]
    5.     private AnimationCurve _curve = new AnimationCurve(new Keyframe(0f, 0f, 0f, 1f), new Keyframe(1f, 1f, 1f, 0f));
    6.  
    7.     private void Update() {
    8.         var time = Random.Range(0f, 1f);
    9.         var rand = _curve.Evaluate(time);
    10.         Debug.Log(rand);
    11.     }
    12. }
    13.  
    also keep in mind the anim curve does allow greater than 1 in both time and value, and you can right click a key to give it exact values
     
  9. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,199
    Aha! Beat you to it!
     
    passerbycmc likes this.
  10. passerbycmc

    passerbycmc

    Joined:
    Feb 12, 2015
    Posts:
    1,739
    when you click post it should check if a new post has been made since you started writing your own.