.NETで独自のUnityランチャーを作る

#はじめに
なんとなく.NETの勉強をしていたのですが、ふとUnityがCommandLineArgumentsを読めるのならUnity製アプリケーションデフォルトのランチャー(Display Resolution Dialog)を使わなくても外部からUnityをCommandLineArgumentsを代入しながら起動すれば起動時設定をUnity側で受け取れる実質的なランチャーを作れるってことなんじゃないかと思い立ったので、その実装をしてみました。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">.NETで値を渡しつつUnityを起動する自前ランチャ―を作りました。 <a href="https://t.co/I0NzPEQPBa">pic.twitter.com/I0NzPEQPBa</a></p>&mdash; 避雷 (@lucknknock) <a href="https://twitter.com/lucknknock/status/1177584320057208832?ref_src=twsrc%5Etfw">September 27, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
#使う技術
##.NET
ランチャー部分を担当。C#だったしxamlで雑にUI組めるので採用しただけでGUI生成出来るなら別にpythonとかでも良さそう
##Unity
ランチされる側担当。
##System.Environment.GetCommandLineArgs(C#)
起動時のコマンドライン変数をstringの配列で返す。例えば`Unity.exe -hoge1 hoge2 -hunyaA hunyaB`で起動すると
`{"-hoge1", "hoge2", "-hunyaA", "hunyaB"}`って返ってくる。

##System.Diagnostics.Process.Start(C#)
.NET側、パスを渡してアプリケーションを起動することができる。第一引数がpath、第二引数でCommandLineArgsをstringで指定する。
#サンプル例の実装
今回はサンプルとして簡単に「ランチャーに入力した文字をUnityアプリ上で表示するランチャー」を作ってみようと思います。
##.NET側の実装
まず、通常のNew SolutionからDesktop Applicationを作ります。今回は最小構成でMainWindow.xamlとMainWindow.xaml.csを書き換えていきます。

```xml:MainWindow.xaml
<Window x:Class="LauncherApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:LauncherApp"
        mc:Ignorable="d"
        Title="UnityLauncher" Height="350" Width="525">
    <Grid>
        <TextBox Name="InitialText" Height="100" Width="400">  
            Initial text contents of the TextBox.  
        </TextBox>  
        <Button Click="Launch" Height="100" Width="200" HorizontalAlignment="Right" VerticalAlignment="Bottom">Launch</Button>
    </Grid>
</Window>

```

```c#:MainWindow.xaml.cs
using System.Windows;

namespace LauncherApp
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        public void Launch(object sender, RoutedEventArgs routedEventArgs)
        {
            System.Diagnostics.Process p =
                System.Diagnostics.Process.Start(@"Unity.exeがあるパス","-text " + InitialText.Text);
        }
    }
}
```
こんな感じになります。TextBoxに入れられた文章をコマンドの引数としてUnityを起動します。
TextBoxに入れる文字列に半角スペースが入ると最初のスペースまでの文字列しか対応するArgumentとして解釈されないので注意しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256231/5c0ba010-7b7a-8c98-f832-d8334059a9a5.png)

##Unity側の実装
起動時のCommandLineArgumentsを取得して、それをText.textに反映します。以下のC#を生成してシーン上のゲームオブジェクトにアタッチします。

```C#:CommandLineArgsReader.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class CommandLineArgsReader : MonoBehaviour
{
    [SerializeField]
    Text text;
    // Start is called before the first frame update
    void Start()
    {
        string[] args = System.Environment.GetCommandLineArgs();
        for (int i = 0; i < args.Length; ++i)
        {
            switch (args[i])
            {
                case "-text":
                    if (i + 1 < args.Length)
                    {
                        text.text = args[i + 1];
                    }
                    break;
            }
        }
    }
}
```
CommandLineArgumentの中から "-text"を取得して次の文章をText.textに反映しています。
CanvasとTextを生成してインスペクタのTextの部分に代入しましょう。

結果としては以下のようになります。![e08e1c0e5c56fe713d6d0af7197ee845.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256231/89b28161-0c76-eea7-824b-1b5f5dfc490b.gif)

Unity側で起動時に特定のコマンドがなかった時にアプリケーションを終了する処理を加えるとランチャー以外からの起動を阻止できそうです。

また、うまくUnityアプリを起動できないときはパスが正しいかどうかを確認しましょう。

#最後に
これを使えばUnity公式のランチャーに頼らずにゲームの個性を活かしたランチャーを作ることができそうです。トリプルAタイトルなんかはランチャーも凝ってるイメージがあるので、これでランチャーをイケてるモノにすればゲームもちょっと高級に見える…かも。