# 무작위성 활용하기

> Use the Random class to generate various types of random values commonly required in game development.

Random 클래스는 흔히 요구되는 다양한 유형의 랜덤 값을 쉽게 생성할 수 있는 방법을 제공합니다.

이 페이지에서는 Random 클래스를 살펴보고, 해당 클래스가 스크립트 작성 시 일반적으로 어떻게 사용되는지 설명합니다. Random 클래스의 모든 멤버에 대한 전체 레퍼런스와 자세한 기술 정보는 [Random 스크립트 레퍼런스](/engine/6000.0/script-reference/unity-engine/random.md)를 참조하십시오.

이러한 유용한 메서드에 대한 자세한 내용과 예제를 보려면 아래 링크를 클릭하십시오.

## 단순 랜덤 숫자

[`Random.value`](/engine/6000.0/script-reference/unity-engine/random/value.md)는 0.0과 1.0 사이의 무작위 **부동 소수점 숫자**를 제공합니다. 일반적으로 이를 통해 생성되는 결과에 특정 숫자를 곱하여 0부터 그 특정 숫자 사이에 속하는 임의의 수를 얻는 데 쓰입니다.

[`Random.Range`](/engine/6000.0/script-reference/unity-engine/random/range.md)는 **지정한 최솟값과 최댓값 사이의 무작위 숫자**를 생성합니다. 지정한 최솟값과 최댓값이 정수인지 플로트인지에 따라 정수나 플로트를 반환합니다.

## 원 또는 구체 내 랜덤 포인트

[`Random.insideUnitCircle`](/engine/6000.0/script-reference/unity-engine/random/inside-unit-circle.md)은 반지름이 1인 **원 안에 있는 임의의 점**을 반환합니다(역시 이 결과에 특정한 수를 곱하여 해당 크기의 원 안에 존재하는 임의의 점을 얻을 수 있음).

[`Random.insideUnitSphere`](/engine/6000.0/script-reference/unity-engine/random/inside-unit-sphere.md)은 반지름이 1인 **구체 안에 있는 임의의 점**을 반환합니다.

[`Random.onUnitSphere`](/engine/6000.0/script-reference/unity-engine/random/on-unit-sphere.md)은 반지름이 1인 **구체의 \_표면\_에 있는 임의의 점**을 반환합니다.

## 기타 유형의 랜덤 값

Unity의 Random 클래스는 다른 유형의 랜덤 값도 제공합니다.

무작위 **회전**을 생성하려면 [`Random.rotation`](/engine/6000.0/script-reference/unity-engine/random/rotation.md)을 사용합니다.

무작위 **컬러**를 생성하려면 [`Random.ColorHSV`](/engine/6000.0/script-reference/unity-engine/random/color-hsv.md)를 사용합니다.

## 배열에서 랜덤 항목 선택

배열 요소를 임의로 선택하는 것은 0과 배열의 최대 인덱스 값(배열의 길이에서 1을 뺀 값) 사이에서 임의의 정수를 선택하는 것과 같습니다. 이 작업은 빌트인 `Random.Range` 함수를 사용하여 쉽게 수행할 수 있습니다.

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

`Random.Range`는 첫 번째 파라미터를 포함하지만 두 번째는 제외하는 범위에서 값을 반환하므로 여기서 `myArray.Length`를 사용하면 올바른 결과를 얻을 수 있습니다.

## 확률이 다른 항목 선택

때로는 항목을 무작위로 선택하되 각 항목이 선택될 확률에 편차를 두어야 할 수 있습니다. 예를 들어 NPC는 플레이어와 조우할 때 여러 가지 방식으로 반응할 수 있습니다.

* 친근하게 맞이할 확률 50%
* 도망갈 확률 25%
* 즉시 공격할 확률 20%
* 돈을 선물로 제안할 확률 5%

이처럼 서로 다른 결과들은 전체 수직선상에서 일부분을 차지하는 서로 다른 영역들로 생각할 수 있습니다. 이때 각 영역이 차지하는 길이는 해당 결과가 선택될 확률에 비례합니다. 결과들 중 하나를 선택하는 것은 이 수직선상에서 임의의 점을 선택하여 그 점이 위치하는 영역을 확인하는 것과 동일합니다.


**Frame:**
![](/api/media?file=/engine/6000.0/media/images/ProbStrip.png)

스크립트에서 이러한 수직선은 다양한 항목의 서로 다른 확률을 순서대로 나타내는 플로트로 구성된 배열입니다. 무작위 지점을 구할 때는 배열 내 모든 플로트의 합계에 `Random.value`를 곱하면 됩니다(플로트의 합이 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])`

줄을 더 작거나 같음 테스트로 변경하면 추가 return 문이 방지되지만 확률이 영(0)인 항목이 간헐적으로 선택될 수 있습니다.

## 연속 랜덤 값 가중치 지정

플로트 배열을 활용하는 방식은 선택해야 하는 결과가 불연속적인 경우에 알맞습니다. 하지만 상황에 따라서는 연속적인 결과를 생성해야 할 수도 있습니다. 예를 들어 보물 상자에서 찾은 금화의 개수를 1과 100사이의 무작위 숫자로 결정하되, 개수가 적을 확률이 더욱 높아지도록 설정할 수 있을 것입니다. 이 경우 플로트 배열을 활용하려면 100개의 플로트를 포함하는 배열이 필요한데, 이는 너무 거추장스럽습니다. 게다가 정수가 아닌 실수 범위의 값이 생성되기를 원한다면 배열을 활용하는 것 자체가 불가능합니다.

연속적인 결과가 필요할 때는 `AnimationCurve`를 사용하여 '원시' 임의 값을 '가중' 값으로 변환하는 것이 좋습니다. 이렇게 하면 다양한 형태의 곡선을 그려서 각기 다른 가중치를 생성할 수 있으며, 코드 작성도 더 간단합니다.

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

0과 1 사이의 '원시' 무작위 값은 `Random.value`에서 읽어오는 방식으로 선택합니다. 이 값은 `curve.Evaluate()`로 전달되어 수평 좌표로 취급되며, 해당 위치에서 곡선의 높이에 해당하는 수직 좌표가 반환됩니다. 곡선의 완만한 부분은 선택될 확률이 더 높은 반면 가파른 부분은 선택될 확률이 더 낮습니다.


**선형 곡선은 값에 가중치를 전혀 부여하지 않습니다. 곡선상의 모든 점에서 수평 좌표와 수직 좌표가 같기 때문입니다.:**
![](/api/media?file=/engine/6000.0/media/images/WeightedRandomCurve-linear.png)


**이 곡선은 앞 부분은 완만하지만 끝으로 갈수록 가팔라지므로 작은 값을 얻을 확률이 더 높고 큰 값을 얻을 확률은 낮습니다. 곡선에서 x=0.5인 점의 높이는 약 0.25이므로, 0과 0.25 사이의 값을 얻을 확률은 50%입니다.:**
![](/api/media?file=/engine/6000.0/media/images/WeightedRandomCurve-bias-low.png)


**이 곡선은 처음과 끝 부분이 모두 완만하므로 양 극단에 가까운 값이 더 많이 발생하고, 가운데 부분은 가파르므로 이 부분의 값은 상대적으로 적게 발생합니다. 또한 이 곡선의 높이는 전반적으로 높아져 있습니다. 곡선의 최저 높이가 1이고 최대 높이가 10이므로, 곡선에서 생성되는 값의 범위는 앞의 곡선처럼 0-1이 아니라 1-10입니다. :**
![](/api/media?file=/engine/6000.0/media/images/WeightedRandomCurve-bias-lowhigh.png)

위의 곡선은 확률 이론 가이드에서 볼 수 있는 확률 분포 곡선이 아니라 역누적 확률 곡선에 더 가깝습니다.

스크립트에서 public `AnimationCurve` 변수를 정의하면 값을 계산해야 할 필요 없이 인스펙터 창에서 곡선을 보고 편집할 수 있습니다.

이 방식은 부동 소수점 숫자를 생성합니다. 예를 들어 금화 82.1214개 대신 82개를 원하는 경우처럼 정수 결과를 계산하고 싶은 경우 간단히 계산된 값을 `Mathf.RoundToInt()` 같은 함수에 전달하면 됩니다.

## 리스트 순서 섞기

많은 게임에서 알고 있는 아이템들이 무작위순으로 나오는 메카닉이 사용됩니다. 예를 들어 포커 카드는 일반적으로 어떤 카드가 뽑힐지 예측할 수 없도록 잘 섞어야 합니다. 이 경우 각 요소를 배열의 임의 인덱스에 있는 다른 요소와 맞바꿔서 배열의 아이템 순서를 섞으면 됩니다.

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

## 아이템 집합에서 반복 없이 선택

일반적인 작업 중 하나로, 집합에서 여러 아이템을 무작위로 선택하되 같은 아이템이 두 번 이상 선택되지 않도록 하는 경우가 있습니다. 예를 들어 임의의 스폰 포인트에서 여러 NPC를 생성하되 각 포인트에는 하나의 NPC만 생성되도록 하고 싶을 수 있습니다. 이렇게 하려면 각 아이템을 선택된 집합에 추가할지 여부를 임의로 결정하는 작업을 모든 아이템에 대해 차례로 반복하여 수행합니다. 각 아이템의 차례에서 해당 아이템이 선택될 확률은 필요한 아이템 수를 선택 가능한 나머지 아이템 수로 나눈 값과 같습니다.

예를 들어 사용 가능한 스폰 포인트가 10개 있지만 다섯 개만 선택해야 하는 경우, 첫 번째 아이템이 선택될 확률은 5/10, 즉 0.5입니다. 이 아이템이 선택되면 두 번째 아이템이 선택될 확률은 4/9인 0.44입니다. 아이템이 4개 필요하고 선택할 수 있는 아이템이 9개 남아 있기 때문입니다. 하지만 첫 번째 아이템이 선택되지 않은 경우 두 번째 아이템이 선택될 확률은 5/9, 즉 0.56입니다. 여전히 다섯 개가 필요하고 선택할 수 있는 아이템이 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;
}
```

이 경우 선택은 무작위지만, 선택된 집합 내 아이템 순서는 원래 배열에서의 순서와 동일합니다. 아이템을 한 번에 하나씩 차례대로 사용하려는 경우에는 이러한 순서로 인해 아이템을 부분적으로 예측할 수 있으므로 사전에 배열을 섞어야 할 수 있습니다.

## 공간 안의 랜덤 포인트

Vector3의 각 컴포넌트를 `Random.value`에서 반환된 값으로 설정하는 방식으로 큐브 입방체 안에 있는 임의의 지점을 선택할 수 있습니다.

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

그러면 변의 길이가 1단위인 큐브 안에 있는 지점을 얻을 수 있습니다. 여기서 벡터의 X, Y, Z 컴포넌트를 원하는 변의 길이로 곱하면 큐브를 스케일할 수 있습니다. 축 하나를 0으로 설정할 경우 지점은 항상 하나의 평면 안에 있게 됩니다. 예를 들어 '지면'에서 무작위 지점을 선택하려면 일반적으로 X와 Z 컴포넌트를 임의로 설정하고 Y 컴포넌트를 0으로 설정합니다.

구체의 경우(즉 원점으로부터 주어진 반경 안에서 임의의 지점을 구하려는 경우), `Random.insideUnitSphere`를 원하는 반지름으로 곱하면 됩니다.

`var randWithinRadius = Random.insideUnitSphere * radius;`

이렇게 얻은 벡터의 컴포넌트 중 하나를 0으로 설정하면 원 안에서 올바른 랜덤 포인트를 얻을 수 *없습니다*. 이 경우 올바른 반경 안에서 무작위로 지점이 선택되기는 하지만, 확률이 원의 가장자리 쪽으로 크게 치우치므로 지점의 분포가 매우 불균등해집니다. 이 작업에는 `Random.insideUnitCircle`을 대신 사용해야 합니다.

`var randWithinCircle = Random.insideUnitCircle * radius;`
