멍멍이네 블로그

유니티 3.0 버젼을 시작으로 어느덧 저도 유니티를 사용한지 3년이 넘어가고 있네요.

손쉽게 개발하는 툴로써 게임 제작 환경 중에서는 굉장히 뛰어난 환경이죠.

그렇지만, 뭐든 그렇지만 완벽한건 없습니다. 

 

아무래도 유니티가 3D엔진이다보니, 2D게임을 만들때도 이런저런 추가적인 지식이 필요하게 됩니다. 

 

이런 고민/공부 하시기 싫으시면 남들이 만든 2D toolkit을 받으셔서 작업하시는것도 방법이긴 합니다만, 유니티 환경을 깊이 이해하시려면 피해갈 수 없는 것들이기도 하네요.

 

이 글은, 그런 몇가지 지식들을 정리해놓은것입니다.

초보자용은 아니기 때문에, 기초적인 정보들은 많이 생략되었습니다.

 

------------------------------------------------------

1대1 픽셀 맞추기

------------------------------------------------------

 

유니티 엔진에는 "카메라"라는 컨셉이 존재합니다.

새프로젝트 만들어서 Scene이 만들어지면 다른건 없고 Main Camera가 하나 생기죠.

 

 

 

우선, 카메라에 Projection이라는 설정이 있습니다.

Orthographic일 경우에는 3D세계를 무조건 2D평면으로 변환해서 그리는 방식을 뜻하고 있습니다. 

간단하게 말하자면, 카메라의 z축 위치와는 상관없이 3D상의 object들을 일정한 크기로 그린다고 보시면 되죠.

그 증거로 이 모드에서는 카메라를 z축으로 아무리 움직여셔도 나오는 사물들의 크기가 변하지가 않죠. 

(너무 멀리 떨어지면 없어지긴 하죠)

 

특별히 신경 쓰지 않고 일정한 크기로 그래픽을 그릴 수 있다는 장점 때문에, 2D게임 그래픽을 다룰때는 이래저래 Orthographic 모드가 편할때가 많습니다.

 

반대로 Projection모드를 Perspective로 바꾸시면, 일반적으로 3D게임 (FPS나 온라인 게임) 등에서 보실 수 있는 3인칭 카메라가 되죠. 

카메라의 위치에 따라 사물들이 가까워보이거나 멀리 보이거나 하죠.

 

3인칭 카메라로 2D게임을 못 만들 이유는 없습니다. 카메라가 항상 직각으로 서있고 사물들이 일정한 거리에 있으면 항상 같은 크기로 그려지기 때문에 충분히 가능은 합니다만, z위치에 따라서 크기 맞추기위해서 계산을 해야한다는 단점이 있습니다. 그렇지만, 반대로 카메라가 x,y축으로 이동을 하면 멀리 있는 사물들은 천천히 움직이고, 가까운 사물들이 빨리 움직이는, 일명 Parallax Scrolling, 효과가 자동으로 된다는 장점이 있긴 하죠. 유니티에서는 카메라를 여러개 두고 복합적으로 사용할 수도 있고 해서, 경우에 따라서는 3인칭 카메라도 유용할수 있습니다. 

 

그렇지만, 여기서는 Orthographic 모드를 살펴보도록 하겠습니다.

위에 스크린샷에서 Projection 세팅 바로 밑에 Size라는 항목이 보이는데, 이 Size가 카메라의 줌인 레벨을 결정합니다.

Size가 작을수록 카메라가 더 줌인 된 효과가 나옵니다.

 

<그림 픽셀 크기와 화면 픽셀을 1대1로 맞추는 방법>

목표로 가지고 계시는 화면 해상도 (저의 경우에는 800 x 450이였습니다) 의 높이 (450) 의 반값 (225) 를 사용하시면 됩니다. 이렇게 되면 그 해당 해상도에서는 100% 줌레벨이라고 보시면 되겠습니다.

 

해상도가 달라지면 그만큼 줌인/줌아웃 레벨이 달라지게 되면서 그림이 화면에 차지하는 비율을 일정하게 됩니다. 

예를 들어 위와 같은 세팅으로 풀스크린 이미지를 그린 다음에 해상도를 1600x900으로 바꾸면 여전히 풀스크린을 유지합니다. 1600x900 화면에 800x450짜리 이미지를 늘려서 그린거나 마찬가지죠.

 

그렇다면 Sprite 그래픽 이미지를 추가해보겠습니다.

 

 

 

우선 추가된 그림 파일의 Texture Type이 Sprite (2D)로 되어 있으면 되죠. (2D 프로젝트에서는 이게 기본)

Pixels To Units라는 항목이 있는데 이건 Scale 크기와 픽셀의 비율이라고 보시면 됩니다. 

기본값 100으로 되어 있기 때문에, Sprite Object의 Scale x,y값이 100,100 이 되면 원본 크기의 100%가 됩니다.

 

 

 

카메라 세팅과 Sprite Texture 세팅을 제대로 하셨다면 화면 픽셀과 실제 픽셀 크기 1대1 이미지가 그려지는 것을 확인하실 수 있습니다. 

정확하게 확인하시려면 해상도와 같은 사이즈의 이미지를 그리시면 비교 하실 수 있겠죠. (저는 좀 작은 이미지를 그렸습니다.

 

 

 

정리를 하자면, 

Camera Orthographic Size => 해상도 높이의 반값

Sprite Texture Pixel To Units => 100

Sprite Game Object Scale X,Y => 100,100

 

저는 이런식으로 사용하는게 편합니다만, 어떤 분들은...

 

Camera Orthographic Size => 해상도 높이의 반값 / 100

Sprite Texture Pixel To Units => 100

Sprite Game Object Scale X,Y => 1,1

 

...으로 맞춰서 사용하는것도 본적 있습니다. 

저보다 Camera를 100배 줌인하고, Sprite을 1/100 사이즈로 맞춘거랑 마찬가지죠.

이건 편하신대로 맞춰서 쓰시면 되겠습니다.

 

------------------------------------------------------

Bilinear Filter vs Point Filter

------------------------------------------------------

 

 

Sprite Texture 세팅에 보시면 Filter Mode라는 항목이 있습니다. 

현재는 Bilinear라는 Filter로 되어 있습니다만, 도트 그래픽이나 저해상도 그래픽의 경우에는 Point로 바꿔써야할때가 있습니다.

 

   

 

도트 그래픽을 Bilinear Filter (왼쪽 그림) 를 쓰면, 버리버리해져서 뚜렷하지 못하다는것을 확인하실 수 있습니다.

 

대신에 Point의 단점은 원본크기보다 크게 그려지거나 작게 그려지거나 했을때 픽셀이 뭉게질 수 있다는 점이 있습니다. (해상도가 바뀌거나 했을때) 

 

기본적으로 위와 같이 저해상도 도트 스프라이트는 Point를 써야됩니다. 해상도가 높은 그림은 Bilinear나 Point나 크게 차이를 못 느끼기 때문에 Bilinear로 가는게 무난합니다. UI로 쓰는 그래픽도 여러 해상도에서 깔끔하게 보이기 위해서 Bilinear를 많이 쓰는 편입니다만, 경우에 따라서는 너무 버리버리하게 나올때가 있어서 Point를 쓸때도 있습니다. 결국 그때그때 상황에 따라서 판단해야겠죠.

 

------------------------------------------------------

Windows DirectX9 렌더링 문제

------------------------------------------------------

 

여기서 설명하는 문제는 Filter Mode를 Point로 사용했을 때 나타납니다.

위에서 설명한 방식대로 2D 도트 그래픽을 ​유니티 윈도우에서 개발하다보면, 이런 현상을 발견하게 됩니다. (세팅에 따라서는 안 일어날수도 있습니다. 그래도 알아두시면 좋겠죠.) 

 

  

 

보시다시피, 위에서 보였던 체크 무늬와 스프라이트 이미지인데, 픽셀들이 일그러져 있죠.

결국 인터넷을 뒤져서 찾아낸건 윈도우 DirectX9 렌더링을 할때 Texture의 좌표 계산이 0.5씩 어긋나면서 생기는 이상한 문제라고 하네요. XNA에서도 비슷한 문제가 있었다고 들은적이 있습니다. 

자세한 내용은 여기서 설명 --> http://drilian.com/2008/11/25/understanding-half-pixel-and-half-texel-offsets/

 

다행인건 이 현상이 DirectX9 문제기 때문에 OpenGL을 쓰는 다른 OS (iOS, Android, 등등) 들은 문제가 없습니다. 즉, 윈도우에서 개발 하실때는 지글거려도, Android로 빌드하시면 문제가 없어진다는 얘기죠.

 

그리고 윈도우에서도 DirectX11 모드로 맞추시면, 문제가 사라지긴 합니다.

 

 

 

Player Setting에 Use Direct3D 11을 세팅하거나, Unity 실행 뒤에 -force-opengl 이라는 flag를 추가해서 실행해보면 멀쩡하게 나오는 것을 확인하실수 있습니다. 사실, 간단하게 Direct3D 11으로 설정해서 배포하는것도 해결책인거 같습니다.(DirectX 11을 지원 안하는 컴퓨터는 포기하셔야겠지만..요즘 다들 컴퓨터 좋으니까요..)

 

그렇다면, DirectX 11을 사용 안하고 Windows로 배포하거나 WebPlayer로 배포할때, 이 문제를 해결할수는 없는가.

뭐, 아주 없는건 아닙니다.

 

 

보통 스프라이트를 생성하면 Sprites-Default라는 기본 Material이 적용됩니다.

 

 

 

이런 문제가 생기는 스프라이트에다가 새로운 Default Material을 하나 생성하셔서 적용을 하시고, Shader를 Sprites/Default로 설정하시고 밑에 나오는 옵션 중 Pixel snap을 체크하시면 지저분했던 스프라이트가 깔끔해지는것을 보실 수 있습니다. 이 옵션은 DirectX에서 0.5씩 밀려나간 Texture 좌표를 교정해주는 역할을 해줍니다. (OpenGL 환경에서는 변화가 없습니다)

 

그렇지만, 제가 실험해본결과 Position이나 Scale 값 소수점에 따라서는 여전히 지글거리는 현상이 보일때도 있습니다.

왠만한 문제들은 해결합니다만, 완벽한 해결책은 아니라는 얘기죠. 주의하시길.

 

------------------------------------------------------

Multiple Sprite + Draw Call 줄이기

------------------------------------------------------


​이건 특별히 2D게임뿐만은 아니지만, 화면에 많은 그래픽을 그려야될때는 렌더링 퍼포먼스를 늘리기 위해서 DrawCall 수를 줄이는게 좋습니다.  

 

 

 

유니티에서는 Game창 우측 상단에 Stats라는 버튼을 누르시면 위의 그림과 같은 정보가 표시됩니다.

두번째 줄에 Draw Calls가 2라고 적혀 있죠. 배경 그림 하나와 사람 그림 하나를 그리기 때문에 Draw Call이 2개죠.

 

  


이번에는, 사람을 Duplicate시켜서 3명으로 만들어봤습니다. 

그림을 4개 그리니까 Draw Call이 4개라고 생각하시겠지만, Stats를 보니 Draw Call이 2개, 그리고 Saved by batching이 2개입니다.

이것은 같은 이미지를 여러번 그릴때 그래픽 카드(GPU)에서 효율적으로 묶어서 렌더링을 할수 있기 때문에 실질적인 Draw Call은 2번이라는 것이죠. 이처럼, 같은 이미지가 여러번 반복되면 Draw Call이 적다는 얘기죠.

 

 

 

다음, 사람 포즈를 다른 그림으로 바꿔봤습니다.

그렇지만, 여전히 Draw Call은 2개네요. 이건 왜일까요? 그림이 종류가 4가지인데...

 

알고 보니, 사람들 Sprite은 한장의 그림에서 나온것입니다. (Sprite Sheet이라고도 부르죠.)

 



 

 

Sprite Texture 세팅에 Sprite Mode를 Multiple로 하고, Sprite Editor 버튼을 누르시면 한장의 그림을 쪼개서 여러 Sprite를 만들 수 있습니다. 이렇게 생성된 여러 Sprite들은 같은 Texture를 사용하기 때문에 여전히 하나의 Draw Call로 렌더링을 할 수 있는 것입니다.

 

이런 Multiple Sprite를 잘 활용하면 Draw Call을 줄일 수 있다는 얘기죠.



------------------------------------------------------

Texture 대신에 그림 파일 직접 사용하기

------------------------------------------------------


​유니티에서 PNG 같은 그림파일을 프로젝트에 집어넣으면, 이것저것 처리를 하면서 Sprite Texture를 만들어줍니다. 

기본적으로 게임에서 사용할때는 별 문제 없죠.

다만, 비주얼노벨 같이 일러스트 그림이 잔뜩 들어있는 프로젝트 같은 경우에는 Texture 사이즈가 좀 부담스러워질때가 있습니다.

 

예를 들자면, 1280x720 이미지를 프로젝트에 집어넣으면 Texture가 생성되죠.

 

 

 

색깔 손실을 줄이기 위해서 TrueColor로 지정하고, Max Size를 2048로 늘리면 원본 이미지가 그대로 Texture로 변환이 되죠.

사이즈를 확인해보면...2.6 MB

원본 PNG이미지 1.55MB 에서 1MB 이상이 붙은 셈이죠.

 

왜 2.6MB 일까요? 

유니티 Texture를 True Color (즉, Compress를 안 한 상태) 로 쓰게 되면 단순하게 계산해서

1280 x 720 x 3 byte (R,G,B) = 2764800 byte = 2700 kb = 2.63671875 MB가 나옵니다.

만약에 투명한 부분이 있어서 Alpha값까지 있는 그림이였다면, 

1280 x 720 x 4 byte (R,G,B,A) = 3686400 byte = 3600 kb = 3.515625 MB 입니다.

 

PNG나 JPG는 이런 RGBA 데이터를 압축시킨 이미지 파일 형식이기 때문에 용량이 더 적은것이죠.

이런 풀스크린 그림이 비주얼 노벨에서는 BCG, ECG 등 몇100장이 될때도 있으니, 용량을 줄일수 있다면 좋겠죠.

 

(Format을 TrueColor 대신에 Compressed를 사용하면 될수도 있겠지만, 일러스트 그림같은 경우 색깔 손실이 커서 사용 못하는 경우가 많습니다.)

 

그렇다면 유니티 내부적으로 PNG나 심지어 JPG를 바로 사용할수는 없는가. 

방법이 있습니다.

 

우선, .png 파일들을 모두 .bytes로 이름을 변경하시고 프로젝트로 가지고 오시면, Texture로 인식이 되지 않습니다.

오히려 텍스트 파일같은걸로 인식이 되죠.

 

 

그래서, 스크립트 파일에서 텍스트 파일 (Text Asset) 다루듯이 로딩을 한 후에, TextAsset.bytes를 사용하여 Texture.LoadImage로 Texture를 만드실수 있습니다.

 

PNG 10장 가지고 빌드 용량 실험해본 결과:

 

Windows EXE

.png 사용했을때 46메가

.bytes 사용했을때 35메가

약 11메가 차이

 

WebPlayer

.png 사용했을때 18메가

.bytes 사용했을때 15.6메가

약 2.5 메가 차이

 

Android

.png 사용했을때 29.2메가

.bytes 사용했을때 23.6메가

약 5.5 메가 차이

 

Windows EXE에 비해서 WebPlayer와 Android가 차이가 적은 이유는 Android, WebPlayer 둘다 리소스를 자체 압축을 하기 때문입니다. 경우에 따라서는, 오히려 .bytes 버젼이 용량이 조금 클 수도 있습니다. (전체 압축 효율이 얼마나 되냐에 따라서)

그렇지만, Windows나 실험은 안했지만 iOS 같이 자체 리소스 압축을 안할 경우에는 용량을 많이 줄일 수 있습니다.

 

그렇다면, 단점은 무엇인가.

당연히 로딩 타임입니다. 

 

제 컴퓨터의 경우에는, 

일반 Sprite Texture를 10개 Resources.Load<Sprite>() 으로 로딩할 경우 40~50 ms가 걸리는데 비해, 위의 코드처럼 bytes text파일을 열어서 로딩할 경우 10개 로딩하는데 500~600 ms가 걸립니다.

 

10배 이상 차이가 나는 결과입니다. 

그렇기 때문에 그림 로딩이 많이 필요한 경우에는 적합하지 않습니다만, 비주얼 노벨 같이 유저가 클릭을 할때 그림을 한장 한장씩 로딩해도 충분히 가능한 경우에는, 조금만 신경쓰면 충분히 사용할 수 있습니다. 

 

로딩 타임과 빌드 용량을 고려하시면서 적절히 사용하시길 바랍니다.

 

 

 

출처 : http://blog.naver.com/mooshicow/220162023231