Version: 2020.1
Important Classes - Mathf
Important Classes - Debug

Important Classes - Random

The Random class provides you with easy ways of generating various commonly required types of random values.

This page provides an overview of the Random class and its common uses when scripting with it. For an exhaustive reference of every member of the Random class and further technical details about it, see the Random script reference.

Follow the links below for further details and examples of these useful methods.

Simple random numbers

Random.value gives you a random floating point number between 0.0 and 1.0. A common usage is to convert it to a number between zero and a range of your choosing by multiplying the result.

Random.Range gives you a random number between a minimum and maximum value that you provide. It returns either an integer or a float, depending on whether the the min and max values provide are integers or floats.

Random points within Circles or Spheres

Random.insideUnitCircle returns a randomly selected point inside a circle with a radius of 1 (Again you can multiply the result to get a random point within a circle of any size).

Random.insideUnitSphere returns a randomly selected point inside a sphere with a radius of 1.

Random.onUnitSphere returns a randomly selected point on the surface of a sphere with a radius of 1.

Other types of random values

Unity’s random class also offers a few other types of random value.

To generate a random rotation, use Random.rotation.

To generate a random color, use Random.ColorHSV.

Choosing a Random Item from an Array

Выбор случайного элемента массива водится к выбору случайного значения в диапазоне от нуля до максимального значения индекса в массиве (который на 1 меньше длины массива). Это сделать довольно просто, используя встроенный метод Random.Range:-

 var element = myArray[Random.Range(0, myArray.Length)];

Учтите, что диапазон, из которого метод Random.Range возвращает значение, включает первый аргумент, но не включает второй аргумент. Так что, если в качестве второго аргумента передавать myArray.Length, вы получите правильный результат.

Choosing Items with Different Probabilities

Иногда, вам требуется случайно выбрать элементы, но при этом некоторые из них должны выбираться с большей вероятностью, чем другие. Например, NPC может реагировать по-разному при встрече с игроком:-

  • с вероятностью 50% он дружелюбно поприветствует игрока
  • с вероятностью 25% он убежит
  • с вероятностью 20% он немедленно начнёт атаковать
  • с вероятностью 5% он предложит деньги в качестве подарка

Вы можете представить эти разные реакции в качестве отрезков, каждый из которых занимает определённую площадь на кусочке бумажной ленты, в сумме занимая всю площадь кусочка. Занимаемая отрезком площадь эквивалентна вероятности реакции, соответствующей данному отрезку. Совершение выбора в таком случае эквивалентно указанию на случайную точку на протяжении всего кусочка бумажной ленты (так сказать, бросанию дротика) с последующим выяснением того, в какой секции находится эта точка.

В коде, кусочек бумажной ленты - это на самом деле массив float чисел, содержащий упорядоченный список вероятностей различных элементов. Случайная точка получается с помощью умножения Random.value на сумму всех float значений в массиве (в сумме они не должны превышать 1; нам важен относительный размер различных значений). Чтобы определить, в какой элемент массива “попала” точка, сперва проверьте не меньше ли она значения первого элемента. Если меньше, тогда первый элемент и выбирается. Иначе, вычтите значение первого элемента массива из значения полученной точки и сравните результат со вторым элементом и так далее, до тех пор, пока не найдётся правильный элемент. В коде это может выглядеть как-то так:-

float Choose (float[] probs) {

    float total = 0;

    foreach (float elem in probs) {
        total += elem;
    }

    float randomPoint = Random.value * total;

    for (int i= 0; i < probs.Length; i++) {
        if (randomPoint < probs[i]) {
            return i;
        }
        else {
            randomPoint -= probs[i];
        }
    }
    return probs.Length - 1;
}

Заметьте, что в данном случае необходимо наличие последнего оператора возврата, т.к. Random.value может вернуть 1 и тогда поиск никогда не найдёт случайно выбранную точку. Изменение строки

 if (randomPoint < probs[i])

.. на проверку меньше-или-равно (<=) позволит избежать дополнительного оператора возврата, но с другой стороны, позволит случайно выбирать элемент, даже если вероятность его выбора равна нулю.

Weighting continuous random values

The array of floats method works well if you have discrete outcomes, but there are also situations where you want to produce a more continuous result - say, you want to randomize the number of gold pieces found in a treasure chest, and you want it to be possible to end up with any number between 1 and 100, but to make lower numbers more likely. Using the array-of-floats method to do this would require that you set up an array of 100 floats (i.e. sections on the paper strip) which is unwieldy; and if you aren’t limited to whole numbers but instead want any number in the range, it’s impossible to use that approach.

A better approach for continuous results is to use an AnimationCurve to transform a ‘raw’ random value into a ‘weighted’ one; by drawing different curve shapes, you can produce different weightings. The code is also simpler to write:

float CurveWeightedRandom(AnimationCurve curve) {
    return curve.Evaluate(Random.value);
}

A ‘raw’ random value between 0 and 1 is chosen by reading from Random.value. It is then passed to curve.Evaluate(), which treats it as a horizontal coordinate, and returns the corresponding vertical coordinate of the curve at that horizontal position. Shallow parts of the curve have a greater chance of being picked, while steeper parts have a lower chance of being picked.

A linear curve does not weight values at all; the horizontal coordinate is equal to the vertical coordinate for each point on the curve.
A linear curve does not weight values at all; the horizontal coordinate is equal to the vertical coordinate for each point on the curve.
This curve is shallower at the beginning, and then steeper at the end, so it has a greater chance of low values and a reduced chance of high values. You can see that the height of the curve on the line where x=0.5 is at about 0.25, which means theres a 50% chance of getting a value between 0 and 0.25.
This curve is shallower at the beginning, and then steeper at the end, so it has a greater chance of low values and a reduced chance of high values. You can see that the height of the curve on the line where x=0.5 is at about 0.25, which means there’s a 50% chance of getting a value between 0 and 0.25.
This curve is shallow at both the beginning and the end, making values close to the extremes more common, and steep in the middle which will make those values rare. Notice also that with this curve, the height values have been shifted up: the bottom of the curve is at 1, and the top of the curve is at 10, which means the values produced by the curve will be in the 1-10 range, instead of 0-1 like the previous curves.
This curve is shallow at both the beginning and the end, making values close to the extremes more common, and steep in the middle which will make those values rare. Notice also that with this curve, the height values have been shifted up: the bottom of the curve is at 1, and the top of the curve is at 10, which means the values produced by the curve will be in the 1–10 range, instead of 0–1 like the previous curves.

Notice that these curves are not probability distribution curves like you might find in a guide to probability theory, but are more like inverse cumulative probability curves.

By defining a public AnimationCurve variable on one of your scripts, you will be able to see and edit the curve through the Inspector window visually, instead of needing to calculate values.

This technique produces floating-point numbers. If you want to calculate an integer result - for example, you want 82 gold pieces, rather than 82.1214 gold pieces - you can just pass the calculated value to a function like Mathf.RoundToInt().

Shuffling a List

Довольно часто встречающаяся в играх механика - выбор из известного набора элементов, но со случайным порядком. Например, колода карт обычно перемешана, поэтому их не выбрать в предсказуемой последовательности. Вы можете перемешивать элементы в массиве путём “посещения” каждого из элементов и его обмена местами с другим элементом, случайно выбранным из массива:-

void Shuffle (int[] deck) {
    for (int i = 0; i < deck.Length; i++) {
        int temp = deck[i];
        int randomIndex = Random.Range(0, deck.Length);
        deck[i] = deck[randomIndex];
        deck[randomIndex] = temp;
    }
}

Choosing from a Set of Items Without Repetition

Распространённая задача - случайно выбрать какое-то количество элементов из массива без повторного выбора одного и того же значения. Например, вы можете захотеть сгенерировать какое-то количество NPC в случайных точках генерации, но при этом вы желаете, чтобы только один NPC генерировался в каждой из точек. Это можно реализовать с помощью перебора последовательности элементов, решая для каждого случайным образом - быть ему добавленным в выбранный набор или нет. После “посещения” каждого элемента, вероятность того, что он будет выбран равна числу ещё требующихся элементов, разделённому на число оставшихся для выбора элементов.

В качестве примера, представьте, что существует десять точек генерации, но выбрать можно только пять. Вероятность выбора первого элемента будет равна 5 / 10 или 0.5. Если он выбран, то вероятность выбора второго элемента - 4 / 9, или 0.44 (то есть требуется ещё 4 элемента и ещё 9 доступно для выбора). Однако, если первый элемент не был выбран, то вероятность выбора второго элемента - 5 / 9, или 0.56 (то есть ещё требуется выбрать 5 элементов и ещё 9 доступно для выбора). Это продолжается до тех пор, пока набор не будет состоять из требуемых пяти элементов. Вы можете реализовать это в коде таким образом:-

Transform[] spawnPoints;

Transform[] ChooseSet (int numRequired) {
    Transform[] result = new Transform[numRequired];

    int numToChoose = numRequired;

    for (int numLeft = spawnPoints.Length; numLeft > 0; numLeft--) {

        float prob = (float)numToChoose/(float)numLeft;

        if (Random.value <= prob) {
            numToChoose--;
            result[numToChoose] = spawnPoints[numLeft - 1];

            if (numToChoose == 0) {
                break;
            }
        }
    }
    return result;
}

Заметьте, что хоть выбор осуществляется случайно, порядок выбранных элементов будет таким же, как и в оригинальном массиве. Если элементы будут использоваться по очереди, один за другим, то их порядок может сделать их частично предсказуемыми, поэтому может потребоваться перемешивание массива перед использованием.

Random Points in Space

Можно присвоить каждой компоненте Vector3 случайное значение, возвращаемое Random.value для получения случайной точки в пространстве куба:-

 var randVec = Vector3(Random.value, Random.value, Random.value);

Это даст вам точку в кубе с ребром длиной в одну условную единицу. Куб можно масштабировать просто умножая X, Y и Z компоненты вектора на требуемые длины сторон. Если одна из осей имеет нулевое значение, точка всегда будет лежать на плоскости. Например, получение случайно точки “на земле” обычно достигается с помощью получения случайных компонент X и Z с установкой Y компоненты в ноль.

Если объёмом является сфера (т.е., когда вы желаете разместить случайную точку в указанном радиусе от начала отсчёта), вы можете использовать значение Random.insideUnitSphere, умноженное на нужный радиус :-

 var randWithinRadius = Random.insideUnitSphere * radius;

Учтите, если вы установите одну из компонент получившегося вектора в ноль, вы не получите правильную случайную точку в круге. Хоть точка и действительно случайна, и расположена в области правильного радиуса, точка с большей вероятностью окажется ближе к краю окружности, так что точки будут распределены очень неравномерно. Для этой задачи следует использовать Random.insideUnitCircle:-

 var randWithinCircle = Random.insideUnitCircle * radius;
Important Classes - Mathf
Important Classes - Debug