Below is a drop‑in template you can copy into your package.
Everything is plain C# / JSON, so it works from Unity 2020.3 LTS up to Unity 6.x.
## 2 — Editor script that injects the scoped registry automatically
Create Editor/RegistryBootstrap.cs inside your package:
// You need Newtonsoft.Json for robust JSON editing.
// Unity ships it as the built‑in package "com.unity.nuget.newtonsoft-json"
using Newtonsoft.Json.Linq;
namespace Com.Company.MyPackage.Editor
/// <summary>Adds the registry once per project, then stays silent.</summary>
[InitializeOnLoad] // runs as soon as the editor loads domain
internal static class RegistryBootstrap
// ---- CHANGE THESE CONSTANTS TO MATCH YOUR REGISTRY ----
private const string RegistryName = "OpenUPM";
private const string RegistryUrl = "https://package.openupm.com";
private static readonly string[] Scopes = { "com.company" };
// ---------------------------------------------------------
static RegistryBootstrap() => TryAddRegistry();
private static void TryAddRegistry()
string manifestPath = Path.Combine(Application.dataPath, "..", "Packages", "manifest.json");
if (!File.Exists(manifestPath)) return; // should always exist citeturn8view0
var json = JObject.Parse(File.ReadAllText(manifestPath, Encoding.UTF8));
// 2. Ensure the array exists
var registries = (JArray)(json["scopedRegistries"] ?? new JArray());
json["scopedRegistries"] = registries; // keep reference if it was missing
bool present = registries
.Any(r => (string)r["name"] == RegistryName);
["scopes"] = new JArray(Scopes)
// 5. Write back, pretty‑printed for humans
File.WriteAllText(manifestPath, json.ToString(Newtonsoft.Json.Formatting.Indented), Encoding.UTF8);
EditorUtility.DisplayDialog(
$"The registry was written to Packages/manifest.json so that packages in {string.Join(", ", Scopes)} can resolve.",
### How it works
- Location –
Packages/manifest.json lives one folder above Assets in every project citeturn8view0.
- Logic – On the first domain reload after your package is imported, the static constructor checks the
scopedRegistries array and adds an entry if it’s missing.
- Safety – If the user already declared the registry (possibly with different URL/scopes) the script does nothing.
- Dependencies – Starting with 2021.1 Unity embeds
Newtonsoft.Json; on older versions declare a dependency on "com.unity.nuget.newtonsoft-json" in your package manifest.
Tip: If you prefer to prompt instead of writing automatically, replace step 4/5 with EditorUtility.DisplayDialogComplex() and write only when the user clicks “Yes”.
## 3 — Guarding your code when the dependency is missing
### a) Add a Version Define to your assembly definition
Open the .asmdef that holds the code which uses the external package (for example Runtime/MyPackage.asmdef) and add:
"name": "Com.Company.MyPackage",
"allowUnsafeCode": false,
"name": "com.external.cool‑lib", // ← the *package name* you depend on
"expression": ">=1.0.0", // any SemVer or range is allowed
"define": "COOL_LIB_PRESENT" // this symbol will appear when present
The Version Defines feature lets you emit a compile‑time symbol when (and only when) a specific package and version is installed, so you don’t need hard references that break when it isn’t. Unity documents the mechanism here citeturn3view0turn7search1.
### b) Isolate the optional code
using CoolLib; // only compiles if the lib is installed
namespace Com.Company.MyPackage
internal static class OptionalFeature
public static void DoStuff()
// Fallback or stub keeps the API stable:
public static void DoStuff() =>
UnityEngine.Debug.LogWarning(
"Install 'com.external.cool‑lib' to enable advanced features.");
### c) (Optional) Show an editor‑time warning
If you prefer a pop‑up rather than a compile stub:
#if !COOL_LIB_PRESENT && UNITY_EDITOR
internal static class MissingCoolLibNotifier
static MissingCoolLibNotifier()
if (EditorPrefs.GetBool("Com.Company.MyPackage.HideMissingCoolLibPrompt"))
if (EditorUtility.DisplayDialog(
"Optional dependency missing",
"Install 'com.external.cool‑lib' (via Package Manager) to enable XYZ.",
"Open Package Manager", "Ignore"))
UnityEditor.PackageManager.UI.Window.Open("");
EditorPrefs.SetBool("Com.Company.MyPackage.HideMissingCoolLibPrompt", true);
## What you just achieved
- Zero‑click onboarding – the first time your package lands in a project, the registry entry appears automatically, so Unity can resolve your indirect dependency.
- Safe compilation – projects compile whether or not the secondary package is present; advanced features simply light up when it is.
- Friendly guidance – developers see a clear dialog or console message explaining why something is disabled instead of staring at compiler errors.
With these two building blocks your package behaves like a polished Unity first‑party package even though it pulls content from a scoped registry that the user didn’t configure beforehand.