Unity에서 Random.Range()는 꽤나 자주 사용하는 친구입니다. 하지만 Random은 단순히 톡 누르면 난수를 내뱉는 난수 자판기의 역할만 있는게 아닙니다.
하지만 엄청나게 많거나 유용한 기능을 제공했다면 조금 더 자주 저 친구를 봤겠죠. 그럼에도 특정 상황에서는 꽤나 유용한 기능을 제공하긴 합니다.
그러니 이에 대해 알아봅시다.
말 그대로 위치를 반환하는 속성들입니다.
간단한 개념이니 코드부터 보도록 하죠.
// 반지름이 1인 원 안에서 위치를 Vector2로 가져옴
Vector2 randCirclePos = Random.insideUnitCircle;
// 반지름이 1인 구 안에서 위치를 Vector3로 가져옴
Vector3 randSpherePos = Random.insideUnitSphere;
// 반지름이 1인 구 위의 점 위치를 Vector3로 가져옴
Vector3 randOnSpherePos = Random.onUnitSphere;
위에3줄(주석 제외) 모두 Vector 형식으로 특정한 위치를 가져옵니다.
Random.insideUnitCircle과 Random.insideUnitSphere는 2차원과 3차원의 차이점만 빼면 동일합니다.
둘 다 원이나 구 안에서 어떠한 위치를 가져오는 프로퍼티입니다.
3번째 줄의 Random.onUnitSphere는 대놓고 다르게 작동합니다.
바로 위에 있는 insideUnitSphere가 반지름이 1인 구를 만들고 "내부"에서 위치를 가져왔습니다.
반면에 처 친구는 반지름이 1인 구를 만드는 것까지는 똑같지만 구 표면에 점을 찍고 그 위치를 가져옵니다.
근데 읽어보면 뭔가 이상한 점이 있습니다.
실제로 코딩을 할 때 원이나 구에서 랜덤한 위치를 가져올 일이 그렇게 많이는 않습니다. 사각형에 비하면 말이죠.
근데 Random에서 특정한 위치를 가져오는건 저게 끝입니다. 그렇기에 제가 지금까지 Random을 난수 자판기라고 생각하며 살았는데요.
아마 사각형이 구현이 안되있는 이유는 "쉬워서" 인거 같습니다.
한번 해볼까요?
// 크기가 1인 정사각형에서 위치 가져오기
float x = Random.Range(0, 1f);
float y = Random.Range(0, 1f);
Vector2 randSquarePos = new Vector2(x, y);
간단하게 됐습니다.
저는 직사각형에서 랜덤한 위치를 가져오려고 x, y의 범위를 다르게 해서 위치를 구한 것도 생각이 나네요.
이처럼 사각형은 쉽습니다. 하지만 원, 구 안에서의 위치, 심지어 구를 만들고 거기에 점을 찍은 위치를 가져오라고 하면, 전 시간이 좀 걸릴 것 같습니다.
이처럼 만들기 어려운 부분을 구현해 놓은 것 같습니다.
위에서 살펴본 친구들을 활용하는 방법을 살펴보겠습니다.
위에서 사용한 친구들은 전부 범위가 0~1인 것을 볼 수 있습니다.
이처럼 Random 프로퍼티는 각 좌표의 최솟값은 0으로 최댓값은 1로 "고정" 되어 있습니다.
따라서 그대로 사용하면 (0, 0, 0)을 기준으로 원이나 구를 그리고 거기에서 위치를 가져옵니다.
하지만 저희는 (0, 0, 0) 말고 다른 좌표에서 원이나 구를 그리고 위치를 가져오고 싶은 경우가 많을 겁니다.
이럴 때는 기준이 되는 좌표를 정해야 됩니다.
직관성을 높이기 위해 대부분 원하는 위치에 오브젝트를 배치하고 그 오브젝트를 기준으로 랜덤한 좌표를 가져오는 작업을 합니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RandomSpawner : MonoBehaviour
{
[SerializeField] GameObject prefebs = null;
float circleRange = 10f; // 원의 범위
private void Awake()
{
// 반지름이 10인 원에서 랜덤한 좌표를 가져옴
// Vector2를 Vector3로 형변환 했기에 z는 0
Vector3 randPos =
(Vector3)Random.insideUnitCircle * circleRange;
Vector3 createPos = transform.position + randPos;
// 프리펩 생성
Instantiate(prefebs, createPos, Quaternion.identity);
}
}
위에 코드를 오브젝트에 추가한 후 실행하면 오브젝트 주변에서 랜덤한 위치에 프리펩이 생성됩니다.
오브젝트 근처에 빨간 공이 잘 생성되었군요
말 그대로 임의의 방향을 제공합니다.
근데 여기서 방향을 제공하는 기능이 2개가 있습니다.
Quaternion randRotate = Random.rotation;
Quaternion randRotate2 = Random.rotationUniform;
둘 다 쿼터니언 타입이며 둘 다 랜덤한 방향을 반환합니다. 뭐가 다를까요?
Unity 공식 문서에서는 rotationUniform이 rotation에 비해 더 균일하며 높은 품질의 방향값을 제공한다고 나와 있지만, 솔직히 잘 체감이 되지는 않습니다.
그냥 아무거나 써도 될 거 같습니다.
말 그대로 랜덤한 색깔을 줍니다. 참고로 불투명도(alpha) 값은 1입니다.
Color randColor = Random.ColorHSV();
하지만 저 친구는 위에 애들과는 근본적으로 다른 부분이 있습니다.
지금까지 봐온 애들은 값의 범위는 고정되 있고 그걸 저희가 알아서 조절해야 하는 형식이었지만 이 친구는 저희가 범위를 "조절" 할 수 있습니다.
그래서 저 함수는 위에서 사용한 방법 말고도 다른 방식으로 쓸 수 있습니다.
public static Color ColorHSV ( float hueMin , float hueMax );
public static Color ColorHSV ( float hueMin , float hueMax , float SaturationMin , float SaturationMax );
public static Color ColorHSV ( float hueMin , float hueMax , float SaturationMin , float SaturationMax , float valueMin , float valueMax );
public static Color ColorHSV ( float hueMin , float hueMax , float SaturationMin , float SaturationMax , float valueMin , float valueMax , float alphaMin , float alphaMax );
갑자기 뭘 본건가 싶기도 하고 실제로 좀 사용이 까다롭습니다.
얼핏 보면 RGBA 하나당 최솟값 최댓값 2개의 인수를 줘서 범위를 정할 수 있도록 한것처럼 보이지만 이름을 보면 아닙니다.
제가 처음에 고정이라고 언급했던 alpha 값이 조정 가능한 것은 맞지만.
앞에 있는 파라미터들을 번역하자면 각각 색조, 채도, 값 입니다.
먼저 Saturation, 채도부터 설명하겠습니다.
채도는 색의 맑고 탁한 정도입니다. 색의 밝기를 다루는 명도와는 다른 개념입니다.
제가 미술을 몰라서 맞는 비유인지는 모르겠지만, 제 경험상 명도가 높으면 눈뽕이고 채도가 높으면 선명하다는 느낌을 받았습니다.
색조는 색깔이라 생각하기 쉽지만 엄연히 다릅니다. 앞에서 설명한 채도와 명도를 통합한 개념이라고 합니다.
가끔씩 밝고 선명한 물체나 그림을 보고 "톤이 높다" 라는 표현을 쓰는데 이는 명도와 채도가 높다. 즉 색조가 높다는 뜻입니다.
근데 색조가 명도와 채도가 통합된 개념이면 왜 분리했냐? 라고 물으면 모르겠습니다.
솔직히 설명만 보면 뭔 차이인지도 모르겠습니다.
그래서 간단하게 사진을 드리겠습니다.
위로 뻗은 큐브들은 색조의 범위가 0 ~ 0.5f이고 수평방향으로 뻗은 큐브들은 0.5f ~ 1f 입니다. 채도 값은 동일합니다.
이번에는 반대로 색조 값이 동일하고 채도 값이 다른 경우입니다.
이번에도 위로 뻗은 쪽이 채도가 낮고 수평으로 뻗은 쪽이 높습니다.
이번에는 3번째인 Value에 대해 알아봅시다.
지금까지 미술 시간이었다면 이번에는 수학 시간입니다.
유니티 공식 문서에 따르면 저 value 값에 따라 RGB값이 "선형보간" 을 통해 나온다고 합니다.
Lerp()함수를 쓰신 적이 있는 분은 친숙한 개념이실 텐데 그냥 비례라고 생각하시면, 절대 안되기는 하는데 글의 범위를 벗어나기 때문에 이렇게만 설명하고 가도록 하겠습니다.
참고로 비례라고 생각하면 안되는 간단한 예를 들자면 여기서 Value를 0으로 설정하면 모든 RGB값이 0이 됩니다. 하지만 Value를 1로 설정한다고 모든 RGB값이 1이 되지는 않습니다. 하지만 전체적으로 RGB값이 높아집니다.
그렇기에 비례 관계라고 생각하면 얼추 맞긴 하지만 절대 같은 개념이 아닙니다.
그래서 우리가 이렇게 잘 알지도 못하는 미술과 수학 개념까지 공부하면서 얻을 수 있는 것은 무었일까요?
"느낌"입니다. 위의 개념을 정확하게는 몰라도 어느 정도 차이를 인지한다면 특정한 느낌을 주는 색깔을 랜덤하게 얻을 수 있습니다.
using UnityEngine;
public class ColorOnClick : MonoBehaviour
{
void OnMouseDown()
{
// 클릭 시 어둡지 않고 포화된 색깔을 랜덤하게 선택
GetComponent<Renderer>().material.color =
Random.ColorHSV(0f, 1f, 1f, 1f, 0.5f, 1f);
}
}
저 코드는 유니티 공식 문서코드를 그대로 가져온 겁니다.
보시면 채도 값을 1로 하고 Value를 올려 전체적인 RGB값을 높게 한 것을 보실 수 있습니다. 저 경우 전체적으로 밝은 느낌의 색깔을 얻습니다.
반대로 어두운 느낌의 색깔도 얻을 수 있습니다.
마지막으로 간단하게 원을 그려봅시다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RandomSpawner : MonoBehaviour
{
[SerializeField] GameObject prefebs = null;
private void Awake()
{
StartCoroutine(Co_CreateSphere());
}
IEnumerator Co_CreateSphere()
{
for (int i = 0; i < 30; i++)
{
for (int j = 0; j < 1000; j++)
{
// 랜덤한 위치 가져옴
Vector3 ranpos
= ((Vector3)Random.insideUnitCircle * 10)
+ transform.position;
// 오브젝트 생성
GameObject obj =
Instantiate(prefebs, ranpos, Quaternion.identity);
// 랜덤한 색깔 설정
Color randColor2 =
Random.ColorHSV(0.1f, 0.1f, 0.5f, 1, 1f, 1f);
// 색깔 설정
obj.GetComponent<MeshRenderer>().material.color =
randColor2;
}
yield return new WaitForSeconds(1f);
}
}
}
게임 오브젝트를 x,y 좌표 10내에서(z는 0 고정) 총 3만개를 생성하는 코드입니다.
게임 오브젝트를 한번에 전부 생성하면 유니티가 튕길 수도 있기 때문에 1000개를 생성하는 작업을 30번 반복하고 중간마다 1초의 대기 시간을 주었습니다.
또한 색깔 부분에서는 색조값과 선형 보간 값을 같게 해 전체적인 색깔은 같게 하고 채도 값의 범위를 0.5f ~ 1로 해 전체적으로 밝고 선명한 느낌을 주도록 했습니다.
코드를 실행하면 달고나가 나오는 모습을 보실 수 있습니다.