- 2.1.1
좌표계는 물체의 위치를 특정한 하나의 점으로, 다시 말해 유일하게 가리키기 위한 체계다.
원점에서 직각으로 교차하는 x축과 y축으로 이루어진 좌표계를 직교좌표계라고 한다. 데카르트 좌표계라고 부르기도 한다.
축이 직각으로 교차하지 않는 사교좌표계라는 것도 존재한다.
x축과 y축에 의해 네 개로 분할된 평면을 사분면이라고 하고, 오른쪽 위에서부터 반시계 방향으로 1, 2, 3, 4사분면으로 구분한다.
- 2.1.3
유니티가 채용한 z 방향의 좌표계를 가리켜 왼손 좌표계라고 한다. 원점에서 안쪽을 향해 깊이로서 z축이 증가한다.
OpenGL과 WebGL에서는 오른손 좌표계가 사용된다. 앞쪽으로 올수록 z의 값이 증가한다.
- 2.1.4
유니티에서 각 물체의 월드 좌표는 각각 게임 오브젝트의 Transform의 position 프로퍼티에 저장된다.
로컬 좌표계의 경우 게임 오브젝트는 parent라는 프로퍼티로 별도의 게임 오브젝트를 부모로 지정할 수 있어, 부모 게임 오브젝트로부터의 상대적 위치로서의 로컬 좌표를 localPosition이라는 프로퍼티로 지정할 수 있다.
- 2.1.5
씬 컨트롤 툴의 오른쪽에 보이는 버튼 두 개는 게임 오브젝트에 겹쳐서 표시되는 좌표계의 기즈모가 표시되는 방식을 지정한다.
왼쪽 버튼은 Center/Pivot을 전환하고 오른쪽 버튼은 Local/Global을 전환한다.
Center를 지정하면 유니티가 표시하는 물체의 중심이 좌표축 원점이 된다.
Pivot을 지정하면 Maya 등의 3D 모델링 툴에서 그 물체의 메시를 생성했을 때 설정된 중심점인 pivot이 원점이 된다.
메시는 3D 공간 내의 물체인 3D 모델을 구성하는 로컬 좌표계 내의 복수 정점을 조합한 그물코 모양의 기하학적 정보와, 각 정점이 형성하는 삼각형 면에 붙는 이미지인 텍스처로 구성된다.
삼각형은 조합되어 폴리곤을 형성하므로 메시를 폴리곤 메시로 부르는 경우도 있다.
Local을 지정하면 로컬 좌표의 기울기에 맞춰 좌표축을 기울인다.
Global을 지정하면 월드 좌표계에 맞춘다.
- 2.1.6
유니티에는 스크린 공간이라는 것이 있고, 그 공간을 위한 좌표계인 스크린 좌표계가 있다.
스크린 좌표계는 화면 왼쪽 구석을 원점으로, 화면 해상도에 따른 픽셀 위치를 단위로 하는 2D 직교좌표계다.
Camera 클래스의 WorldToScreenPoint 메서드를 호출해서 월드 좌표에서 스크린 좌표로 변환할 수 있다.
유니티에는 스크린 좌표를 정규화한 좌표로 뷰포트 좌표가 있다.
정규화란 어떤 값의 범위가 가지는 최솟값부터 최댓값의 범위를 0~1의 범위로 변환하는 것이다.
뷰포트 좌표에서는 화면 해상도에 관계 없이 항상 왼쪽 아래가 (0, 0)이고 오른쪽 위가 (1, 1)이 된다는 것을 의미한다.
뷰포트 좌표를 사용하면, 어떤 물체가 화면 안에 들어오는지 화면 해상도에 관계없이 쉽게 판정할 수 있다.
- 2.2.1
2D 극좌표계에는 세계의 중심에 극(pole)이라 불리는 기준점이 있다.
극좌표계에서 평면상의 점 P의 위치를 결정하는 것은 극축과 P를 연결하는 직선 r의 길이와, 극축과 r이 이루는 각도 θ이다.
P의 극좌표는 (r, θ)로 지정된다.
게임에서 극좌표는 평면 상에서 원의 원주 위를 이동시킬 경우 사용할 수 있다.화면 중심으로부터의 거리에 따라 화면에 특정 이미지 처리를 가하고자 할 때, 직교좌표계인 스크린 좌표계에서 극좌표로 변환해주면 처리가 쉬워진다.
예시) VR 기기에서 렌즈로 이미지를 확대했을 때 오목 일그러짐이 발생하는데 이때 볼록 일그러짐을 일으키는 이미지 처리를 후처리로 적용함으로써 일그러짐을 보정한다.
- 2.2.2
3D 극좌표계 = 구면좌표계
지구과학에서 사용하는 구 모양 좌표계
구면좌표계를 게임에서 응용한 예로는 특정 캐릭터의 로컬 좌표 공간 내에서 원형 궤도를 도는 위성처럼 움직이는 물체를 표현하거나, 캐릭터의 후방 상공에 떠서 일정한 거리로 캐릭터를 추적해 화면 중심에 계속 비추는 3인칭 시점 게임의 카메라가 있다.
public class Chapter02 : MonoBehaviour {
public float rotateSpeed = 1f;
public float scrollSpeed = 200f;
public Transform pivot;
[System.Serializable]
public class SphericalCoordinates
{
public float _radius, _azimuth, _elevation;
public float radius // 구면좌표의 반지름
{
get { return _radius; }
private set
{
// 값을 최소 최대 범위 안으로 제한
_radius = Mathf.Clamp( value, _minRadius, _maxRadius );
}
}
public float azimuth // 방위각
{
get { return _azimuth; }
private set
{
// 최댓값에 도달하면 다시 최솟값으로 돌아간다 (또는 그 반대)
_azimuth = Mathf.Repeat( value, _maxAzimuth - _minAzimuth );
}
}
public float elevation
{
get{ return _elevation; }
private set
{
_elevation = Mathf.Clamp( value, _minElevation, _maxElevation );
}
}
public float _minRadius = 3f;
public float _maxRadius = 20f;
public float minAzimuth = 0f;
private float _minAzimuth;
public float maxAzimuth = 360f;
private float _maxAzimuth;
public float minElevation = 0f;
private float _minElevation;
public float maxElevation = 90f;
private float _maxElevation;
public SphericalCoordinates(){}
// 카메라의 직교좌표 수치를 받아 구면좌표의 초깃값으로 설정한다
public SphericalCoordinates(Vector3 cartesianCoordinate)
{
_minAzimuth = Mathf.Deg2Rad * minAzimuth;
_maxAzimuth = Mathf.Deg2Rad * maxAzimuth;
_minElevation = Mathf.Deg2Rad * minElevation;
_maxElevation = Mathf.Deg2Rad * maxElevation;
radius = cartesianCoordinate.magnitude; // 카메라와 원점의 거리
azimuth = Mathf.Atan2(cartesianCoordinate.z, cartesianCoordinate.x);
elevation = Mathf.Asin(cartesianCoordinate.y / radius);
}
public Vector3 toCartesian // 구면좌표에서 직교좌표로의 변환
{
get
{
float t = radius * Mathf.Cos(elevation);
return new Vector3(t * Mathf.Cos(azimuth), radius * Mathf.Sin(elevation), t * Mathf.Sin(azimuth));
}
}
public SphericalCoordinates Rotate(float newAzimuth, float newElevation){
azimuth += newAzimuth;
elevation += newElevation;
return this;
}
public SphericalCoordinates TranslateRadius(float x) {
radius += x;
return this;
}
}
public SphericalCoordinates sphericalCoordinates;
void Start () {
sphericalCoordinates = new SphericalCoordinates(transform.position);
transform.position = sphericalCoordinates.toCartesian + pivot.position;
}
void Update () {
float kh, kv, mh, mv, h, v;
kh = Input.GetAxis( "Horizontal" );
kv = Input.GetAxis( "Vertical" );
bool anyMouseButton = Input.GetMouseButton(0) | Input.GetMouseButton(1) | Input.GetMouseButton(2);
mh = anyMouseButton ? Input.GetAxis( "Mouse X" ) : 0f;
mv = anyMouseButton ? Input.GetAxis( "Mouse Y" ) : 0f;
h = kh * kh > mh * mh ? kh : mh;
v = kv * kv > mv * mv ? kv : mv;
if (h * h > Mathf.Epsilon || v * v > Mathf.Epsilon) { // 수치의 절댓값이 0이 아님을 확인하기 위함
// float형끼리 비교할 경우에는 == 연산자를 사용하지 않고 Mathf.Approximately를 사용해서 모호한 비교를 할 것
transform.position
= sphericalCoordinates.Rotate(h * rotateSpeed * Time.deltaTime, v * rotateSpeed * Time.deltaTime).toCartesian + pivot.position;
}
float sw = -Input.GetAxis("Mouse ScrollWheel");
if (sw * sw > Mathf.Epsilon) {
transform.position = sphericalCoordinates.TranslateRadius(sw * Time.deltaTime * scrollSpeed).toCartesian + pivot.position;
}
transform.LookAt(pivot.position); // pivot으로 설정한 게임 오브젝트 쪽으로 이 스크립트가 추가된 오브젝트의 방향을 돌린다
}
}
'책 > 유니티로 배우는 게임 수학' 카테고리의 다른 글
[유니티로 배우는 게임 수학] 3장-벡터 복습 및 정리 (0) | 2024.08.07 |
---|---|
[유니티로 배우는 게임 수학] 1장-삼각함수 복습 및 정리 (0) | 2024.08.06 |