Unity UWP ことはじめ

Unity は UWP (ユニバーサル Windows プラットフォーム) にも対応しています。
UWP は Windows 用のアプリ作成方式の一つで Microsoft Store で配布されてるタイプ、と言うと分かりやすいかと思います。

今回試しに Unity で UWP ビルドしてみたのですが、他のプラットフォームに比べるとネット上の情報もあまりなく、躓きやすいと感じましたので備忘録を兼ねて記事にまとめておこうと思います。

環境

Windows 10 Home (22H2)
Unity 2022.3.22f1
Visual Studio Community 2022 (17.9.4)

事前準備

前提として開発機は Windows である必要があります。
Unity に Universal Windows Platform Build Support のモジュールを追加します。
また、Visual Studio にユニバーサル Windows プラットフォーム開発のモジュールを追加します。

サンプルプロジェクト作成

適当に Unity プロジェクトを作っていきましょう。
今回は画像ファイルを読み込んで表示するだけのアプリにします。
空のプロジェクトができたら早速「File > Build Settings…」を開き、Universal Windows Platform を選択して、Switch Platform ボタンを押して切り替えます。

Player 設定も見ておきましょう。Scripting Backend は IL2CPP 固定、Api Compatibility Level は .NET Standard 2.1 から変更せずに進めます。

シーンに Button と RawImage を置いたら、ここからはスクリプトを書いていこうと思います。
さて、気になるのが Windows ネイティブのファイルダイアログをどうやって呼び出すのか、ですね。
他のプラットフォームであればネイティブプラグインを追加して C# からマーシャリングしますよね?UWP もそのノリかなと思ってしまうのですが、そうではなく、WinRT API サポートがデフォルトで組み込まれていてそのまま使うことができます。
参考: https://docs.unity3d.com/Manual/windowsstore-scripts.html

ということで、UWP 公式ドキュメントにある例を参考にコードを書いていきましょう。

参照解決できないやん!組み込まれているんじゃないんかい!

実はもう一つすべきことがあります。「Edit > Preferences…」を開き、External Tools の Player projects にチェックを入れた状態で Regenerate project files を押して Visual Studio のプロジェクトファイルを再生成。

すると *.Player のプロジェクトが出現するので、切り替えると参照が通るようになります。

色々調べながら、最終的には以下のようなコードを書きました。
ポイントは 3 つ。
・WinRT API を使う部分は「ENABLE_WINMD_SUPPORT」ディレクティブで囲う
・UI スレッドで呼び出さなければならないようなので、UnityEngine.WSA.Application.InvokeOnUIThread を通す
・結果を Unity API で使う場合はメインスレッドで行う

using System;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;

public class UwpSample : MonoBehaviour
{
    [SerializeField]
    private Button _button = null;

    [SerializeField]
    private RawImage _image = null;

    private void Awake()
    {
        _button.onClick.AddListener(async () =>
        {
            // WinRT API を使う部分は ENABLE_WINMD_SUPPORT で囲う
#if ENABLE_WINMD_SUPPORT
            bool isFinished = false;
            byte[] bytes = null;
            // UWP アプリの UI スレッドで実行する
            UnityEngine.WSA.Application.InvokeOnUIThread(async () =>
            {
                var picker = new Windows.Storage.Pickers.FileOpenPicker();
                picker.ViewMode = Windows.Storage.Pickers.PickerViewMode.Thumbnail;
                picker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary;
                picker.FileTypeFilter.Add(".jpg");
                picker.FileTypeFilter.Add(".jpeg");
                picker.FileTypeFilter.Add(".png");

                Windows.Storage.StorageFile file = await picker.PickSingleFileAsync();
                if (file != null)
                {
                    var buffer = await Windows.Storage.FileIO.ReadBufferAsync(file);
                    bytes = new byte[buffer.Length];
                    buffer.CopyTo(bytes);
                }
                else
                {
                    Debug.Log("Canceled.");
                }

                isFinished = true;
            }, true);

            // スレッドが終わるまで待つ
            while (!isFinished)
            {
                await Task.Yield();
            }

            // 反映するのはメインスレッドで行う
            if (bytes != null)
            {
                Texture2D texture = new Texture2D(1, 1);
                texture.LoadImage(bytes);

                _image.texture = texture;
            }
#endif
        });
    }
}

Unity でビルド出力

Build Settings を開いて、シーンを追加し、Build ボタンを押してビルドを作成します。

ビルドが成功すれば出力フォルダにファイル群ができます。この段階は exe や apk のような実行ファイルやインストーラーファイルそのものではなく、それを作るための Visual Studio プロジェクトになります。

Visual Studio でパッケージ作成

ソリューションファイル (*.sln) をダブルクリックして開きます。
まずは構成を任意のものに切り替えます。例えば development build したい場合は Debug、リリースに使う場合は Master または MasterWithLTCG といった具合です。
詳しい説明は公式ドキュメントを参考にしてください。
https://docs.unity3d.com/Manual/windowsstore-generatedproject-il2cpp.html

ソリューションエクスプローラーの「プロジェクト名 (Universal Windows)」を右クリックして「公開(P) > アプリ パッケージの作成(P)…」を選択します。

ひとまず、出てくるウィザードはすべて「次へ(N)」と「作成(R)」で構いません。
一点補足しておきたいこととして、途中で証明書を求められていますが、これは Unity が作ったテスト用のいわゆるオレオレ証明書です。
(プロジェクトにしれっと「WSATestCertificate.pfx」というファイルが追加されていると思いますが、それです。)
ここでは詳しく書きませんが Microsoft Store に出すかサイドローディングかに関わらず、アプリを他の人に配布してインストールできるようにするにはちゃんとした証明書が必要になります。

ビルドが終わると指定した出力先にインストーラーファイル群が出力されます。

インストーラーファイルは *.appx という拡張子です。お使いの環境に合ったフォルダ (画像例では *_x64_*) 内にあるものをダブルクリックして実行してみましょう。

証明書がオレオレなので検証できず怒られます。

これを通せるようにするため、Windows PowerShell を起動して Install.ps1 を実行しましょう。

PS C:\Users\xxx> cd [Install.ps1があるフォルダのパス]
PS [Install.ps1があるフォルダのパス]> .\Install.ps1

そうすると管理者権限で PowerShell が立ち上がるので、指示に従い進めます。
途中、開発者モードにすることを求められるので、テスト中は開発者モードをオンにしておきましょう。

最終的に「エラー: 開発者ライセンスを取得できませんでした。」と出るかもしれませんが、これは当環境がお試しで開発者ライセンスを持っていないためなので、とりあえず無視。

怒られなくなりました。インストールが終わったら実行 (準備ができたら起動にチェックが入っていたら自動で起動する) して動作確認してみましょう。
手動で実行する場合はスタートメニューから見つけることができます。

よさそうですね!(何も考えずフルスクリーンだったのでちょっとサイズがアレですが)

補足

エディタ上では UWP の表記が多いですが、スクリプトでは WSA が使われるようなので注意です。
例えば名前空間の UnityEngine.WSA や BuildTarget.WSAPlayer など。
WSA とは Windows Store Apps の略みたいですね。

おわりに

なかなかすんなりとはいきませんでしたが、少しコツが掴めたと思います。
UWP での開発はほとんど意識したこともなく無知でしたが、実際にやってみるとしっかりサポートされていることを改めて感じました。

参考

https://forum.unity.com/threads/migrating-from-windows-standalone-to-uwp.1045375/
https://forum.unity.com/threads/invokeonuithread.381673/

コメント

タイトルとURLをコピーしました