유니티 앱을 윈도우 스토어로 포팅하기(1/2)

유니티로 개발한 윈도우 스토어 게임이 많이 출시되고 있다. 한국에서도 유니티를 사용하는 개발자들이 꽤 많고, 한국 마이크로소프트와 유니티 코리아는 유니티 포팅랩을 진행하기도 한다. 온라인에 포팅 관련 참고사항이나 링크를 정리해 둔 자료는 있지만, 포팅 관련한 비교적 자세한 한글 자료가 없는 것 같아서 한번 정리해 보았다. 유니티로 만든 게임으로 스토어에 등록이 잘 되는지 테스트한 결과를 정리한 내용도 있으니 참고하면 좋겠다.

먼저 영문으로 된 공식 가이드를 소개하고자 한다. 플랫폼 별(윈도우, 윈도우폰)로 각각 두 가지 문서가 제공되는데 하나는 유니티로 윈도우 앱을 만드는 개요이고, 또 다른 하나는 포팅의 실제 가이드이다. 또한, 유니티로 윈도우 앱을 개발하는 포럼(영문)이 있는데 포팅하면서 발생하는 기술 이슈 들을 문의할 수 있다. 한글 커뮤니티/포럼은 페이스북의 유니티 개발자 커뮤니티 그룹이나 윈도우 게임개발자 그룹 등이 있다.

이 글에서는 영문 공식 가이드를 참고로 윈도우8 앱으로 포팅하는 방법을 요점만 정리해 본다.

유니티로 윈도우 스토어 앱 개발하기

먼저, 앱 개발 및 포팅에 필요한 것은 다음과 같다.

  • 유니티 4.3 버전 – 무료, 프로 버전 모두 가능
  • 비주얼 스튜디오 – 윈도우8.1 앱이면 VS2013, 윈도우8 앱이면 VS2012를 쓰면 된다. Express for Windows(무료 버전)을 사용할 수도 있다.
  • 윈도우 8.0 이상 버전 – 아직 윈도우8을 사용하고 있지 않다면, 90일 체험판을 사용할 수 있다. 만약 기존 운영체제를 계속 사용하면서 체험하려면 가상화 솔루션을 사용해서 설치를 하면 된다. Windows의 경우 VHD를, 맥의 경우 Boot Camp, VMWare, Parallels 등을 이용할 수 있다.
  • 마이크로소프트 계정 – live.co.kr 또는 hotmail.co.kr 등의 계정(이전 Live 메일)이 필요하다. 없으면 여기서 가입할 수 있다.
  • 스토어 개발자 계정 – 앱을 스토어에 등록할 때 필요하다. 4번의 계정으로 스토어 포털에서 등록할 수 있다. 개인 계정, 기업 계정이 있고 개인 개발자의 경우 개인 계정을 19$(한화로 약 21,216원)로 등록할 수 있다. 계정 유형에 대해서는 여기를 참고하자. 하나의 계정으로 윈도우폰 스토어에도 앱을 등록할 수 있다.(참고)
  • 테스트 장비 – 윈도우8.x를 설치한 어떤 장비에서도 테스트를 할 수 있다. 꼭 태블릿이 아니라 일반 PC에서도 개발 및 테스트가 가능하고, 시뮬레이터를 통해서 멀티터치나 GPS 등의 기능도 테스트해볼 수 있다. 하지만 터치나 센서 등의 기능은 실제 장비에서 테스트해보는 것을 추천한다.

유니티를 이용한 윈도우8 앱 개발 과정을 간단히 정리하면 다음과 같다.

  1. 다른 플랫폼 앱과 동일하게 유니티를 이용해서 컨텐츠를 만든다.
    유니티는 C# 등을 이용해서 개발하는데 Mono 프레임워크에 기반하고 있다. Mono 기반 코드는 .NET과 호환성을 가지고 있고 (C#의 경우) 컴파일 시점에 .NET에 대응하여 컴파일 되기 때문에 Unity 고유의 API를 사용하지 않는 이상 문제가 발생하지 않는다. 자세한 내용은 포팅 가이드(영문)이나 아래 내용 참고하자.
  2. 윈도우8 앱에서 필요한 API 코드를 넣는다.
    #if (UNITY_METRO && !UNITY_EDITOR) 등의 전처리기 코드를 이용해서 유니티 에디터에서는 실행되지 않지만, 윈도우8 앱으로 포팅되었을 때에만 실행되도록 한다.
  3. 윈도우 스토어 앱으로 빌드한다.
    유니티의 빌드 옵션에서 Windows Store Apps를 선택하여 스토어 앱으로 빌드한다. 빌드의 형식에 XAML/Direct3D11과 C#/C++ 를 선택할 수 있는데, 특별히 별도의 UI가 필요없고 퍼포먼스를 높이기 위한 경우가 아니라면 XAML 형식을 추천한다. 그 밖에 윈도우 버전(8.0 또는 8.1) 등을 설정할 수 있다.
  4. 비주얼 스튜디오에서 최종 바이너리를 생성한다.
    유니티에서 스토어 앱을 빌드하면 비주얼 스튜디오 프로젝트를 생성해 준다. 이를 비주얼 스튜디오에서 열어서 테스트, 실행, 배포 등을 할 수 있다.
  5. 앱 패키지를 스토어에 등록한다.
    앱 인증 요구 사항을 확인하고, 자주 겪는 인증 실패 오류를 해결하는 방법을 참고로 하여 앱을 검토한다. 비주얼 스튜디오에서 앱 패키지를 생성하면 나오는 화면에서 인증 검사 툴(WACK=Windows App Certification Kit)로 테스트를 할 수 있는데 이를 통과하면 스토어 대시보드에서 새 앱 제출 메뉴를 이용해서 패키지를 제출하여 인증을 진행한다.

비주얼 스튜디오에서 프로젝트(4단계)를 실행해 보면 다음과 같은 에러가 나는 경우가 있는데 플랫폼이 Debug ARM으로 되어 있는 것을 알 수 있다.

ARM

만약 Surface RT와 같은 ARM 디바이스에서 테스트 하는 것이 아니라, 일반 PC 디바이스라면 Local Machine 버튼 옆에 Debug – Configuration Manager를 실행하여 다음과 같이 설정을 바꾸어 주어야 에러가 나지 않는다.

debug

여기까지가 유니티로 윈도우 스토어 앱을 만드는 개요에 해당하고, 이제부터는 포팅에 관한 내용을 다뤄보자.

윈도우 스토어 용으로 포팅할 때 참고할만한 팁

일단, 포팅 가이드에서 다루는 기법들을 살펴볼 수 있는 샘플 프로젝트가 Github에서 제공되고 있다. Windows 8.1 과 Windows Phone 솔루션이 포함되어 있다.

포팅을 하면서 거치는 다음과 같은 작업들을 살펴 보도록 한다.

  • 유니티에서 컴파일 하기
  • 게임을 불러올 때 진행 상태 표시하기
  • 기기 회전 지원
  • 플랫폼 고유 코드 작성
  • 그래픽 이슈
  • 게임의 일시정지와 다시 시작
  • 윈도우 사이즈 조정
  • 텍스트 입력
  • 퍼포먼스 분석

유니티에서 컴파일하기

윈도우 스토어 앱은 모노 대신 .NET Framework의 부분셋에서 실행된다.(유니티 포팅 가이드에는 .NET Core profile이라고 되어 있는데 .NET for Windows Store apps를 말하는 듯) 그래서 일부 Mono 클래스들은 여기에 없을 수 있다. 유니티 코드에서 컴파일 에러가 발생한다면, 두 가지 이유일 것이다.

  • 클래스 자체가 없다(예. Hashtable)
  • 메서드가 없거나 지원하지 않는 오버로드인 경우.(예. String.Format)

따라서 이럴 경우에는 같은 이름으로 없는 클래스를 직접 만들거나, 없거나 지원하지 않는 메서드 오버로드를 확장 메서드로 직접 만드는 것을 권장한다. 이 방식은 다른 플랫폼에서도 높은 이식성을 유지하고, 유니티나 마이크로소프트가 추후에 지원을 하더라도 쉽게 롤백할 수 있다.

레거시 코드 사용하기

샘플 프로젝트에서도 볼 수 있지만, 일부 오래된 .NET 네임스페이스와 클래스는 Mono와 .NET 어셈블리 간에 혼동을 피하기 위해, 유니티 에디터에서 약간 다른 네임스페이스를 사용하여 참조하고 있다. 그래서 System.IO를 대신하는 네임스페이스는 LegacySystem.IO가 된다. 이 클래스들을 지원하려면 간단히 다음과 같은 코드를 사용하면 된다.


#if UNITY_METRO && !UNITY_EDITOR

    using LegacySystem.IO;

#else

    using System.IO;

#endif

다음은 가장 흔하게 사용되는 없는 형식들과 어떻게 해결할 수 있는지를 정리한 내용이다.

Collections

System.Collections 네임스페이스가 없는데, 여기에는 Hashtable, ArrayList, OrderedDictionary, SortedList, Queue, Stack 등이 포함된다. 샘플 프로젝트에서는 이들의 구현을 포함하고 있는데, 여기에서 해당 소스를 찾을 수 있다.

File storage와 IO

가장 자주 겪는 이슈는 아마도 System.IO.File, System.IO.StreamReader, System.IO.Directory 등 System.IO 네임스페이스에 있는 일부 클래스들이 없는 것인데, 이는 스토어앱이 다양한 파일 IO API를 사용하고 비동기 방식을 지원하면서 다른 네임스페이스를 사용하기 때문이다. 완전한 Wrapper는 아직 없지만, 유니티에서 File과 Directory라고 부르는 2개의 클래스를 UnityEngine.Windows에 포함시켜서 이를 이용해서 게임에 필요한 기본적인 IO 기능을 구현할 수 있다. 역시 샘플 프로젝트의 여기에서 해당 구현을 찾을 수 있다.

Socket 기반의 네트워크 API들

System.NET에 속한 네트워크 클래스들은 사용할 수 없다. WinRT의 Windows.NetworkingSockets 네임스페이스를 이용해서 System.Net.TCPClient를 거의 완전히 구현한 것이 샘플 프로젝트(여기)에 포함되어 있다. 샘플 프로젝트처럼 WinRT의 새로운 소켓 API들(Widnows.Networking 네임스페이스)을 이용해서 확장하거나, Photon과 같은 서드파티를 이용하는 것도 하나의 옵션이다.

Crypto

System.Cryptography의 일부 API들은 사용할 수 없다. 보통 Crypto를 사용하는 작업은 해시들을 계산하는 것인데, UnityEngine.Windows에서 MD5나 SHA1 해시를 계산하는 몇 가지 작은 Wrapper들을 제공한다. 이걸로 충분하지 않으면 Windows.Security.Cryptography를 사용하면 된다.

다른 클래스들…

샘플 프로젝트에서처럼 없는 기능을 직접 구현하여 이식성을 해치지 않고 구현할 수도 있지만, 경우에 따라서는 윈도우 스토어의 네이티브 API를 사용해야 하는 경우도 있는데, 이런 경우에는 아래 자세하게 다룬 “플랫폼 고유 코드 작성” 부분을 참고하자.

서드파티 플러그인들

이 밖에 서드파티 플러그인들을 사용하는 경우, 유명한 플러그인들(NGUI나 Toolkit2D 등)은 이미 윈도우 스토어 용으로 포팅이 되어 있다. 소스코드로 제공이 되는 플러그인(예, NGUI)이 있으면 이는 컴파일러가 알아서 대부분의 이슈를 잡아줄 것이다. 바이너리 형태라면 불러오거나 사용할 때 에러가 날 수 있다. 호환되는지 여부를 알려면 여기에서 실행해보고 Windows Store API와 호환이 되는지 여부를 살펴볼 수 있다. 호환이 되지 않는 플러그인이 있으면 이 친구(jaimer@microsoft.com)에게 메일을 (영문으로…) 보내면 대신 컨택을 해준다고 한다.

게임을 불러올 때 진행 상태 표시하기

유니티로 만든 윈도우 게임을 시작하면 로딩 화면에서 유니티 로고만 덩그러니 나온다. 이 화면을 스플래시 스크린(Splash Screen)이라고 하는데 물론 이 스크린을 적절한 로고나 로딩화면으로 바꿀 수 있는데 두 단계로 구성된다.

  1. 일반 앱 스플래시 스크린
  2. 스플래시 화면 기능 추가

스플래시 스크린

비주얼 스튜디오 솔루션의 앱 메니페스트 파일(Package.appxmanifest)을 열고 Assets 탭에 들어가서 스플래시 이미지 파일을 교체하는 것으로 쉽게 변경 가능하다. 620×300 사이즈의 이미지가 필요하다.

spla

또한, 유니티에서 빌드할 때 윈도우 스토어 앱을 선택하고, Player Settings – Publishing Settings 에서도 설정할 수 있다.

sp

스플래시 화면 기능 추가

리소스의 로딩이 오래 걸리는 경우 스플래시 스크린을 커스터마이징하여 프로그레스바등을 추가할 수 있다.

exspla

앞서 소개한 샘플 유니티 프로젝트(https://github.com/windowsgamessamples/UnityPorting) 에서 간단하게 이를 구현하고 있다.

실행할 때에는 Platformer 폴더를 유니티4.3에서 한번 열어서, Windows Store Apps로 빌드를 하고(이 때 대상 폴더는 PlatformerWindowsStore를 선택한다) PlatfomerApps 폴더에서 솔루션파일을 열어서 실행하면 된다.

unityforwindwos

MainPage.xaml 파일을 열어보면<ProgressBar> 컨트롤을 사용하고 있는 것을 알 수 있다. 이 페이지가 보여진 후에 Unity 로드가 완료되면 게임으로 전환하는 식으로 구현이 가능하다.

xaml

(MainPage.xaml 파일과 짝을 이루고 있는) MainPage.xaml.cs 파일을 보면 여기에 WindowsGateway.UnityLoaded()를 구현하여 유니티에서 로딩이 완료되었을 때 코드를 실행하고 있다. 자세한 설명은 뒤에 “플랫폼 고유 코드 작성” 부분을 참고하자.

// ensure we listen to when unity tells us game is ready
 WindowsGateway.UnityLoaded = OnUnityLoaded;

...

DXSwapChainBackgroundPanel.Children.Remove(ExtendedSplashGrid);

기기 회전(Orientation) 지원

유니티에서 빌드하면 기본으로 가로 모드로 생성이 되는데, 이를 세로 모드로 하고 싶으면 앱 매니페스트 파일(Package.appxmanifest)을 비주얼 스튜디오에서 수정해야 한다.(유니티에서는 수정할 수 없음)

ori

회전을 지원하려면 게임 종류에 따라 UI나 카메라 등을 수정할 수도 있다. 유니티4.3부터는 회전 API(Input.deviceOrientation과 screen.orientation)가 잘 동작한다. 디바이스 특정 씬에 따라 이를 활용할 수 있다.

샘플 프로젝트에서는 유니티에서 OrientationChanged 핸들러를 사용할 수 있는 WindowsPlugin.cs를 제공하는데 관심있는 사람은 참고하자.

플랫폼 고유 코드 작성

인앱 구매 등 플랫폼 API를 사용하려면 고유의 코드를 작성하는 법을 알아야 한다. 여기에는 몇 가지 방법이 있다.

가장 단순한 방법으로 유니티 엔진과 앱간에 직접 연결을 하는 방법이 있다. 하지만, 타입과 커뮤니케이션 방법에 제약이 있을 수 있다.

다른 방법은 직접 유니티 플러그인을 만드는 것이다. 조금 더 수고가 들긴 하지만, 재사용성이 높고 참조할 수 있는 타입이나 API가 열려있다.

두 가지 방법을 다 살펴보려고 하는데 샘플 프로젝트의 /Assets/Scripts/Windows/WindowsGateway.cs에서 찾을 수 있다.

직접 통신

유니티에서 호스트(비주얼 스튜디오) 쪽에서 참조 및 접근할 수 있는 클래스를 만들 수 있다. 샘플 프로젝트의 WindowsGateway에서는 아래와 같이 Action(파라미터가 없는 void delegate)을 노출시킨다. 코드에 대한 설명은 밑에서 자세히 살펴보도록 하자.


static WindowsGateway()

{

#if UNITY_METRO

// unity now supports handling size changed in 4.3

UnityEngine.WSA.Application.windowSizeChanged += WindowSizeChanged;

#endif

// create blank implementations to avoid errors within editor

UnityLoaded = delegate {};

}

/// <summary>

/// Called from Unity when the app is responsive and ready for play

/// </summary>

public static Action UnityLoaded;

비주얼 스튜디오 쪽에서는 다음과 같이 WindowsGateway.UnityLoaded를 이용하여 이벤트 방식으로 코드를 실행시킨다.

// ensure we listen to when unity tells us game is ready</span>

WindowsGateway.UnityLoaded = OnUnityLoaded;

/// <summary>

/// Unity has loaded and the game is playable

/// </summary>

private async void OnUnityLoaded()

{

/* here you can use WinRT APIs that your unity scripts did not know about and would not have been able to reference. */

}

이 글의 처음에서도 잠깐 소개했지만, #if UNITY_METRO && !UNITY_EDITOR 와 같은 컴파일러 전처리기는 윈도우 스토어 앱이 실행될 때만 해당 코드를 실행시킨다. 참고로, 윈도우와 윈도우폰을 함께 실행시키려면 UNITY_WINRT를 사용할 수 있다.

한 가지 주의해야 할 부분은, 스토어 앱과 유니티 측이 각각 다른 Thread로 실행이 되기 때문에 Thread를 넘어서 호출을 처리하는 경우 마샬링을 해주어야 한다.  유니티 측 호출은 App Thread 상에서 실행해야 하는데 스토어 앱 측에서 아래와 같이 호출할 수 있다.

AppCallbacks.Instance.InvokeOnAppThread(() =>
{
 // back to Unity
}, false);

AppCallbacks는 MainPage.xaml.cs 등에서 참조하는 UnityPlayer 네임스페이스에 속한 클래스로 C++과 C# 에서 모두 사용할 수 있고, 스토어 앱과 유니티 엔진 사이에 브릿지 역할을 하면서 여러 가지 메서드를 제공한다.

반대로, InvokeOnUIThread() 메서드는 유니티 앱 Thread에서 윈도우 스토어 앱 측에서 실행하고자 할 때 사용할 수 있다. 인앱 구매 같은 경우에 UI thread로 하지 않으면 예외가 발생한다.

AppCallbacks.Instance.InvokeOnUIThread(() =>
{
// UI Thread
}, false);

직접 호출 방식은 호스트(스토어 앱 측) 코드가 특정 씬에 묶여 있을 경우에 메모리 누수가 발생할 수 있으므로 주의해야 하고, 약한 참조(쉽게 레퍼런스를 풀어주거나 이벤트 구독 해지할 수 있는)와 같은 기법을 사용할 수도 있다.

유니티 플러그인

(2부에서 계속…)

Advertisements

One comment

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Google+ photo

Google+의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

%s에 연결하는 중