Unityカスタムパッケージ作成

最近のUnityが機能を分別してPackageManagerからほしいものだけをインストールする形になりました。自分もこういうパッケージをどうやって作るか検討しました。

## パッケージ用プロジェクトのレイアウト
まずはパッケージになる新しいUnityプロジェクトを作成。プロジェクト内のフォルダーなどを下記のレイアウトと合わせる
参考：Unityマニュアル https://docs.unity3d.com/Manual/cus-layout.html

Unityマニュアル通りにしたら通常Unityプロジェクトではないので作る時は別のプロジェクトにインポートしないといけない。これは使いにくいのかなと思って通常のプロジェクトの中に突っ込みました。

```
<root>
  └── Assets
	├── PackageContents
	│	 ├── package.json  			
	│	 ├── README.md
	│	 ├── CHANGELOG.md
	│	 ├── LICENSE.md
	│	 ├── Editor
	│	 │     ├── [YourPackageName].Editor.asmdef
	│	 │     └── EditorScript.cs
	│	 ├── Runtime
	│	 │     ├── [YourPackageName].asmdef
	│	 │     └── RuntimeScript.cs
	│	 └── Documentation~
	│
	│
	└── Tests
	    ├── Editor
	    │    ├── [YourPackageName].EditorTests.asmdef
	    │    └── EditorTests.cs
	    └── Runtime
	          ├── [YourPackageName].RuntimeTests.asmdef
	          └── RuntimeTests.cs
```

### package.jsonを作成
まずはパッケージの情報をpackage.jsonっていうファイルに書き込もう
参考：https://docs.unity3d.com/Manual/upm-manifestPkg.html

例

```
{
    "name": "com.company.package",
    "displayName": "Package Name",
    "version": "1.0.0",
    "unity": "2019.2",
    "description": "A Test Package",
    "keywords": [
        "package"
    ],
    "category": "Utility"
}
```
もっと設定あるけど基本的にこのくらいあると大丈夫かな？
name = reverse url形式のパッケージ名
displayName = 人間が読みやすい名前
version = パッケージのバージョン(major.minor.patch)
unity = unityの必要バージョン
description = 説明文
keywords = 検索用言葉
category = パッケージグループ名

### C#スクリプトを作成

これはもちろん自由なところです。作りたいパッケージを実装する。例えばAssetBundleの作成とロード周りのシステムとか？
Editor用ならAssets/PackageContents/Editorのフォルダーを利用してゲーム用のスクリプトならAssets/PackageContents/Runtime。

### Assembly Definitionを作成
Assembly Definitionを追加するとパッケージのDLLを作成してくれます。これはメインプロジェクトのコンパイル時間が早くなったり、誰かにDLLだけ渡したり、便利なものです！追加するだけで終わるので入れましょう。
EditorとRuntimeフォルダーに別々に追加してEditorのdefinitionでは参照するdllのところにruntime用dllを付ける

### Testファイルを作成
必要かどうか個人判断だけど、せっかくいい機能があるのでテスト用スクリプト追加してもいいよね。

* Testフォルダーの中にEditor用とランタイム用分かれてる
* テスト用スクリプトなどがまだ作られてない場合、Testフォルダーに右クリック→Create→Testing→Tests Assembly Folderを選択
* 作られたフォルダーをEditorかRuntimeの名前に変更して中にあるassembly definitionの名前を好きに変更
* そのフォルダーにいって右クリック→Create→Testing→C# Test Scriptを選択してスクリプトを追加
* 出来上がったテストスクリプトはだいたいこの感じになります

```
using UnityEngine;
using UnityEngine.TestTools;
using NUnit.Framework;
using System.Collections;

public class EditorTests {

    [Test]
    public void RunTests() {
        Asset.Pass();
    }

    [UnityTest]
    public IEnumerator RunTestsWithEnumerator() {
        yield return null;
    }
}
```

* 確認方法： UnityのメニューからWindow→General→Test Runnerにいって出てくるwindowでテストが正常に動くか

![Screen Shot 2019-12-17 at 12.36.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539248/f29d9f75-3199-efed-0d78-39cb9da4cdc8.png)
* 後でちゃんとしたテストスクリプト作りましょう！


### Verdaccioっていうnpm repositoryにパブリッシュ
これから作るパッケージがどこかに管理したいのでどこかに入れましょう。 UnityのPackage Managerがnpm利用してるので自分が作ったものをPackage Managerに出したいので合わせる必要がある。Unityさんが推奨したのはVerdaccioっていうもの。（多分インストールと準備が楽だから、unityが内部的に違うもの使ってるらしい）
ここからダウンロードとインストール：https://github.com/verdaccio/verdaccio

インストール後command lineでpackage.jsonがあるフォルダーにいって

```
npm publish --registry http://localhost:4873
```
を叩くと登録されます！（もちろん別のマシンにnpmホストしてるならurlをあわせる必要ある）


### 本番プロジェクトに利用
まずは私達作ったrepositoryはどこにあるかUnityに教えないといけない。
Packagesっていうフォルダーの中にあるmanifest.jsonを開いてscopedRegistriesっていうjsonオブジェクトを追加する。
単純にurl、名前、とパッケージの逆urlを追加する

```
{
   "scopedRegistries": [
        {
          "name": "My Packages",
          "url": "http://localhost:4873",
          "scopes": [
            "com.company"
          ]
        }
    ],
    "dependencies": {
        "com.unity.ads": "2.0.8",
        "com.unity.analytics": "2.0.16",
        ..hogehoge..
     }
}
```

後は"dependencies"のところに自分のパブリッシュされたパッケージ名とバージョンを定義するだけ

```
"dependencies": {
    "com.unity.ads": "2.0.8",
    "com.unity.analytics": "2.0.16",
    ..hogehoge..,
    "com.company.package": "1.0.0"
}
```

Unityに戻ったらパッケージがダウンロードされて使えるようになります！


### さらに便利に

まずはコード管理するにはgit repositoryがもちろんやったほうがいいよね。さらにGitLabのCI機能を利用してテストクラスを自動的に走らせてOKが出ればnpmにpublishするような流れるを作る！
・Git repoを作る
・git runnerを作る（やり方がCIのところに書いてる）
・git runner用のスクリプトを追加 (ルートフォルダーに.gitlab-ci.ymlを追加）

自分が作ったスクリプトはこんな感じになりました

```
stages:
  - runTests
  - publish

variables:
  version: "awk '$$1==\"m_EditorVersion:\"{print $$2}' ./ProjectSettings/ProjectVersion.txt"

editor-tests:
  script: 
    - "echo 'Running Editor Tests...'"
    - "unityVersion=$(eval $version)"
    - "unity='/Applications/Unity/Hub/Editor/'$unityVersion'/Unity.app/Contents/MacOS/Unity'"
    - $unity -batchmode -runTests -projectPath . -testResults ./editmodeResults.xml -testPlatform editmode
  stage: runTests
  tags:
    - unity
  only:
    - master

runtime-tests:
  script: 
    - "echo 'Running Runtime Tests...'"
    - "unityVersion=$(eval $version)"
    - "unity='/Applications/Unity/Hub/Editor/'$unityVersion'/Unity.app/Contents/MacOS/Unity'"
    - $unity -batchmode -runTests -projectPath . -testResults ./playmodeResults.xml -testPlatform playmode
  stage: runTests
  tags:
    - unity
  only:
    - master

publish-npm:
  script:
    - "cd ./Assets/PackageContents"
    - "npm publish --registry http://localhost:4873/"
  stage: publish
  tags:
    - unity
  only:
    - master
```
・２つのステージに分ける（テストとパブリッシュ）
・editor用とランタイム用のテストスクリプトを実行する（起動するunityバージョンをProjectVersionsのテキストから引っ張る）
・テストがOKならnpm publishかける
・メインブランチしか動かない（only: master)

### おわりに

パッケージ分けるとちゃんとしたフレームワークができて各プロジェクト使い回すことが本当に便利！後から分別するのも大変かもしれないので早い段階でパッケージ化するのはおすすめ！