Unityでビジュアル要素 (ヘッドレス) 無しのCLIアプリを作成する
Unityでアプリケーションを作成する際に、コマンドラインでも操作したい事があると思います。
Server Buildのオプションと、Mono.Options を利用することで、Dedicated Server を簡単に作成することができたのでその方法をメモします。
サンプルコードも最後にあります。
何に使えるのか?
ツール系アプリケーションをUnityで開発していると
- 起動時にコマンドラインでオプションを指定したい
- GUI無しでUnityで作成した処理を利用したい。
- マルチプレイ用のネットワークサーバとしてUI無しのヘッドレスモードで起動したい.
- アプリケーション実行中に標準入力を受け取りたい
- UbuntuのCLI上で、ゴニョゴニョしたい。
ということがあり、活用できると思います。
CLIアプリ作成の前提知識
1. Server Build オプション
PC, Mac & Linux Standalone のPlatformには BuildSettings にServer Buildというオプションがあります。
コマンドラインのオプションを全く使わずに、ビジュアル要素 (ヘッドレス) なしのサーバー用のプレイヤーをビルドします。これを有効にすると、UNITY_SERVER 定義を持つマネージスクリプトがビルドされます。これにより、アプリケーションにサーバー固有のコードを書くことができます。また、stdin と stdout にアクセスできるようにコンソールアプリケーションとして Windows バージョンにビルドすることもできます (Unity ログはデフォルトで stdout に保存されます)。
- ServerBuildするとコンソールに以下のようなメッセージが表示されます。
NullGfxDevice: Version: NULL 1.0 [1.0] Renderer: Null Device Vendor: Unity Technologies Begin MonoManager ReloadAssembly - Completed reload, in 0.071 seconds Microsoft Media Foundation video decoding to texture disabled: graphics device is Null, only Direct3D 11 and Direct3D 12 (only on desktop) are supported.
2. -batchmode オプション
- Server Build オプションを有効にしなくても、通常ビルドで
-batchmod
のオプションを付けるとHeadlessで起動することもできます。 - その場合 標準出力を受け取るには合わせて、
-logFile -
のオプションも付与します。
./hoge.exe -batchmode -logFIle -
3. #if UNITY_SERVER ~ #endif
- Server Build を有効にすると UNITY_SERVER のシンボルが有効になります。
- これにより、引数や stdin/stdout の利用など、Server Build 時のみ有効なコードを記述する事ができます。
- ただし、
-batchmode
を利用した場合は、UNITY_SERVER は有効にはなりません。
4. コマンドライン引数の解析
コマンドライン引数を利用するには入力オプションや値を解析する必要があります。
C# における、コマンドライン引数の解析は、いろいろライブラリがあるのですが、Mono.Options が追加ライブラリ不要で、そのままUnity内で利用できたので、それを今回は利用しています。
導入が容易で実装もシンプルなのが、良いですが正常系以外の処理などは充実していないので、高度なことをしたい場合は、他のライブラリも選定しても良いかもしれません。
Mono.Options について
- Mono.Optionsに関してはこちらのサイト を見てもらうのがわかりやすいです。
- UnityのC# でも、
using Mono.Options;
を書くことで使うことができます。 - 以下のサンプルコードにあるように、OptionSetを定義する事で簡単にコマンドラインの解析を行うことができます。
値を取りえるオプションの場合、以下のいずれの方法でも値の指定ができます。
-n value -n=value -name value -name=value --name value --name=value /n value /n=value /name value /name=value
また、スイッチ系のオプションの場合、以下のような指定ができます。
# オンにする -s -s+ # オフにする -s-
実際にコードを書く
Unityのシーンに、以下のコードを空のGameObjectにAttachして、ProjectSettingsでServerBuildをONすることで挙動を確認することができます。
具体的には以下の処理を行うことができます。
起動時にコマンドライン引数を受け取る 例:
xx.exe -v 10 —number=200 -x hoge
起動後の画面で標準入力としてオプションを与えることで、処理を制御することができる。
例 -v HOGE —number=200 -x FUGA
using Mono.Options; using System; using System.Collections.Generic; using System.Threading.Tasks; using UnityEngine; public class StdinReader : MonoBehaviour { private string stringValue = ""; private int number = 0; private bool help = false; private OptionSet options; void Start() { #if !UNITY_SERVER return;//Server Build 以外は無視する #endif //CLIアプリケーションの場合、UIの描画はされないので、 //処理内容によってはフレームレートを抑えることで //CPUの負荷も減らすことができる。 Application.targetFrameRate = 10; options = new OptionSet() { // string 型の引数を取るオプション { "v|value=", "specify a value.", v => stringValue = v }, // string 型以外の引数を取るオプション { "n|number=", "specify a number.", (int v) => number = v }, // 引数を取らないオプション { "h|help", "show help.", v => help = v != null }, // 引数を持たないメソッドをコール { "t|test", "call test method", v => TestMethod()}, // 引数を持つメソッドをコール { "x|xmethod=", "call test method2", v => TestMethod2(v) }, }; string[] args = System.Environment.GetCommandLineArgs(); ParseCommandLineArgs(System.Environment.GetCommandLineArgs()); Task.Run(() => { string line; Console.WriteLine("CLI task start"); while (true) { if ((line = Console.ReadLine()) != null) { args = line.Split(' '); ParseCommandLineArgs(args); } Task.Delay(100); } }); } void ParseCommandLineArgs(string[] args) { List<string> extra; try { extra = options.Parse(args); Console.WriteLine($"value={stringValue}"); Console.WriteLine($"number={number}"); if (help) options.WriteOptionDescriptions(Console.Out); } catch (OptionException e) { Console.WriteLine(e.Message); } Console.WriteLine("GetLine:" + string.Join(" ", args)); } void TestMethod() { Console.WriteLine("call TestMethod"); } void TestMethod2(string _v) { Console.WriteLine("call TestMethod2:" + _v); } }
参考サイト
Unity Editor Preferences の設定をC#から変更する
Unity Cloud Build に限った事では無いのですが、 自動ビルド環境を構築する際、Unity Editorの Edit > Preferences の設定をC# scriptから変更したい場合があると思います。
普段触らないので毎回忘れるのでメモ。
UnityEditor.EditorPrefs で設定できる。
結論としては、UnityEditor.EditorPrefs で設定できます。
ただ、どんな設定項目(key)があるのかの一覧を見つけることができなかったので、 手元の設定を開いてキーと型を見つけ出す必要があります。
上記、ドキュメントにもあるのですが、
- Windowsの場合はレジストリ (Win + R > regedit)
HKCU\Software\Unity Technologies\UnityEditor 5.x
- mac の場合は、
~/Library/Preferences/com.unity3d.UnityEditor5.x.plist
を開いて見つけ出さないといけないようです。
Gradleの設定を切り替える
具体例として、Android ビルド時に利用するgradleの切り替えを行います。
デフォルト状態では、インストール時の設定にもよりますが、Unity組み込みのgradleが利用されます。
これを変更するには、GUI上では Edit > Preferences > External toolsを開いて
- Gradle Installed with Unity recommended のチェックを外す
- Gradleのパスを
browse
を押して任意のパスに変更する
という手順を踏む必要があるのですが、これをスクリプトでやったのが以下のコードとなります。
gradle-5.6.4 が、Application.dataPath/../../gradle-5.6.4
に配置されている想定です。
public static class BuildAndroid { private static void PreBuild() { var defaultGradle = EditorPrefs.GetString("GradlePath", ""); var newGradle = Path.Combine( Application.dataPath, "..", "..", "gradle-5.6.4"); EditorPrefs.SetBool("GradleUseEmbedded", false); EditorPrefs.SetString("GradlePath", newGradle); Debug.Log($"ChangeGradlePath: {defaultGradle} to {newGradle}"); } }
Unity Cloud Buildでも、gitリポジトリ内にgradleライブラリを配置して、
Config > Advanced Options > Pre-Export Method Name に上記 BuildAndroid.PreBuild
を設定してやれば動作する事は確認できました。
(ARCore関連で必要に迫られることがあったのです)
EditorPrefs の設定は全プロジェクト共通なので注意
- EditorPrefs の設定は、Editor側の設定なので、同一バージョンのエディターを利用している場合は、全UnityProjectで共通の設定として扱われます。
- 特定のProjectのみgradleのバージョンを切り替える場合は、ビルド完了後に、gradleのバージョンを元に戻す設定を入れておいた方が良いかもしれません。
参考
Unity Bolt Tips6. BoltでJson.netを使う
Bolt でサーバ上のJsonを取得してParseしたのでその方法のメモ。
Jsonシリアライザの選定
UnityでJsonフォーマットを読み込む場合、標準では UnityEngine.JsonUtility が用意されています。
ただし、JsonUtilityは
など、制約があります。
今回は、上記の制約がない、Json.Net(Newtonsoft.json)を利用します。
Newtonsoft.jsonであれば、C#のクラス定義が不要で、Boltのノードだけで完結することができます。
ただ、デメリットとしては処理速度が遅いようで、大量のデータを読み取る場合や頻繁にデータを更新する場合は JsonUtilityを使ったほうが良いかもしれません。
こちらの比較記事が参考になります。
Newtonsoft.jsonを扱うための準備
1. Newtonsoft.json のインポート
Newtonsoft.json は標準ではUnityには付属していません。そのため外部ライブラリとして取り込んでおく必要があります。
こちらの記事を参考にDLLをインポートします。
また、IL2CPPビルドの際にランタイム上でエラーが出る場合もあるので、link.xmlの調整が必要になる場合もあります。
2. Boltのセットアップ
また、Bolt環境で、Newtonsoft.jsonを利用しようとすると、初期設定のままでは利用できません。(右クリックでノードが出てこない)
Tools > Bolt > Setup Wizard でNewtonsoft.jsonが利用できるように設定を行う必要があります。
Assembly Options | Type Options |
---|---|
追加項目 Newtonsoft.json |
追加項目 JsonCovert Jobject JArray System.DateTime (日付を扱う場合) |
実際にJsonを読み込んでみる
1. ローカルのテキストファイルを読み込む
ここまで準備できたら、テキストファイルとして保存されたJsonファイルを、Boltを利用して読み込んでみます。
Test.json
[ { "key1": "value1", "key2": 0.1, "key3": 1, "datetime": "2021-02-18T02:31:57.000000Z" }, { "key1": "value2", "key2": 0.2, "key3": 2, "datetime": "2021-02-18T02:31:57.000000Z" } ]
Resourcesフォルダに、Test.jsonファイルを配置してそれをstringとして読み込みます。
2. Jsonを読み込む
その出力を JsonCovertでデシリアライズすることで値を取り出すことができます。
- Consoleの出力
3. UnityWebRequestを利用してサーバからJsonを受け取る
UnityWebRequestを利用してサーバ上のJsonを取得する場合は以下のように記述すれば利用可能です。
- Getでリクエストしています。
- わかりやすいように、エラーハンドリング等かなり簡略化しています。
以上。
Unity Cloud Build でsubmodule + symbolic linkを使う
Unity Cloud Build で シンボリックリンクを使うための方法を調べたのでメモ。
マルチデバイス向けのUnity開発プロジェクトや、メインプロジェクト内で、他のリポジトリの特定のフォルダ/ライブラリのみを利用したい場合、submodule と symbolic linkをよく利用します。
ローカル環境ではClone時に symbolic link 生成用のバッチ/shellを手動で実行すれば良いですが、Unity Cloud Build で利用する場合、そこも自動化する必要があります。
1. リポジトリ構成例
[Repository root] ├───Assets ├───Packages ├───ProjectSettings ├───_external │ └───SUB_REPO1 │ ├───Assets │ │ │ Scripts.meta │ │ │ │ │ └───Scripts │ ├───Packages │ └───ProjectSettings └───_tools ProjectGenerate.sh ProjectGenerate.bat
- _external/SUB_REPO1/ に 別のProjectをgit submoduleとして登録している
- _external/SUB_REPO1/Assets/Scripts/フォルダを メインの root/Assets 内でシンボリックリンクで利用したい
- シンボリックリンク作成用のShellは _tools/ProjectGenerate.shとして作成している
2. シンボリックリンク作成用のシェルスクリプトを作成
シンボリックリンク作成用のShellは以下のように作成します。 これは手元のShell環境(Macなど)でもそのまま動作するものです。 普段Windows環境では .bat のバッチファイルで書いています。
# _tools/ProjectGenerate.sh # CWDにshell scriptのカレントディレクトリを代入する CWD=$(cd $(dirname $0); pwd) echo ${CWD} # 対象のサブモジュールディレクトリ SUBMODULE_DIR="${CWD}/../_external/SUB_REPO1/Assets/" # メインのUnity Project MAIN_DIR="${CWD}/../Assets/" # Assets/Scripts/フォルダのシンボリックリンクを作成 ln -sfn ${SUBMODULE_DIR}Scripts ${MAIN_DIR}Scripts ln -sfn ${SUBMODULE_DIR}Scripts.meta ${MAIN_DIR}Scripts.meta
3. Unity Cloud Buildの設定
一般的な設定内容に加えて、Pre-Build Script を設定することで、ビルド開始前にシンボリックリンクを作成します。
- Unity Cloud Build の該当プロジェクトのConfigメニューを開く
- advanced options > Pre-Build Script Path に shellを設定
_tools/ProjectGenerate.sh
これで、おしまいです。
また、この状態の時に、ビルドログに
[Unity] Error (Not a directory) occured whilst enumerating Assets/Scripts.meta
のエラーが表示されますが、ビルド自体は成功(Success)しているので、問題は無さそうです。
Subfolderを指定している場合の注意事項
ただし、Basic Info の Project Subfolder を設定している場合は、そのフォルダを起点としてパスを指定する必要があります。
Subfolder を指定しているにもかかわらず、リポジトリルートからのパスでスクリプトを指定すると
! Pre-Build Script configured, but not found at {Pre-Build Script Path} Skipping.
のメッセージが表示され実行されません。
余談: Windows .bat の場合
シンボリックリンク作成用のShellはWIndowsの場合バッチファイルで作成しますが、上記と同じ内容をWindowsで作成する場合は以下のように書いています。 mklink と ln で シンボリックリンクの書き順が逆なので、よく間違える
- _tools/ProjectGenerate.bat
@echo off rem _tools/ProjectGenerate.bat set CWD=%~dp0 set SUBMODULE_DIR=%~dp0..\_external\SUBMODULE_REPO1\Assets\ set MAIN_DIR=%~dp0..\Assets\ cd %CWD% echo %CWD% mklink /d %MAIN_DIR%Scripts %SUBMODULE_DIR%Scripts mklink %MAIN_DIR%Scripts.meta %SUBMODULE_DIR%Scripts.meta
おしまい。
Azure Static Web AppsとGitHubを使ってSSL+SNS認証付きWebサイトを手軽に構築する
Azure Static Web Apps とは?
Azure Static Web Apps を使用すると、GitHub の特定のbranchと連動して、自動更新されるWeb アプリケーションを簡単に構築できます。
同じような事はGitHub Pagesでもできるのですが、
- master 以外の特定のbranchを指定できる (運用環境とステージング環境とか)
- 認証機能を組み込める(json書くだけ)
- 地理的分散 による高速化
- 他のAzureリソースと組み合わせやすい
- プレビュー版なので無料
というところが、良さそうです。
- Azure Static Web Apps とNetlifyで表示速度を比較してみた · kapieciiのブログ
- フロントエンド初心者でも無料でお手軽に Azure Static Web Apps に Web サイトをデプロイしちゃおう - Qiita
現在、Azure Static Web Apps はパブリック プレビュー段階であり、無料で運用が可能です。
- 1アプリ250MB以内/認証は25人までなど、制限が書かれています。 docs.microsoft.com
個人的には
という使い方ができるんじゃないかなと思っています。
静的ページを公開してみる
公式ドキュメントにチュートリアルがあり、これにそって進めれば、簡単に試すことができます。 アカウントが一通り用意されていれば、15分ぐらいでも試せるのではないでしょうか?
本題、認証付きページを作成する
ここからがこの記事の本題なのですが、 Unityアプリの配布などを行う場合、不特定多数の人には見られたくないケースもあります。 そういった場合に、標準で組み込まれている認証機能が、便利そうなので試してみました。
現時点でドキュメントを見ると
のアカウントを利用したアクセス制限の設定が可能です。
サイト構成
簡単なテストなので以下の構成とします。/index.html は誰でも見れるページ /member/フォルダは承認された人しか見れないページとします。
「静的ページを公開してみる」のチュートリアルにそってHTMLを配置すれば、認証機能以外は簡単に準備できると思います。
/index.html (誰でも見れる) /member/index.html (認証が必要) /routes.json (route設定のjson)
routes.json
が出てきましたが、ここで、認証の設定を行います。
{ "routes": [ { "route": "/login", "serve": "/.auth/login/github" }, { "route": "/.auth/login/facebook", "statusCode": "401" }, { "route": "/member/*", "serve": "/member/index.html", "allowedRoles": [ "reader", "contributor" ] }, { "route": "/", "allowedRoles": [ "anonymous" ] } ], "platformErrorOverrides": [ { "errorType": "Unauthenticated", "statusCode": "302", "serve": "/index.html" } ] }
各項目で以下のような設定を行っています。
- /login はGitHubアカウント認証へのエイリアス(必要ないけど例として)
- facebookアカウントを用いた認証は status 401で利用できないようにしている(必要ないけど例として)
- /member/フォルダへのアクセスは Roleが
reader
とcontributor
のユーザのみがアクセスできるように制限をかけている - /(ルートフォルダ)は誰でもアクセスできる
- 未認証ユーザーは status:302 で /index.htmlへリダイレクトしている
という設定になっています。
また、公開している/index.htmlには、認証ページへのリンクを設置します。
<!DOCTYPE html> <html> <head> <meta charset='utf-8'> <meta http-equiv='X-UA-Compatible' content='IE=edge'> <title>Hello</title> <meta name='viewport' content='width=device-width, initial-scale=1'> <link rel='stylesheet' type='text/css' media='screen' href='main.css'> <script src='main.js'></script> </head> <body> <h1>Hello (public page)</h1> <a href="/member/index.html">ダウンロードページ</a>へのアクセスは認証が必要です。 <li><a href="/.auth/login/google?post_login_redirect_uri=/member/">Googleでログイン</a></li> <li><a href="/.auth/login/aad?post_login_redirect_uri=/member/">Azure Active Directoryでログイン</a></li> <li><a href="/.auth/login/github?post_login_redirect_uri=/member/">GitHubでログイン</a></li> <li><a href="/login?post_login_redirect_uri=/member/">GitHubでログイン(Alias利用)</a></li> <li><a href="/.auth/login/twitter?post_login_redirect_uri=/member/">twitterでログイン</a></li> <li><a href="/.auth/logout">Log out</a></li> </body> </html>
/.auth/login/***
という記述は もともと定義されている認証リンクです。このページにあります- 認証が成功した際には、
post_login_redirect_uri
パラメータを利用して /member/フォルダが表示されるようにしています。
このような形で、jsonの設定だけで認証機能を設けることができます。
実際の承認対象ユーザを追加する。
ユーザの追加は Azure Portal 上で行います。
対象のAzure Static Web Apps (静的Webアプリ) を開いて、
設定 > ロール管理 >招待 からユーザを追加できます。
プレビュー版の制限で最大25ユーザまでらしいですが、少人数への配布のために 手間なく、SSL +認証付きのページを作成できるのは、結構便利です。