You could call the AppCallbacks class a bridge between your main application and the Unity engine. Here, we’ll try to explain what every call to AppCallbacks exactly does. Let’s build solution and explore App.xaml.cpp and MainPage.xaml.cpp files.
App::App()
{
InitializeComponent();
SetupOrientation();
m_AppCallbacks = ref new AppCallbacks();
}
void App::OnLaunched(LaunchActivatedEventArgs^ e)
{
m_SplashScreen = e->SplashScreen;
InitializeUnity(e->Arguments);
}
void App::InitializeUnity(String^ args)
{
ApplicationView::GetForCurrentView()->SuppressSystemOverlays = true;
m_AppCallbacks->SetAppArguments(args);
auto rootFrame = safe_cast<Frame^>(Window::Current->Content);
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (rootFrame == nullptr && !m_AppCallbacks->IsInitialized())
{
rootFrame = ref new Frame();
Window::Current->Content = rootFrame;
#if !UNITY_HOLOGRAPHIC
Window::Current->Activate();
#endif
rootFrame->Navigate(TypeName(MainPage::typeid ));
}
Window::Current->Activate();
}
MainPage::MainPage()
{
m_SplashScreenRemovalEventToken.Value = 0;
m_OnResizeRegistrationToken.Value = 0;
InitializeComponent();
NavigationCacheMode = ::NavigationCacheMode::Required;
auto appCallbacks = AppCallbacks::Instance;
bool isWindowsHolographic = false;
#if UNITY_HOLOGRAPHIC
// If application was exported as Holographic check if the device actually supports it,
// otherwise we treat this as a normal XAML application
isWindowsHolographic = AppCallbacks::IsMixedRealitySupported();
#endif
if (isWindowsHolographic)
{
appCallbacks->InitializeViewManager(Window::Current->CoreWindow);
}
else
{
m_SplashScreenRemovalEventToken = appCallbacks->RenderingStarted += ref new RenderingStartedHandler(this, &MainPage::RemoveSplashScreen);
appCallbacks->SetSwapChainPanel(m_DXSwapChainPanel);
appCallbacks->SetCoreWindowEvents(Window::Current->CoreWindow);
appCallbacks->InitializeD3DXAML();
m_SplashScreen = safe_cast<App^>(App::Current)->GetSplashScreen();
auto dispatcher = CoreWindow::GetForCurrentThread()->Dispatcher;
ThreadPool::RunAsync(ref new WorkItemHandler([this, dispatcher](IAsyncAction^)
{
GetSplashBackgroundColor(dispatcher);
}));
OnResize();
m_OnResizeRegistrationToken = Window::Current->SizeChanged += ref new WindowSizeChangedEventHandler([this](Object^, WindowSizeChangedEventArgs^)
{
OnResize();
});
}
}
m_AppCallbacks = ref new AppCallbacks();
Let’s take a closer look at AppCallbacks class. When you create it, Unity creates a new thread called “AppThread”. This is done because there’s a restriction from Microsoft - if your application does not become responsive after 5 seconds you’ll fail to pass WACK (Windows Application Certification). (You can read more here - http://msdn.microsoft.com/en-us/library/windowsphone/develop/hh184840(v=vs.105).aspx) Imagine if your first level is pretty big and takes a significant amount of time to load. Because your application is running on UI thread, the UI will be unresponsive until your level is fully loaded. That’s why Unity always runs your game on different thread.
Read more on the UI thread here - http://msdn.microsoft.com/en-us/library/windows/apps/hh994635.aspx
You can also pass custom command line arguments as string array into the AppCallbacks constructor.
Note: Code located in App.xaml.cpp, MainPage.xaml.c[[ is always running on UI thread, unless called from InvokeOnAppThread function.
appCallbacks->SetSwapChainPanel(m_DXSwapChainPanel);
Эта строка просто передаёт XAML контрол в Unity, который будет использован для вывода рендера DirectX 11.
appCallbacks->SetCoreWindowEvents(Window::Current->CoreWindow);
Устанавливает основное окно для Unity, Unity подписывается на следующие события (их может быть больше, в зависимости от того, когда эта информация обновлена):
appCallbacks->InitializeD3DXAML();
Это основной метод инициализации для Unity, он совершает следующие действия:
На этой стадии, когда Unity закончит загрузку первого уровня, он войдёт в главный цикл.
Invokes a delegate on application thread, which is useful when you want to execute your script function from UI thread.
Invokes a delegate on UI thread, useful when you want to invoke something XAML specific API from your scripts.
Returns true, if you’re currently running in application thread.
Returns true, if you’re currently running in UI thread.
Initialization function for D3D application.
Function used by D3D application, for entering main loop.
Returns true, when first level is fully loaded.
Sets a command line argument for application, must be called before InitializeD3DWindow, InitializeD3DXAML.
Sets application arguments, which can be later accessed from Unity API - UnityEngine.WSA.Application.arguments.
This function is obsolete and does nothing. In previous versions of Unity, this was needed to register native plugins for callbacks such as UnityRenderEvent. All plugins are now registered automatically. This function will be removed in a future update.
Parses command line arguments from a file, arguments must be separated by white spaces.
Pauses Unity if you pass 1, unpauses if you pass 0, useful if you want to temporary freeze your game, for ex., when your game is snapped.
Enables/Disables input.
Returns true, if Unity will process incoming input.
Sets the control to be used for triggering on screen keyboard. This control will simply receive focus, when on screen keyboard is requested in scripts. Should be called with control, that does open keyboard on focus.
Returns control, currently used control for triggering keyboard input. See SetKeyboardTriggerControl.
Sets system cursor. The given curosr is set for both CoreWindow and independent input source (if used).
Sets system cursor to custom. Parameter is cursor resource ID. Cursor is set for both CoreWindow and independent input source (if used).