unity-package-management について
unity-package-management は、Unity プロジェクトの保守担当者向けのスキルガイドです。Pipeline コマンドによる UPM パッケージ管理と、Editor コマンドが使えない場合の C# Client API による代替手順を説明し、検索、バージョン確認、ヘッドレスインストールも扱います。
主な機能
- 01UPMパッケージの追加・削除・更新
- 02パッケージ検索と利用可能なバージョン確認
- 03Pipelineコマンドを優先して利用
- 04Editorがない場合のC#スクリプト代替
- 05ヘッドレスでのインストールと検証
- 06アセットのインポートと.metaファイル生成
活用シーン
- 01既存のUnityプロジェクトに依存関係を設定
- 02CIでパッケージを非対話インストール
- 03導入前にパッケージIDとバージョンを確認
- 04ヘッドレスでアセットをインポートしメタデータを生成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ unity-technologies/ skills --skill unity-package-managementインストール後、Claude Code で/unity-package-managementを入力して読み込まれたか確認します。
同梱ファイルreferences/select-packages.md
詳細
Unity パッケージ管理(まず Pipeline コマンドを使用し、C# スクリプトはフォールバックとして使用)
プロジェクトの Pipeline パッケージにある package コマンドを unity command 経由で実行し、UPM(Unity Package Manager)パッケージを追加、削除、アップグレード、検索します。これらのコマンドに応答できる Editor がない場合に限り、スクリプトからヘッドレスで実行する UnityEditor.PackageManager.Client にフォールバックします(フォールバックを参照)。**Packages/manifest.json を手動で編集しないでください。**Package Manager は依存関係と互換性のあるバージョンを正しく解決しますが、手動編集では解決が頻繁に壊れます。
このガイドは、Editor のインストール、プロジェクトの作成、ビルド/テストを扱う unity-cli スキルを補完するものです。同スキルでは unity command、unity status、unity pipeline install について説明しています。
まず package コマンドを使用する
Pipeline パッケージは add、remove、list、resolve、search、status の package コマンドグループを登録します。これらのコマンドを使うには、プロジェクトに com.unity.pipeline が含まれた状態で Editor が起動している必要があります(unity pipeline install で追加し、次に unity open を実行し、unity status --until-ready で準備ができるまで待ちます)。各コマンドのパラメーターは Editor から取得するため、最初の呼び出しの前に確認してください。パラメーター名を推測しないでください。
unity status --until-ready --project-path "<project-path>"
unity command package --project-path "<project-path>" # the package commands this Editor has
unity command --query package --format json --project-path "<project-path>" # each command's parameter schema
unity command package search --project-path "<project-path>" # then pass the parameters the schema lists
unity command package add --project-path "<project-path>" # add one or more packages by id
unity command package list --project-path "<project-path>" # confirm what is installed
コマンドのパラメーターは、スキーマに記載された名前を使ってコマンド名の後に渡します(unity command <group> <command> --<parameter> <value>)。スキーマが複数の ID を受け付ける場合は、必要なパッケージをすべて 1 回の package add 呼び出しで追加してください。最低バージョンが必要な場合は、スキーマの説明に従ってバージョンを指定します(@<version> 形式の ID は、後述の C# フォールバックで使用します)。package status または package list で確認し、パッケージを削除するには package remove を使用します。終了コードがゼロ以外の場合は失敗とみなし、メッセージを確認してください。
unity command package でそのようなグループがないと報告される場合、プロジェクトの Pipeline パッケージが古いバージョンです。unity pipeline upgrade を実行するか、以下のフォールバックを使用してください。
使用する場面
- 既存または新規作成したプロジェクトで、1 つ以上のパッケージを追加/削除/アップグレードする場合。
- CI でプロジェクトのパッケージを非対話的にセットアップする場合(Editor が起動していないときはフォールバックのスクリプトを使用)。
- パッケージ ID が存在するか確認する場合、または依存する前に利用可能なバージョンを調べる場合。
- ゲームに実際に必要なパッケージを決める場合。references/select-packages.md を参照してください。
インストールするものを選ぶ
すべてではなく、プロジェクトが実際に必要とするものをインストールしてください。選択したテンプレートにすでに含まれているパッケージを優先します(URP テンプレートには、レンダーパイプライン、Input System などがすでに含まれています)。ジャンル/外観/プラットフォーム/収益化に応じたパッケージの対応関係と、レジストリの検索方法については、references/select-packages.md を参照してください。重複のないパッケージ ID のリストを作成し、インストール前にユーザーに読み上げて確認してください。
フォールバック:問い合わせ可能な Editor がない場合(C# Client API)
package add を実行できない場合に限り、このセクションを使用してください。Editor が起動しておらず、起動する予定もない場合、プロジェクトに Pipeline パッケージがない場合、またはコマンドグループがなくアップグレードも希望しない場合が該当します。Editor がバッチモードで実行するスクリプトから Client.AddAndRemove を呼び出してインストールします。
-quitの問題 — インストールにunity runを使ってはいけない理由
Client.Add / Client.AddAndRemoveは非同期です。後続のEditorApplication.updateティックで完了するRequestを返します(UPMの子プロセスは、Editorのメインループポンプで結果をマーシャリングして返すため、ブロッキングするwhile (!req.IsCompleted)のビジーウェイトではデッドロックします)。リクエストが完了するまで、-executeMethodが戻った後もEditorを起動したままにする必要があります。
インストーラーにunity runは使えません。デフォルトの実行パスでは-quitが挿入されるためです(unity-cliスキルの予約済みフラグを参照)。-quitを指定すると、メソッドが戻った直後にEditorが終了します。UPMの解決が完了する前なので、パッケージはインストールされず、コールバックも実行されません。
解決方法: -quitを指定せず、-batchmodeでEditorのバイナリを直接起動します。Editorは起動したままになり、EditorApplication.updateのティックが続き、ポーリングコールバックが実行されます。処理が終わるとコールバックがEditorApplication.Exit(code)を呼び出し、Editorの終了とプロセスの終了コードの設定を行います。
インストーラースクリプト
これをAssets/Editor/ProjectBootstrap/PackageInstaller.csに記述します。UnityEditorを使用するため、Editor/フォルダー内(またはEditor専用アセンブリ内)に配置する必要があります。
using System.Linq;
using UnityEditor;
using UnityEditor.PackageManager;
using UnityEditor.PackageManager.Requests;
using UnityEngine;
namespace ProjectBootstrap
{
// Installs (and optionally removes) a fixed set of packages via the PackageManager
// Client API, headless-safe.
public static class PackageInstaller
{
// EDIT this list to match the package selection (see references/select-packages.md).
static readonly string[] PackagesToAdd =
{
"com.unity.inputsystem",
"com.unity.cinemachine",
"com.unity.render-pipelines.universal",
// "[email protected]" // pin a version with @ when a minimum is required
};
// Optionally drop packages in the same resolution pass (e.g. a template default you don't want).
static readonly string[] PackagesToRemove = { };
const double TimeoutSeconds = 600; // UPM resolution + downloads can be slow
static AddAndRemoveRequest _request;
static double _deadline;
// Invoke with: -executeMethod ProjectBootstrap.PackageInstaller.Install (NO -quit)
public static void Install()
{
if (PackagesToAdd.Length == 0 && PackagesToRemove.Length == 0)
{
Debug.Log("[PackageInstaller] Nothing to do.");
EditorApplication.Exit(0);
return;
}
Debug.Log($"[PackageInstaller] Adding: {string.Join(", ", PackagesToAdd)}");
_request = Client.AddAndRemove(packagesToAdd: PackagesToAdd, packagesToRemove: PackagesToRemove);
_deadline = EditorApplication.timeSinceStartup + TimeoutSeconds;
EditorApplication.update += Poll;
}
static void Poll()
{
if (_request == null) return;
if (!_request.IsCompleted)
{
if (EditorApplication.timeSinceStartup > _deadline)
{
EditorApplication.update -= Poll;
Debug.LogError("[PackageInstaller] Timed out waiting for UPM.");
EditorApplication.Exit(2);
}
return;
}
EditorApplication.update -= Poll;
if (_request.Status == StatusCode.Success)
{
var names = _request.Result.Select(p => $"{p.name}@{p.version}");
Debug.Log($"[PackageInstaller] Resolved: {string.Join(", ", names)}");
EditorApplication.Exit(0);
}
else
{
Debug.LogError($"[PackageInstaller] Failed: {_request.Error?.message}");
EditorApplication.Exit(1);
}
}
}
}
AddAndRemoveは、UPMの解決処理1回でパッケージ一式をインストールします。パッケージごとにClient.Addを呼び出すより速く、エラーも発生しにくくなります。
1つのスクリプトで追加/削除/アップグレードする方法:
- 追加:IDを
PackagesToAddに記載します。 - 削除:IDを
PackagesToRemoveに記載します。 - アップグレード/バージョン固定:
@<version>を付けてIDを追加します(例:[email protected])。バージョンを指定しない場合、解決時に互換性のある最新リリースが選択されます。
パッケージの検索/検証
IDが存在することを確認したり、追加前にバージョン一覧を取得したりするには、レジストリを検索します。Editor内のClient.SearchAll() / Client.Search("<id>")の呼び出しも非同期のため、インストーラーと同じポーリングとExitのパターン、および同じヘッドレス実行方法を使用します。Assets/Editor/ProjectBootstrap/PackageSearch.csを作成します。
using System.Linq;
using UnityEditor;
using UnityEditor.PackageManager;
using UnityEditor.PackageManager.Requests;
using UnityEngine;
namespace ProjectBootstrap
{
public static class PackageSearch
{
const double TimeoutSeconds = 120;
static SearchRequest _request;
static double _deadline;
// Invoke with: -executeMethod ProjectBootstrap.PackageSearch.SearchAll (NO -quit)
public static void SearchAll()
{
_request = Client.SearchAll(); // or Client.Search("com.unity.cinemachine")
_deadline = EditorApplication.timeSinceStartup + TimeoutSeconds;
EditorApplication.update += Poll;
}
static void Poll()
{
if (_request == null) return;
if (!_request.IsCompleted)
{
if (EditorApplication.timeSinceStartup > _deadline)
{
EditorApplication.update -= Poll;
Debug.LogError("[PackageSearch] Timed out.");
EditorApplication.Exit(2);
}
return;
}
EditorApplication.update -= Poll;
if (_request.Status == StatusCode.Success)
{
foreach (var p in _request.Result.OrderBy(p => p.name))
Debug.Log($"[PackageSearch] {p.name}@{p.versions.latestCompatible} {p.displayName}");
Debug.Log($"[PackageSearch] {_request.Result.Length} packages found.");
EditorApplication.Exit(0);
}
else
{
Debug.LogError($"[PackageSearch] Failed: {_request.Error?.message}");
EditorApplication.Exit(1);
}
}
}
}
_request.ResultはPackageInfo[]です。各エントリーにはname、displayName、description、versions(.latest、.latestCompatible、.all)が公開されています。Editorを使わずにターミナルだけで確認する場合(自由入力の検索ではなく、既知のIDが対象)は、レジストリに直接問い合わせてください。references/select-packages.mdを参照してください。
ヘッドレス実行(-quitなしでEditorを直接起動)
バージョンからEditorのバイナリを特定し、バッチモードで実行します。終了処理はスクリプトがEditorApplication.Exitで行うため、-quitは指定しないでください。
VERSION="<version>" # e.g. 6000.0.47f1 (or an installed version)
PROJECT="<project-path>"
METHOD="ProjectBootstrap.PackageInstaller.Install" # or ...PackageSearch.SearchAll
# Install directory of that editor (Hub layout), via the unity CLI
ED=$(unity editors path "$VERSION" --format json | python3 -c "import sys,json;print(json.load(sys.stdin)['data']['path'])")
# Resolve the executable per-OS (handles both "dir containing Unity.app" and the ".app" itself)
case "$(uname)" in
Darwin) if [ -d "$ED/Unity.app" ]; then UNITY_BIN="$ED/Unity.app/Contents/MacOS/Unity";
elif [[ "$ED" == *.app ]]; then UNITY_BIN="$ED/Contents/MacOS/Unity";
else UNITY_BIN="$ED/Unity"; fi ;;
Linux) UNITY_BIN="$ED/Editor/Unity" ;;
*) UNITY_BIN="$ED/Editor/Unity.exe" ;; # Windows (Git Bash / MSYS); use Editor\Unity.exe in PowerShell
esac
"$UNITY_BIN" -batchmode -projectPath "$PROJECT" -executeMethod "$METHOD" -logFile -
echo "Exit code: $?" # 0 = success, 1 = UPM error, 2 = timeout
-logFile -を指定すると、Editorのログ([PackageInstaller] / [PackageSearch]の行を含む)が標準出力にストリーミングされるため、解決の進行状況を確認し、UPMのエラーを読むことができます。ビルドによってunity editors pathの出力形式が異なる場合は、代わりにunity editors --installed --format jsonからディレクトリを取得してください。
検証
# Every requested id should appear as a dependency
cat "<project-path>/Packages/manifest.json"
実行が0で終了したこと、およびリスト内の各パッケージがmanifest.jsonに存在することを確認します。パッケージの解決に失敗した場合は、_request.Error.messageにログが記録されます。内容を読み、ID/バージョンとレジストリを照合してください。-logFile -でストリーミングしたEditor自身のログ([PackageInstaller]の行を含む)は上記の標準出力にあります。unity logsではなく、そちらを確認してください(unity logsに表示されるのはCLI自身のログであり、Editorのログではありません)。
ヘッドレスでインポートして保存する(.meta ファイルを生成する)
スクリプトやツールで新しい .cs/アセットファイルを書き込んだ後、Unity はそれらをインポートして、各アセットに必要な .meta ファイルを生成する必要があります。また、すべての .cs/アセットは、対応する .meta と一緒にコミットする必要があります。プロジェクトを一度開くだけ(unity open "<project-path>")でインポートされ、これらのファイルが生成されます。この処理をヘッドレスで実行する必要がある場合(スクリプトや CI など)は、次の方法を使用してください。
パッケージインストーラーとは異なり、これは同期処理です。処理は戻る前に完了するため、unity run 経由で安全に実行できます(自動挿入される -quit は問題ありません。このメソッドも正常な終了コードを返すために EditorApplication.Exit を呼び出します)。次のファイルを書き込みます。
Assets/Editor/ProjectBootstrap/ProjectSaver.cs:
using UnityEditor;
using UnityEngine;
namespace ProjectBootstrap
{
public static class ProjectSaver
{
// Invoke with: -executeMethod ProjectBootstrap.ProjectSaver.SaveAll
public static void SaveAll()
{
AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
AssetDatabase.SaveAssets();
Debug.Log("[ProjectSaver] Assets imported and saved.");
EditorApplication.Exit(0);
}
}
}
unity run "<project-path>" --editor-version <version> \
-- -executeMethod ProjectBootstrap.ProjectSaver.SaveAll
注意事項
- これらのエディタースクリプトは、ブートストラップを簡便にするためのものです。
Assets/Editor/ProjectBootstrap/に残す(呼び出さない限り何もしません)か、セットアップ後に削除するかは任意です。ユーザーにその旨を伝えてください。 - すべてのスクリプトは
UnityEditorを使用するためEditor/以下に置かれており、ビルドに含まれることはありません。 - マネタイズ/バックエンドのパッケージ(
com.unity.purchasing、com.unity.services.levelplay、UGS パッケージ)も同じ仕組みでインストールしますが、実際の統合には専用のスキル、implement-in-app-purchases、levelplay-unity-integration、build-live-gameを使用してください。