A template for building Unity DLLs from an external .NET project, enabling modern C# development, fast iteration, and test-driven development outside the Unity Editor.
Unity's built-in scripting workflow (asmdef) has limitations:
- No latest .NET APIs — Unity's compiler lags behind mainstream .NET
- Slow iteration — Every code change triggers a domain reload in the Editor
- Limited TDD — Unity Test Framework requires the Editor to run
- AI agent unfriendly — AI coding agents can't easily build/test without Unity
This template solves all of these by compiling code in a standard .NET project, then auto-copying the resulting DLLs into Unity.
UnityModernTemplate/
├── unity/ # Unity 6 (6000.3.7f1) project
│ └── Assets/_Main/Scripts/ # DLLs auto-copied here by post-build
│
├── csharp/ # .NET solution
│ ├── Directory.Build.props # Unity path resolution + shared settings
│ ├── Directory.Build.targets # Post-build copy to Unity
│ ├── src/
│ │ ├── UnityDotNetSample/ # Runtime library → csharp.dll
│ │ │ ├── Components/ # MonoBehaviour samples
│ │ │ ├── Core/ # Pure C# utilities
│ │ │ └── ScriptableObjects/ # ScriptableObject samples
│ │ │
│ │ └── EditorConsoleLogProxy/ # Editor library → editor-console-log-proxy.dll
│ │ # Proxies Unity console logs to a local file
│ │
│ └── tests/
│ └── UnityDotNetSample.Tests/ # xUnit tests (net9.0)
│
├── .editorconfig # Code style rules
└── .gitignore
cd csharp
dotnet buildThis compiles all projects and auto-copies the DLLs to unity/Assets/_Main/Scripts/.
dotnet testAll 28 tests run outside Unity via xUnit — no Editor required.
Open unity/ in Unity Hub. The DLLs are already in place from the build step.
- Each
.csprojopts in with<CopyToUnityPlugins>true</CopyToUnityPlugins> Directory.Build.targetsruns a post-build step that copies DLL + PDB toAssets/_Main/Scripts/- Unity detects the file change and reimports only the changed DLL
This means editing runtime code only rebuilds the runtime DLL, and editing editor code only rebuilds the editor DLL — minimal domain reload.
The Unity Editor path is resolved in 3 tiers (highest priority first):
- Environment variable:
UNITY_EDITOR_PATH - Local user file:
Directory.Build.props.user(gitignored) - Default:
C:\Program Files\Unity\Hub\Editor\6000.3.7f1
- Create a new folder under
src/(e.g.,src/MyFeature/) - Create a
.csprojtargetingnetstandard2.1 - Set
<CopyToUnityPlugins>true</CopyToUnityPlugins> - Add Unity module references with
<Private>false</Private> - Add the project to
csharp.slnx - Add a
<ProjectReference>in the test project - Add
.gitignoreentries for the new DLL/PDB/meta files
A built-in Editor extension that writes Unity console output to Logs/console.log in JSON Lines format. This enables AI agents and external tools to read Unity logs without opening the Editor UI.
- Auto-initializes via
[InitializeOnLoad] - Thread-safe file writing with
FileShare.Readfor concurrent access - File is cleared on every domain reload
Log format:
{"t":"2025-02-06T23:45:12.345Z","l":"Log","m":"Hello World"}
{"t":"2025-02-06T23:45:12.346Z","l":"Error","m":"NullRef","s":"at Foo.Bar()"}Reference only the module DLLs (UnityEngine.CoreModule.dll, etc.), never the facade UnityEngine.dll. Referencing both causes CS0433: type exists in both assemblies errors.
Do not place external DLLs in Assets/Plugins/. Unity treats this folder specially and may interpret DLLs as Roslyn analyzers, causing spurious warnings. Use any non-special folder (e.g., Assets/_Main/Scripts/).
This is required to avoid System.Object vs UnityEngine.Object ambiguity when both namespaces are implicitly imported.