실제 동작하는 Scene은 공간(3D)환경인데 사용자가 모니터로 보는 환경은 스크린(2D)환경이다. 화면상에 있는 어떤 Object를 누를 때, 입력값은 스크린좌표(2D 좌표)인데 실제 선택되는 것은 공간좌표(3D 좌표)를 가지는 Object이다.
2D 좌표는 카메라의 위치, Object의 위치 각도와 같은 여러 요인에 의해서 달라질 수 있기 때문에 2D 좌표로 계산하는 것은 매우 어렵다. 이런 상황에서 사용하는 것이 Raycasting
인데 다음과 같은 아이디어가 적용되었다.
공간에서의 선벡터(Ray)
로 나타낼 수 있다.Raycasting
은 카메라 시점에서 누른 좌표로 광선을 쏜다고 가정해 어떤 물체에 부딪히게 된다면 해당 물체를 선택하게 된다는 접근을 기반으로한 기술이다.
Physics.Raycast(위치(Vector3), 방향(Vector3))
Physics.Raycast()
는 많은 형태의 오버로딩이 구현되어 있지만 그 중 시작위치
와 방향
을 사용하는 코드를 가져왔다. 반환 자료형은 bool
로 어떤 Object와 부딪히는지에 대한 정보를 반환한다.
Physics.Raycast()
는 부딪히는 Object를 반환하는 인수로 넘길수도 있고, Raycast하는 광선의 길이도 지정할 수 있는 많은 버전이 존재한다.
인수로 넘어가는 방향(Vector3)은 World기준이다. Obejct가 바라보는 방향으로
Raycasting
을 하고 싶다면 Local->World의 전환이 필요하다.
- Transform에 관련 내용이 있다.
만약 특정 GameObject에 Raycast가 되지 않는다면 해당 GameObject에 Collider가 있는지 확인해보자.
앞서 말한것 처럼 Physics.Raycast()
에는 많은 오버로딩이 구현되어 있는데 그중 하나는 다음과 같은 형태이다.
Physics.Raycast(Vector3 origin, Vector3 direction, out RaycastHit hitinfo, float maxDistance)
collider
, point
, distance
등 여러 정보를 알 수 있다.특정 Object가 바라보는 방향에 Raycasting을 하는 코드
Vector3 look = transform.TransformDirection(Vector3.forward);
RaycastHit hit;
if (Physics.Raycast(transform.position, look, out hit, 10.0f)){
//내용
}
Physics.Raycast()
는 Ray에 부딪히는 첫번째 Object를 가져오는데 만약 모든 Object를 가져오고 싶다면Physics.RaycastAll()
을 사용하면 된다.
Raycating
는 어떤 Object에서 특정 위치의 어떤 방향으로 광선을 쏘았을 때 어떤 Object와 충돌하는지를 알아내는 것이다. 하지만, Raycating
만으로는 화면을 눌렀을 때 어떤 Object가 선택되는지를 구현하기에는 위치
에 해당하는 요소가 무엇인지 알 수 없다.
Rotate에서 Local좌표계와 World좌표계에 대한 얘기를 하였는데 화면좌표에 해당하는 Screen좌표계를 World좌표계로 변환할 수 있다.
Scene에 존재하는 Object들은 카메라의 가시권에 들어오게 되면 사용자의 화면에 출력해야 하는데 결국 3D세상에 있더라도 2D좌표(화면)로 나타내야 한다는 것을 의미한다.
Input.mousePosition
을 사용하면 된다. 반환형은 Vector3
이다.Input.mousePosition
은 화면에 있는 마우스의 픽셀좌표를 의미하고 왼쪽아래가 (0,0)이다.화면은 카메라의 위치와 카메라가 날아가는 방향에 따라 다르다. 여기서 보이는 화면은 카메라를 윗꼭지점으로 하는 사각뿔을 생각하면 되는데 여기서 가장 중요한 것은 일정한 비율이 존재한다는 것이다.
카메라가 Scene을 찍는 영역은 아래 그림과 같이 나타낼 수 있다. 영역안에 있고 카메라 시점에 보이는 화면이 결국 사용자가 보는 화면이 된다.
카메라에 보이는 화면은 위 그림과 같은 구조면 결국 점
이 되는데 실제 보이는 화면은 점
으로 모이기 전에 형성되는 사각형
이며 아래 그림의 파란색 사각형
이 화면에 출력되는 화면이다.
카메라의 속성 중에 Near
와 Far
라는 속성이 있다.
파란색 사각형
까지의 거리빨간색 사각형
까지의 거리, 즉 촬영 영역에 해당Screen 좌표계
에서 화면상의 2D좌표를, 투영
에서는 실제 화면과 카메라 위치와의 깊이차이(Near)를 알아 냈으니 다음과 같은 과정을 거쳐 클릭한 방향 벡터
를 얻을 수 있다.
Input.mousePosition
을 통해 얻는다.Input.mousePosition
의 값은 z=0.0
인 Vector3이다.카메라에서 클릭한 방향벡터
를 얻을 수 있다.위 단계를 한번에 처리할 수 있는 방법이 Unity에 구현되어 있는데 Camera.main.ScreenToWorldPoint()
함수이다. 입력값은 Vector3로 MousePosition.x
, MousePosition.y
, Near
를 입력하면 된다.
Vector3 mousPos = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, Camera.main.nearClipPlane));
Vector3 dir = mousPos - Camera.main.transform.position;
dir = dir.normalized;
Camera.main
은 메인카메라를 의미하기 때문에 다른 카메라를 기준으로 할 때는 Camera.XX
로 사용한다.
Camera.main.ScreenToWorldPoint()
은 클릭한 위치를 World좌표계로 하였을 때의 위치를 Vector3로 반환하는 함수이다.Camera.main.ScreenPointToRay(Input.mousePosition)
을 사용하면 된다.충돌을 구현할 때 물체가 정육면체나 구와같이 모형이 범위를 특정하기 쉬운 물체라면 좋겠지만 울퉁불퉁한 벽과 같은 경우 Collider를 구현하기 힘들다.
연산 부하를 줄이기 위해 충돌 전용 Mesh Collider
를 만들기도 한다. 충돌 전용 Mesh Collider
는 일반 Mesh Collider
보다 더 적은 수의 도형으로 이루어져 있기 때문에 연산 부하가 적다.
같은 Object에 복수의 Collider가 있을 때 어떻게 특정 Collider만 Raycast를 할 수 있을까
Layer를 사용하면 연산하고 싶은 애들만 골라서 Raycast가 가능하다.
Layermask는 bitflag로 실제로 보여줄 Layer의 번호비트를 켜서 여러가지 Layer중 보여주고싶은 Layer만 보여줄 수 있다.
아래 예시 코드는 Monster
또는 Wall
LayerMask를 가진 Object와 Ray가 충돌했을 때를 나타내는 코드이다.
LayerMask mask = LayerMask.GetMask("Monster") | LayerMask.GetMask("Wall");
if (Physics.Raycast(ray, out hit, 100.0f, mask){
//내용
}