エディター拡張を Unity Package Manager に対応させる

`Assets/` に実行時に必要のないエディター拡張用のファイルも追加しなければならない、そんな状況も Unity Package Manager に対応することで解決出来るようになりました。嬉しいですね。


※ ローカルパッケージとして読み込んだ状態
<img src="https://dl.dropbox.com/s/aykqpuk6vdl5phu/PoseEditor_UnityPackageManager.PNG" />




と思って色々と調べていたのですが、今時点では遊びや好奇心の範疇を超えなかったので、真面目に運用しようと思ったら Unity 2020 飛んで Unity 2021 あたりかな、という印象でした。

エディター拡張の管理はシンボリックリンクを使った方法が現実的だと思いますが、Unity Package Manager に興味があれば。


### 追記


Google の Unity 向け SDK の Unity Package Manager 対応をふと見つけたり、その他 UPM 関連の諸々を読んだ結果。

↓ ↓ ↓

プロジェクトを跨いで使用する汎用的なツールの更新を日常的に行っていて、どのプロジェクトでも常にそのツールを最新の状態にしておきたい。

その維持管理の手段として Unity Package Manager を使うのはどうか、について。

- 汎用ツールのコピーが散在しているからとりあえず何かしら手を打ちたい、位であれば、ローカルパッケージにまとめるなんて手間をかけずにシンボリックリンクを作った方が良い。

- がっつりやるならローカルパッケージとしてではなく UPM 向けの Scoped Registry を立てるまでやった方が良い。


といった感じに。


ローカルパッケージの読み込みや Git レポジトリをパッケージとして読み込む、なんかの機能は今後ディスコンされそうな気配まである。Scoped Registry 一択。


- [Unity Package Manager の使いどころ](https://qiita.com/sator_imaging/items/87034527f80db23da038)



--


※ Unity Package Manager で推奨（？）されている、`Editor` フォルダーに対する `Runtime`・`Tests` フォルダーという存在は、Assembly Definition 作りが大変になる従来の `Scripts` 内に `Editor` フォルダーを置く、という手法より良い感じ。






<!--
https://qiita.com/sator_imaging/items/469ecc69425b1927b603
-->




### もくじ

- [はじめに](#はじめに)
   - [シンボリックリンクとフォルダージャンクションの違い](#シンボリックリンクとフォルダージャンクションの違い)
- [Unity Package Manager 対応の利点](#unity-package-manager-対応の利点)
   - [UPM に対応させるかどうか](#upm-に対応させるかどうか)
   - [Assembly Definition（Assembly-CSharp.dll）の問題](#assembly-definitionassembly-csharpdllの問題)
   - [その他](#その他)
- [UPM 対応手順](#upm-対応手順)
   - [package.json を作成](#packagejson-を作成)
   - [Assembly Definition（.asmdef）を追加](#assembly-definitionasmdefを追加)
   - [その他の UPM パッケージ用ファイル](#その他の-upm-パッケージ用ファイル)
   - [サンプルシーンの扱い](#サンプルシーンの扱い)
   - [パッケージが見つからないとき](#パッケージが見つからないとき)
- [おわりに](#おわりに)




## はじめに

Unity エディターで汎用的に使用するエディター拡張であっても、通常は `Assets/` 以下の `Editor/` フォルダーにファイルをコピーすることになります。


ゲームに必要なコンポーネントは動作確認済みのバージョンのコピーであって欲しいので問題ないですが、エディター拡張は常に最新の機能を持った物を使いたいですよね。

しかし、エディター拡張関連のファイルもコピーなので、特定のプロジェクトに存在するファイルを更新しても他のプロジェクトには更新が適用されません。


※ 最新のものを使う以前に、結構便利に使ってたアレ、いつのどのプロジェクトにファイルがあるんだっけ？？ ともなりがち。


で、

- [【UnityEditor】プロジェクトをまたいでエディター拡張したい - Qiita](https://qiita.com/r-ngtm/items/493fdcbd62eb94031c6e)
   - エディター拡張を `.dll` 化しなきゃいけないのが…


や、


- [Windows 用のシンボリックリンク・フォルダージャンクション作成シェル拡張](https://github.com/kobake/lnhdr)
   - 今時点では一般的（？）なやり方


を使ってどうにかしていた訳です。



### シンボリックリンクとフォルダージャンクションの違い

Windows のショートカットは　「`.lnk` という拡張子を持ったエクスプローラー向けのファイルフォーマット」　という厄介な存在でしたが、少し前から Windows でも Linux と同じようなシンボリックリンクとフォルダージャンクションという機能が使えるようになっています。

この機能を使ってファイルの実体は一か所、そのファイルを参照するプロジェクトは沢山ある、を実現するわけですが、両者には少しだけ違いがあります。


- **シンボリックリンク**
   - シンボリックリンクは SourceTree 等の Git クライアントではフォルダーとして認識されないので、エディター拡張の共有には便利。※インストール時の設定でシンボリック云々を設定していない場合
   - ただし、ファイルシステムレベルでフォルダーとして認識されるわけではないので、Asset Store Tools でシンボリックリンク以下のファイルをアップしようとするとエラーが出る、等の問題も。


- **フォルダージャンクション**
   - 対してフォルダージャンクションはより低層で機能しているようで、どのソフトウェアからも通常のフォルダーと区別なく認識され、Git クライアントや Asset Store Tools でも運用可能。


※ シンボリックリンクは Windows の機能、フォルダージャンクションはファイルシステム（NTFS）の機能、だったハズ。なのでジャンクションは FAT 等でフォーマットされたディスクでは使えない。


## Unity Package Manager 対応の利点


エディター拡張を Unity Package Manager（以下UPM）に対応させる利点は、

- UPM の UI を使って気軽に追加・削除ができる。

- 関連ファイルを `Assets/` にコピーしたり、シンボリックリンクを作ったりしなくても良くなるので、ゲームそのものには不要なファイルが `Assets/` 以下から一掃できる。

- `Packages/` 以下のファイルはコピーではなく参照なので、色々なプロジェクトに複製が散らばっているような事態を防げる。

といった所。


今時点では UPM の UI から独自パッケージの検索・バージョン管理等は出来ず、配布はかろうじて、という状態。検索が出来ないのが残念。









### UPM に対応させるかどうか




で、今時点で UPM 運用に耐えるのは、以下のような Unity の基本機能にのみ依存した、単純・単機能なエディター拡張ぐらいかと。

- [【Unity】 キャラクターポーズエディター - Sator Imaging - BOOTH](https://sator-imaging.booth.pm/items/1381822)
   <iframe
      class="embed-card embed-webcard"
      scrolling="no"  frameborder="0"
      style="
         display: block;  border: none;
         margin: 10px 0px;
         width: 100%; height: 155px;
         max-width: 500px;
      "
      src="https://hatenablog-parts.com/embed?url=https://sator-imaging.booth.pm/items/1381822">
   </iframe>




Unity 標準ではない機能から呼び出されず、呼び出すこともしない物すね。










### Assembly Definition（Assembly-CSharp.dll）の問題


UPM への対応では Assembly Definition（`.asmdef`）の使用は不可避なので、Unity 標準機能ではない機能に向けて作成したエディター拡張は UPM への対応が難しい。




UPM については元々 VRM 用のエディター拡張を対応させてみよう、と調べてたんですが、


- VRM の `.asmdef` を追加でダウンロードして導入する。
- VRM.Samples がある場合はエラーが出るので、VRM.Samples の `.asmdef` も導入する。


が、パッケージ受け入れ側のプロジェクトで必要に。



VRM 書き出しの為だけのプロジェクトであれば VRM 関連の `.asmdef` を導入するだけでおっけだが、VRM を使ったアプリを作るプロジェクトでも `.asmdef` を導入した場合、結果的にプロジェクトの全ての機能に `.asmdef` を用意する必要が出てくる。


`.asmdef` は全か無かというポリシーなので、ちょっと UPM 試してみよう、では厳しい。


※ 下手に Assembly Definition を作ってない ＆ Tests 関連がなければ、`Assets/` のルートに一つ Assembly Definition 作ればいいだけ。（のハズ）


--


Assembly Definition（`.asmdef`）の何が問題になるかについては、以下の記事が非常に参考になります。

エディター拡張をどうこうする程度なら熟読する必要は無いけども、ざっと目を通しておいて損は無いです。


- [【Unity】Assembly Definition Filesという神機能](http://tsubakit1.hateblo.jp/entry/2018/01/18/212834)


- [Unity Assembly Definition 完全に理解した - Qiita](https://qiita.com/toRisouP/items/d206af3029c7d80326ed)


- [【Unity】 Assembly Definition Files を使った上でinternalにアクセスする方法 - Qiita](https://qiita.com/toRisouP/items/156eb91dfe7e4a848dc3)




#### .asmdef 対応の副産物


Assembly Definition を使う副産物で `System.Reflection` に頼らずに Unity のプライベートな機能にアクセスできるようにしたりも可能。



参考：

- [【Unity, C#】internalな型やメンバにアクセスするには、多分これが一番早いと思います - Qiita](https://qiita.com/mob-sakai/items/f3bbc0c45abc31ea7ac0)


- [【Unity, C#】続・privateな型やメンバにアクセスするには、多分これが一番早いと思います - Qiita](https://qiita.com/mob-sakai/items/a24780d68a6133be338f)



`InternalsVisibleTo` になっている `.dll` 名一覧：

- [Unity-Technologies/UnityCsReference](https://github.com/Unity-Technologies/UnityCsReference/blob/master/Editor/Mono/AssemblyInfo/AssemblyInfo.cs)




※ <a href="https://assetstore.unity.com/packages/tools/animation/scene-timeline-110178?aid=1011l89HG">Scene Timeline</a> が Reflection でどうにかするエディター拡張だから、この情報は福音だった。（`.dll` 名が微妙になるけども）



### その他


その他にも問題（？）があって、

- UPM の UI が独自パッケージの更新や再ダウンロード、バージョン切り替えには対応していない。検索も出来ない。

- パッケージをダウンロードする機能はあれど、`Packages/manifest.json` に URL とダウンロード済みか否かのステータス（正確には lock）も一緒に保存されている状態なので、バージョン管理ソフト経由で `manifest.json` を共有した場合、大丈夫？？


等々。

そもそも、UPM が流行っていない現状で　「（実は）UPM に対応した独自のパッケージが必要なんすよ」　というのはトラブルの匂いしかしないので、ビルドに必要なファイルは従来通り、`Assets/` 以下で管理を行い、その存在が自明になっている方が安心ですね。







## UPM 対応手順


UPM 対応パッケージの作成方法は非常に簡単。


fuqunaga さんがマニュアル未公開の部分も含め、非常に詳しく紹介してくれています。


- [Package Managerに自作パッケージを追加する2019年版｜fuqunaga｜note](https://note.com/fuqunaga/n/n683474b8a663)

- [【Unity】Package Managerに自作パッケージを追加する｜fuqunaga｜note](https://note.com/fuqunaga/n/ne331cb5600fe)




--


最低限の対応であれば、`package.json` と `.asmdef` のふたつのファイルをフォルダーに配置してやるだけ。




### package.json を作成


まずはパッケージのルートフォルダーに `package.json` という名前のテキストファイルを作成。


最低限記載する必要がある情報は以下の通り。

```json
{
    "name": "com.sator-imaging.pose-editor",
    "displayName": "Pose Editor",
    "version": "1.0.0",
    "unity": "2017.1",
    "description": "Editor extension to pose a character.\n\nCopyright (c) Sator Imaging, all rights reserved."
}
```

※ ライセンス情報などを記載するべきファイルは UPM に仕様として存在するが、読んでほしい重要な情報は `"description"` に記載した方が良さそう。（ライセンス情報を記載したファイルが有ろうと無かろうと、docs.unity3d.com にリンクされ、当然ページは見つからない @ Unity 2018 LTS）
※ パッケージの管理名（`"name"`）は全て小文字（マニュアルにもちらっと記載アリ）そのくらい大丈夫だろうという感じだけど、vs code で注意が出ているところは直しておかないと Unity でエラーが出る。
※ 対応する Unity のバージョン（`"unity"`）は年号でないと受け付けないので、2017.1 が最も若いバージョン。
※ パッケージ自体のバージョン（`"version"`）はセマンティックバージョニングでないとエラーで弾かれる。


参考：

- [Unity - Manual: Package manifest](https://docs.unity3d.com/Manual/upm-manifestPkg.html)

- [セマンティック バージョニング 2.0.0](https://semver.org/lang/ja/)


--

`package.json` さえあれば、UPM からパッケージとして読み込みが可能になる。

もう一つの必要なファイル、`.asmdef` はパッケージとして Unity に読み込んだ後に Unity 上で作成していく。


作成したローカルパッケージの読み込み方法は、こちら。


- [Unity - Manual: Installing a local package](https://docs.unity3d.com/Manual/upm-ui-local.html)
   <iframe
      class="embed-card embed-webcard"
      scrolling="no"  frameborder="0"
      style="
         display: block;  border: none;
         margin: 10px 0px;
         width: 100%; height: 155px;
         max-width: 500px;
      "
      src="https://hatenablog-parts.com/embed?url=https://docs.unity3d.com/Manual/upm-ui-local.html">
   </iframe>




### Assembly Definition（.asmdef）を追加


`.asmdef` は Unity エディターの Project ビューから、`Create > Assembly Definition` で作成する事が可能。

UPM の公式のマニュアルによれば、Runtime と Editor でアセンブリは分けるような事が書いてあるが、特段分けなければいけないという訳ではない。もの凄い大作のエディター拡張でもなければ、ルートに一つ置いておけば良いと思う。


また、エディター拡張を UPM パッケージにする場合に限れば、Platforms を Editor のみに設定するよりは、`Define Constraints` に `UNITY_EDITOR` を設定しておく方が良い。


これはエディター拡張といいつつ、マニピュレーターを描画する等の関係で `MonoBehaviour` を継承したものが含まれることもある為。


※ `Define Constraints` は `Required Preprocessor Symbols` とかの方が分かり易くて良いのでは…？ 拘束を定義するとは…。
<img src="https://dl.dropbox.com/s/wraa5zo41mgksp1/UPM_EditorExtensionAssembly.png" />


--

UPM パッケージとしては、ここまでやれば最低限の仕様を満たしたものに。

後は UPM パッケージのフォルダー内に各種ファイルを追加すれば、Unity のプロジェクトビューの Packages 以下に追加したファイルが読み込まれ、使用可能な状態になる。


※ Assembly Definition でエラーが出た場合の対処方法は、@namazuchin さんの記事が非常に参考になります。

- [UniVRMのAssembly Definition設定方法 | 有有新聞](https://blog.ariari.biz/2019/01/27/univrm-assembly-definition/)




### その他の UPM パッケージ用ファイル



UPM には `package.json` と `.asmdef` 以外にも、パッケージに含めるべきファイルが以下のマニュアルに記載されているが、

- README.md
- CHANGELOG.md
- LICENSE.md
- Documentation~　※ 末尾にチルダがあると Unity にインポートされない


等は追加したとしても UPM の UI は期待した通りには動かないので、UPM が独自パッケージの管理に完全に対応するまで、読んでほしい情報は `package.json` の `"description"` に羅列しておくのが良さそう。



その他、UPM パッケージについての詳細は以下の公式マニュアルで確認できる。

- [Unity - Manual: Package layout](https://docs.unity3d.com/Manual/cus-layout.html)


抜粋： UPM パッケージのファイル構成

```
<root>
  ├── package.json
  ├── README.md
  ├── CHANGELOG.md
  ├── LICENSE.md
  ├── Editor
  │   ├── Unity.[YourPackageName].Editor.asmdef
  │   └── EditorExample.cs
  ├── Runtime
  │   ├── Unity.[YourPackageName].asmdef
  │   └── RuntimeExample.cs
  ├── Tests
  │   ├── Editor
  │   │   ├── Unity.[YourPackageName].Editor.Tests.asmdef
  │   │   └── EditorExampleTest.cs
  │   └── Runtime
  │        ├── Unity.[YourPackageName].Tests.asmdef
  │        └── RuntimeExampleTest.cs
  └── Documentation~
       └── [YourPackageName].md
```





### サンプルシーンの扱い

UPM のパッケージにはどんなファイルでも追加する事が出来るが、`.unity` ファイルは `Packages/` 以下から直接開く事が出来ない。

ただ、参照は可能なので、マルチシーンの子供としては追加が可能。アセットストアに UPM 対応として出品するときには、その旨記載しておくと良さそう。


※ Unity 2019 だと直接 `.unity` ファイルを開けてしまうが、それはそれで問題がある気も…？



--


追記： @shiena さんにサンプルシーンをパッケージに追加する方法を教えて頂きました。


https://qiita.com/sator_imaging/items/469ecc69425b1927b603#comment-aa3078ffd20c89043547

> サンプルシーンについて以下の手順が公式の方法のようです。
>
> 1. ドキュメントと同様に~で終わるフォルダにサンプルシーンやアセット類を入れる
> 2. package.jsonでサンプルシーンのパスを指定する
>
> 例: https://github.com/Unity-Technologies/Graphics/blob/master/com.unity.shadergraph/package.json#L12-L18
>
> こうするとPackage Managerにボタンが表示されてクリックすると指定したフォルダが/Assets/にコピーされます。
URP / HDRP / ShaderGraph / Visual Effect Graph / ProBuilderなどに同様のサンプルボタンがあります。
注意点としてサンプルフォルダはインポートではなくコピーなので隠しフォルダにしておかないとファイルが重複するのでスクリプトエラーになったりアセットのGUIDが更新されてしまいます。




### パッケージが見つからないとき

`package.json` を移動するなどして見つからなくなった場合、以下のエラーが出る。

※ 「Continue」を押せば、とりあえず起動は出来る。
<img src="https://dl.dropbox.com/s/hnrloknygd8enib/UPM_LoadingError.png" />

Windows なら UPM の UI から見つからないパッケージを消せばおっけだが、macOS だと UPM の UI が空っぽになるので、手動で `Packages/manifest.json` を編集する必要がある。


※ macOS だと UPM が空っぽに。
<img src="https://dl.dropbox.com/s/trtd2rbzwmfqwak/UPM_LoadingErrorNoPackagesVisible.png" />


macOS は結構不遇な扱い受けてるんですね… ライセンス認証しても UI 明るいままだし。



## おわりに



UPM 対応に必須な Assembly Definition はあまり流行っていないようなので、UPM の前にまずはそこから、という感じ。


今時点で UPM の為に使用できるオープンなレジストリもあるようだけれど、Unity 的にアセットストアの前にそれらに対応するのか？ というのもあると思うので、ごく一部の人が UPM でなんかやってる… という状態がしばらくは続きそうな感じすね。


自前でレジストリを建てる方法もあるみたいですが、プロジェクト全体を巻き込むほどの利便性があるのか＆メンテナンスのコスト、社外に協力会社がいたら？ 等々を考えると…


※ 将来的に公式のレジストリが用意されたとしても、プライベート公開が出来ず全世界公開オンリーだとしたら使いどころも限定的に… 難しいですね。







--


以上です。お疲れさまでした。