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

유니티 플러그인

플러그인은 유니티 스크립트로 참조할 수 있는 바이너리 dll이다. 플러그인은 유니티에서 직접 참조할 수 없는 플랫폼 별 고유 코드들을 포함할 수 있다. 윈도우 역시 이를 이용해서 WinRT API들을 호출할 수 있다.

윈도우 스토어 용 플러그인을 만드는 간단한 설명은 여기서 확인할 수 있다. 유니티 사이트의 설명은 개념만 설명하고 있어서 처음부터 플러그인을 만드는 방법을 알아보려면 여기(튜토리얼을 따라해도 잘 안 되는 부분이 있는데 추후에 이 부분은 따로 다뤄볼 생각이다…)를 참고하자.

유니티 샘플 프로젝트에 포함되어 있는 MyPlugin 솔루션을 한번 살펴보자. 이 프로젝트에 윈도우 스토어 앱과 관련해서는 2개의 플러그인이 있다.

  • MyPluginUnity – .Net 3.5 class library
  • MyPluginWindows – Windows 8.1 class library

둘 다 MyPlugin.dll 로 같은 이름의 어셈블리를 만들지만(유니티 요구사항) 각각은 빌드 후에 비주얼 스튜디오의 빌드 설정에 있는 Post build script에 의해서 다음과 같은 폴더에 복사된다.

  • /Assets/Plugins/MyPlugin.dll   – 유니티 에디터에서 사용함.
  • /Assets/Plugins/Metro/MyPlugin.dll    – 비주얼 스튜디오 프로젝트에서 런타임에 실행됨.

이 플러그인은 다음과 같이 유니티 스크립트(/Assets/Scripts/ShareManager.cs)에서 ShowShareUI()를 실행할 수 있게 한다.

/// <summary> 
/// Handles Share Integration 
/// </summary> 
public class ShareManager : MonoBehaviour  
{ 
    void OnGUI() 
    { 
        if (GUI.Button(new Rect(Screen.width - 120, 20, 100, 20), "Share")) 
        { 
#if UNITY_WINRT 
            MyPlugin.WindowsPlugin.ShowShareUI(); 
#endif 
        } 
    } 
} 

1부에서 언급한 것처럼 #if UNITY_WINRT 안의 구문은 윈도우와 윈도우폰 모두에서 실행된다.

유니티 에디터를 위해 생성한 MyPluginUnity는 같은 API를 노출하는 것 외에는 특별히 하는 것이 없다. 대부분의 경우 에디터용 dll에는 어떠한 처리나 실행도 하지 않고, 유니티가 스크립트를 컴파일 하기 위해서 존재한다.

이 글의 1부에서 Thread 를 넘어 호출할 때 사용한다고 했던 AppCallbacks을 플러그인에서는 사용할 수 없다. 따라서 별도의 작업을 해주어야 하는데 샘플 프로젝트에는 미리 이 작업이 되어 있다.

MyPluginWindows 프로젝트를 보면 AppCallbacks의 Thread 마샬링 관련 메서드들을 대신할 static 프로퍼티를 갖고 있는 Dispatcher라는 클래스를 찾을 수 있다.

/// <summary> 
/// Handles dispatching to the UI and App Threads 
/// </summary> 
public static class Dispatcher 
{ 
    // needs to be set via the app so we can invoke onto App Thread 
    public static Action<Action> InvokeOnAppThread 
    { get; set; } 

    // needs to be set via the app so we can invoke onto UI Thread 
    public static Action<Action> InvokeOnUIThread 
    { get; set; } 
} 

이제 윈도우 스토어 앱 측에서 이 속성들을 지정해 주면 된다.

public App() 
{ 
    this.InitializeComponent(); 
    appCallbacks = new AppCallbacks(false); 
    appCallbacks.Initialized += appCallbacks_Initialized; 
} 
void appCallbacks_Initialized() 
{ 
    MyPlugin.Dispatcher.InvokeOnAppThread = InvokeOnAppThread; 
    MyPlugin.Dispatcher.InvokeOnUIThread = InvokeOnUIThread; 
} 
public void InvokeOnAppThread(Action callback) 
{ 
    appCallbacks.InvokeOnAppThread(() => callback(), false); 
} 
public void InvokeOnUIThread(Action callback) 
{ 
    appCallbacks.InvokeOnUIThread(() => callback(), false); 
} 

그럼 이제 플러그인에서는 다음과 같이 사용하면 된다.

// From UI Thread
Dispatcher.InvokeOnAppThread(() => 
{ 
    // back to Unity 
}, false); 

// From App Thread
Dispatcher.InvokeOnUIThread(() => 
{ 
    // UI Thread 
}, false); 

여기까지 플러그인 방식을 살펴보았는데 두 가지 방식 중에 어떤 것을 사용하는 것이 좋을까? 아래의 장단점을 보고 상황에 맞게 선택해야 한다.

플러그인

  • 재활용 가능한 플랫폼 종속 코드를 바이너리로 캡슐화하는데 최적
  • 일반적으로 더 추상화된 시나리오에 적합
  • 설정과 유지보수에 조금 더 시간이 많이 소요됨
  • 유니티에서 플랫폼 종속 코드를 다루는 방식으로 추천

의존성 있는 코드의 사용(직접 통신)

  • 더 빠르게 이해하고 구현 가능
  • 단순하게 유니티 스크립트와 앱 클래스에 직접 추가
  • 앱과 유니티 간에 양방향 통신 지원
  • 재활용에 좋지 않음. 프로젝트 간에 복사 및 붙여넣기를 유발할 수 있음

그래픽 이슈

가이드를 보면 Unity 4.3 릴리즈 시점에 유니티에 내장된 쉐이더에 약간의 이슈가 있다고 한다. 가장 흔하게 발생하는 이슈는 다이렉트X 11의 Feature Level 9.1에서 Fixed Function 쉐이더들이 동작하지 않는 것이다.

유니티에서 그래픽 에뮬레이션 레벨을 설정해서 쉐이더 이슈의 대부분은 찾아낼 수 있다.(Edit –> Graphics Emulation)

스토어 앱을 만들 때에는 2가지 레벨 중에 선택할 수 있다.

  • DirectX 11 9.3 (shader model 3)
  • DirectX 11 9.1 (shader model 2) No fixed function. 이 설정은 Surface RT의 1세대에서 쓰였기 때문에 이 단계로 낮추는 것을 권장한다.

쉐이더를 직접 작성했다면, 쉐이더 스테이지 간에 전달하는 모든 변수에 시맨틱이 요구된다.

예를 들면, 이런 경우에 에러가 발생한다.

struct vertOut {     
    float4 pos:SV_POSITION;      
    float4 scrPos;  	 
}; 	//ERROR! no semantic 

다음과 같이 간단히 고칠 수 있다.

struct vertOut {
     float4 pos:SV_POSITION;
     float4 scrPos: TEXCOORD0; // <-- FIX! add semantic. 
}; 

여기에 쓰인 TEXCOORD[n]은 일반적 용도의 시맨틱으로, 아무 것도 지정된 것이 없을 때 쓰면 좋다.

쉐이더와 관련한 몇 가지 다른 이슈들은 다음과 같다.

  • Fixed function 쉐이더는 shader model 2에서 지원하지 않는다.
    유니티 쉐이더는 surface 쉐이더, vertex and fragment 쉐이더, 그리고 fixed function 쉐이더 등 3가지 유형인데, Surface 쉐이더는 shader model 2에서 지원하지 않는다.
  • feature level 9.3 에서는 Fog가 동작하지 않는다.
    직접 구현을 해야 하는데, 유니티에서 샘플 fog 쉐이더를 여기에 공유하고 잇다. 샘플에도 fog 쉐이더를 구현한 것이 있다.

일시 정지와 다시 시작 구현하기

앱 화면이 전환될 때 게임이 적절히 일시 정지 상태로 전환되는지 확인해야 한다. 게임 루프를 일시 정지 하기 위해서는 UnityPause 메서드를 호출한다.

앱이 전환됐는지 여부를 파악하기 위해서 VisibilityChanged 이벤트를 앱 메인 윈도우에서 사용할 수 있다.

다음 코드는 이벤트를 이용해서 일시 정지(1)와 다시 시작(0)을 구현하는 예이다.

Window.Current.VisibilityChanged += OnWindowVisibilityChanged; 
 
private async void OnWindowVisibilityChanged(object sender, VisibilityChangedEventArgs e)
{ 
    if (e.Visible) 
    {
        if (AppCallbacks.Instance.IsInitialized()) AppCallbacks.Instance.UnityPause(0);
        return;
    }
    else
    { 
        if (AppCallbacks.Instance.IsInitialized()) 
        { 
            AppCallbacks.Instance.UnityPause(1); 
        } 
    } 

윈도우 사이즈 조정

앱의 화면 사이즈는 최소 320px 또는 500px에서 모니터 화면 사이즈에 따라 커질 수 있다. 유니티 4.3에서는 UnityEngine.WSA 네임스페이스를 통해 바로 처리할 수 있다. 유니티 스크립트인 WindowsGateway에서 사용하고 있다.

static WindowsGateway() 
    { 
 
#if UNITY_METRO 
 
        // unity now supports handling size changed in 4.3 
        UnityEngine.WSA.Application.windowSizeChanged += WindowSizeChanged; 
#endif 
 
    } 
#if UNITY_METRO 
 
    /// <summary> 
    /// Deal with windows resizing 
    /// </summary> 
    public static void WindowSizeChanged(int width, int height)  
    { 
 	    // TODO deal with window resizing. e.g. if <= 500 implement pause screen         
        if (width <= 500) 
        { 
            SnapModeManager.Instance.Show(); 
        }
        else
        { 
            SnapModeManager.Instance.Hide(); 
        } 
    }  
   
#endif 

윈도우에서 텍스트 입력

(작성중…)

디버깅과 퍼포먼스 분석

디버깅하기

비주얼 스튜디오를 이용해서 앱과 유니티의 C# 스크립트를 모두 디버깅할 수 있다.

  • File->Add > Existing Project 메뉴에서 비주얼 스튜디오의 솔루션에 유니티 폴더의 Assembly-Csharp 프로젝트를 추가한다.
  • 선택적으로 Assembly-CSharp는 Build 플래그를 해제한다.(이미 유니티에서 빌드되었으므로)
  • Project > Properties > Debug에서 디버거 형식이 Mixed(Managed and Native)로 설정되어 있는지 확인한다.

이렇게 설정하면 기존에 비주얼 스튜디오에서 하듯이 유니티나 앱 코드에 breakpoint를 설정하고, F5 눌러서 실행하여 디버깅하는 것이 가능하다.

유니티 로그 파일

디버깅이 문제 해결에 도움이 안 되면 아래 폴더에 있는 로그 파일을 분석해 볼 수 있다. 버그 리포트할 때도 사용할 수 있다.

<user>\AppData\Local\Packages\<productname>\TempState\UnityPlayer.log

퍼포먼스 분석

이 경우에는 테스트를 위한 별도의 기기가 필요하다. 개발 머신에서는 윈도우 스토어 앱이 실행되는 통안 분석을 할 수가 없다.

  • 윈도우 방화벽을 양 쪽 디바이스에 제대로 설정한다. 유니티 원격 프로파일링에 사용하는 54998~55511이 방화벽의 outbound 규칙에 열려 있는지 확인한다.
  • 스토어 앱 매니페스트 파일의 Capabilities 탭에서 Private Network(Client &Server)와 Internet(Client & Server)를 사용 설정하였는지 확인한다.
  • File  > Build Settings > Windows Store App 에서 Autoconnect Profiler를 체크한다.

image

  • 원격 디버거가 테스트 머신에서 실행 중인지 확인한다.
  • Project > Properties > Debug에서 원격 디버거를 사용하도록 설정한다.
  • 비주얼 스튜디오에서 F5를 눌러서 실행한다.

배포가 되면 유니티의 Window > Profiler에서 앱에서 보내는 퍼포먼스 정보를 볼 수 있다.

image

Advertisements

One comment

답글 남기기

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

WordPress.com 로고

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

Twitter 사진

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

Facebook 사진

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

Google+ photo

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

%s에 연결하는 중