Uing? Uing!!

유어테일 개발 - 01. Ray2D를 통한 2D 터치이벤트 구현 본문

Unity

유어테일 개발 - 01. Ray2D를 통한 2D 터치이벤트 구현

Uing!! 2018. 12. 20. 02:31
반응형



우선 가장 메인이 되는 GameScene의 바를 구현하기로 했다.
일러스트 소스가 없으니 우선 아무 이미지나 가져다 넣었다. 책에서 사용한 고양이 소스를 임시로 사용했다.
말풍선이나 다른 내용물은 일러스트레이터로 가능한 가장 단순하게 만들었다. 나중에 수정해야지.

잘 진행이 되다가 터치 이벤트를 작성하면서 문제가 생겼다.
내가 본 책에서는 Ray라는 걸 사용하는 법을 알려 주지 않았는데, 구글에 물어 보니 특정 오브젝트를 인식하려면 Ray를 사용해야 한단다.
그래서 사용하려고 보니 2D Ray에 대한 정보는 많지도 않았다.
2D가 3D보다 쉬울 거라고 생각했는데, 유니티가 3D를 염두에 두고 메소드들을 만들어서 그런지 2D로 변환해 사용하기가 쉽지는 않더라.
아무튼, 몇 시간을 구글링한 끝에 2D 터치를 구현하는 법을 알아냈다.
원리는 카메라에서 내가 터치한 방향으로 Ray를 쏘아서, 그 Ray가 부딪히는 collider를 감지하는 것이었다.
우선 2D이므로 Ray2D 변수를 만들어 준다. Ray2D를 만들어 줄 때에는 아래와 같이 코드를 작성한다.
Vector2 wp = Camera.main.ScreenToWorldPoint(Input.mousePosition); // 터치한 좌표를 가져 옴
Ray2D ray = new Ray2D(wp, Vector2.zero); // 원점에서 터치한 좌표 방향으로 Ray를 쏨
RaycastHit2D hitDrawer = Physics2D.Raycast(ray.origin, ray.direction,  distance, 1 << LayerMask.NameToLayer("drawer")); 
    // ray의 시작점에서, ray 방향으로, 최대 distance까지 감지하며, "drawer"라는 이름을 가진 레이어에 해당하는 오브젝트만 감지한다.
여기에서 내가 특별히 헷갈렸던 부분은 LayerMask 부분이다.
일단 Ray를 쏘면 닿는 개체를 감지하게 되는데, 특정한 object만을 감지하고 싶다면 레이어를 설정해 주는 방법으로 구분을 줄 수 있다.
오브젝트를 감지해 내기 위해서는 부딪힌 collider의 name, Tag, Layer 등으로 구분을 줄 수 있는데, 일차적으로는 레이어로 구분을 주고 LayerMask를 씌워 투영하는 것이 대부분인 듯하다.

Unity 도구들 중 'Edit -> Project Settings -> Tags and Layers'에 들어가면 레이어들을 편집할 수 있고, 이후 각 객체를 선택하면 Inspector에서 Tag와 Layer를 설정해 줄 수 있다.
0에서 7까지는 미리 셋팅되어 있는 레이어이므로, 사용자가 설정 가능한 레이어는 8부터이다.
설정을 해 준 이후에는, 코드에서 Ray에 감지할 Layer를 지정해 주어야 한다.
Layer 번호를 가져오는 것은 번호를 직접 사용할 수도 있고 (예를 들어 drawer 레이어가 8번이라면, 8이라는 숫자를 사용), LayerMask.NameToLayer(레이어 이름) 메소드를 사용할 수도 있다.
중요한 점은 Layer 번호를 가져온 후 이것을 LayerMask로 바꿔 주려면, LayerMask 타입인 '1 << 번호' 형식을 취해햐 한다는 것이다.

위 코드에서는 코드가 길어지는 것이 싫어 매개변수에 직접 '1 << LayerMask.NameToLayer("drawer")'를 넣어 주었으나,
아래와 같이 LayerMask 변수를 새로 만들어 주어 ray 생성의 매개변수로 이용해 줄 수도 있다.

LayerMask drawerLayer = 1 << LayerMask.NameToLayer("drawer"); // LayerMask.NameToLayer("drawer") 대신 레이어 번호(8 등)을 사용할 수도 있다.
RaycastHit2D hitDrawer = Physics2D.Raycast(ray.origin, ray.direction, distance, drawerLayer);
아래는 내 작업에서 전체 동작을 제어하는 GameDirector.cs의 코드 중 일부이다.
코드는 drawer가 닫혀 있을 때 누르면 열리고, 열려 있을 때 누르면 닫히는 동작을 수행한다.

void Update() { if (Input.GetMouseButtonDown(0)) { Vector2 wp = Camera.main.ScreenToWorldPoint(Input.mousePosition); // 터치한 좌표 가져옴 Ray2D ray = new Ray2D(wp, Vector2.zero); // 원점에서 터치한 좌표 방향으로 Ray를 쏨 float distance = Mathf.Infinity; // Ray 내에서 감지할 최대 거리 RaycastHit2D hit = Physics2D.Raycast(ray.origin, ray.direction, distance); // 다 잡음 RaycastHit2D hitDrawer = Physics2D.Raycast(ray.origin, ray.direction, distance, 1 << LayerMask.NameToLayer("drawer")); // drawer 레이어만 잡음 if (hit) { // 게임 배경화면 등이 눌렸을 때 에러가 발생하는 것을 방지하기 위한 부분 if (hitDrawer) { // drawer 눌렀을 때 if (!drawer.GetComponent<drawercontroller>().IsOpen()) { Debug.Log("열려라 참깨"); drawer.GetComponent<drawercontroller>().Open(); } else drawer.GetComponent<drawercontroller>().Close(); } } } }


그리고 왜인지 유니티에서 drawer를 한 번 열고 나면 그 때부터 겹쳐 있는 이미지들을 인식하지 못하는 오류가 발생했는데, 몇 시간을 고민했지만 해결이 되지 않았다.
그러다가 안드로이드에 빌드하려고 보니 UI Button들의 해상도가 pixel 기준으로 되어 있어, Canvas 오브젝트의 Canvas Scaler 중 Screen Match Mode를 Scale With Screen Size로 변경했더니, 갑자기 문제가 해결되었다.
정확한 이유는 모르겠지만 유니티 내부에서 정의된 Ray를 생성하고 hit를 감지하는 방식, 그리고 Canvas의 내부 좌표계와 관련이 있지 않을까 싶다.

고양이 이미지를 예시로 사용했더니 아래처럼 귀여워져 버렸다.
그냥 고양이 칵테일 바로 컨셉을 바꿔 버릴 지 고민 중이다.


반응형
Comments