銀の光と碧い空

クラウドなインフラとC#なアプリ開発の狭間にいるエンジニアの日々

Unity で WinRT のAPIを使うプラグインを作る

Unity Advent Calendar 2013 - Qiita [キータ] の8日目のエントリです。

UnityがWindows Store App (ランタイムとしてはWinRT)をサポートして、ストアアプリ向けのゲームもUnityで開発できるようになりました。Unityはマルチプラットフォーム対応ですが、プラットフォーム固有の機能を実行することも可能です。今回はWinRTのAPIを使うプラグインを作って、Unityから使う方法を試してみました。

WinRT向けプラグインの作り方は、ほぼほぼMicrosoftの大西さんの資料にあるので、それに倣って作っていけばOKです。

Akira Onishi's weblog Unity on Windows 8.1

今回作ったのはトースト通知をUnityから実行するサンプルになります。

ストアアプリ用クラスライブラリの作成

まずは、WinRTのAPIを呼び出すクラスライブラリを作成します。Visual Studioでプロジェクトテンプレートで 「Windowsストア > クラスライブラリ」を選択して作成します。

f:id:tanaka733:20131208014453p:plain

そして必要なクラスを実装します。今回トースト通知を行いますが、別のライブラリを使って簡単に書けるようにします。Nugetを使って「NotificationsExtensions」というライブラリを追加しました。その上で実装します。

Using Windows. UI. Notifications;

namespace UnityWinRTPlugin
{
    public static class ToastNotificationUtil
    {
        public static void Notify(string text)
        {
            var toastContent = NotificationsExtensions.ToastContent.ToastContentFactory.CreateToastImageAndText01();
            toastContent.TextBodyWrap.Text = text;

            toastContent.Image.Src = "https://pbs.twimg.com/profile_images/378800000175980394/244072044d40def7cc6ad6ca6a73e817.png";
            toastContent.Image.Alt = "profile icon";

            toastContent.Duration = NotificationsExtensions.ToastContent.ToastDuration.Long;
            toastContent.Audio.Content = NotificationsExtensions.ToastContent.ToastAudioContent.Reminder;

            var toast = toastContent.CreateNotification();
            ToastNotificationManager.CreateToastNotifier().Show(toast);
        }
    }
}

Unity Editor部分のクラスライブラリの作成

次に、UnityEditorから作ったライブラリを呼ぶためのクラスライブラリを作成します。アセンブリ名や名前空間含めたクラス名は先ほど作成したものを同じでなければいけませんが、.NET 3.5相当のコンパイラレベルでかつデスクトップAPIでなければ利用できません。つまり先ほどのWinRT用APIを呼び出してるコードはUnity Editorから利用することができません。(リンク先の大西さんの資料30ページ参照)

そこで、外面だけ同じクラスライブラリを作成します。先ほど作成したプロジェクトが含まれるソリューションに新しく「Visual C# > クラスライブラリ」のプロジェクトを追加します。

f:id:tanaka733:20131208015506p:plain

その後、プロジェクトのプロパティを開き、アセンブリ名と既定の名前空間を、先につくっておいたWinRT向けのプロジェクトと同じにします。そして、ターゲットフレームワークを 「.NET Framework 2.0」にします(ここまで下げなくてもよさそうですが、特に新しいAPIを使うわけでもないので一番低くしました)。

f:id:tanaka733:20131208015602p:plain

その後、すでに作られている Class1.cs というファイルを削除し、

f:id:tanaka733:20131208015744p:plain

WinRT向けのプロジェクトで作ってあるコードを、リンクとして追加という機能で追加します。プロジェクトを右クリックし、既存の項目の追加から、

f:id:tanaka733:20131208015901p:plain

WinRT向けのcsファイルを選択し、リンクとして追加を選びます。

f:id:tanaka733:20131208020010p:plain

これで追加できました。ただ、WinRT向けのAPIを.NET Framewrok 2.0ではビルドできないはずです。そこで、条件付きコンパイルという方法で、WinRTに固有の部分を.NET Framework 2.0 では使わないようにします。WinRT固有の部分を #if NETFX_CORE ~ #endif で囲います。今回、usingで参照している名前空間もWinRTのものであれば囲います。

#if NETFX_CORE
using Windows.UI.Notifications;
#endif

namespace UnityWinRTPlugin
{
    public static class ToastNotificationUtil
    {
        public static void Notify(string text)
        {
#if NETFX_CORE
            var toastContent = NotificationsExtensions.ToastContent.ToastContentFactory.CreateToastImageAndText01();
            toastContent.TextBodyWrap.Text = text;

            toastContent.Image.Src = "https://pbs.twimg.com/profile_images/378800000175980394/244072044d40def7cc6ad6ca6a73e817.png";
            toastContent.Image.Alt = "profile icon";

            toastContent.Duration = NotificationsExtensions.ToastContent.ToastDuration.Long;
            toastContent.Audio.Content = NotificationsExtensions.ToastContent.ToastAudioContent.Reminder;

            var toast = toastContent.CreateNotification();
            ToastNotificationManager.CreateToastNotifier().Show(toast);
#endif
        }
    }
}

これでOKです。Releaseビルドして、各プロジェクトのbin/Release以下にdllが作成されてていることを確認しましょう。

Unityプロジェクトでの操作

あとはUnity側での操作になります。UnityプロジェクトのAsset以下に Pluginsというフォルダと、Pluginsの下にさらに Metro というフォルダを作ります。

f:id:tanaka733:20131208020740p:plain

そして、WinRT向けのプロジェクトで作られたdll(Nugetで追加したdll含む)を Plugins/Metro以下に、.NET Framework 2.0でビルドした(Unity Editor向けの)dllをPlugins直下にコピーします。

  • WinRT向け

f:id:tanaka733:20131208020630p:plain

  • Unity Editor 向け

f:id:tanaka733:20131208020647p:plain

あとは、UnityのC# スクリプトでこのdllを使ったコードを記述します。(下は一例)

using UnityEngine;
using System.Collections;
using UnityWinRTPlugin;

public class ToastNotification : MonoBehaviour {

    // Use this for initialization
    void Start () {
        ToastNotificationUtil.Notify("Unityからトースト通知");
    }
    
    // Update is called once per frame
    void Update () {

    }
}

そして、UnityからWindows Store向けにビルドして、ストアアプリのソリューションを作成します。(このとき、PlayerSettingsで下でやっている作業の一部をUnity側でできますが、全てできる訳ではないので省略)

ストアアプリ側での作業

これで完成...といいたいところですが、このままだとトースト通知ができません。ストアアプリでトースト通知するには、そのアプリがどのような機能を使っているか、というマニフェストファイルを編集する必要があります。

作成されたストアアプリのソリューションで、アプリケーション マニフェストの編集からマニフェストを開き、アプリケーションタブのトースト対応を「はい」に設定します。

f:id:tanaka733:20131208021357p:plain

さらに、上のサンプルコードでは、トースト通知につかうアイコン画像をインターネット上から取得しています。インターネット通信する場合もマニフェストの編集が必要なので、機能タブの機能一覧で「インターネット(クライアント)」にチェックを入れます。

f:id:tanaka733:20131208021523p:plain

これでトースト通知できます。アプリを実行すると、UnityのC#スクリプトで定義したタイミングでトースト通知されることがわかります。

f:id:tanaka733:20131208021621p:plain

今回の例では簡単でしたが、この方法を使えば、WinRT固有の機能も手軽にUnityから利用することができることがわかりました。