許多 Unity 開發者都熟悉 DOTween 等熱門套件,這些套件的核心功能都封裝在一個獨立的 .dll (動態連結函式庫) 檔案中,而 Unity 專案本身只剩下少數幾個腳本。這種作法能保持專案的整潔,也大幅提升了程式碼的可重用性。
然而,一般的 .NET 專案 (如 Console、WPF) 無法直接使用 Unity 獨有的 MonoBehaviour、GameObject、Coroutine 等功能。那麼,要如何才能像 DOTween 一樣,建立一個可以編寫這些 Unity 專屬功能的 .dll 專案呢?
本文將逐步拆解這個過程,帶你從頭開始,打造一個能被 Unity 專案引用的獨立 C# .dll 函式庫。
建立 .NET 類別庫專案
首先,我們需要建立一個 .NET 專案。
新增一個空資料夾來當作專案資料夾,在資料夾中開啟 VS Code,輸入以下指令來建立一個用於開發 .dll 的專案。
1dotnet new classlib
現在專案資料夾中會出現一個 Class1.cs、obj 資料夾和一個與專案資料夾同名的 .csproj。
引用 Unity 核心函式庫
這是最關鍵的一步,為了讓我們的 .dll 專案能引用 Unity 的 API,我們必須手動將 Unity 的核心函式庫檔案引入專案。
開啟 Unity Hub,切換到「安裝」,點擊 Unity 編輯器右邊的「管理」,選擇「在檔案總管中顯示」,他會用檔案總管開新視窗,並開啟 Unity.exe 的所在位置。
現在的路徑可能是這樣:
1F:\UnityEditors\2021.3.37f1\Editor
那 UnityEngine.dll 的位置就會在 Data > Managed 裡面,先將路徑記錄下來,例如:
1F:\UnityEditors\2021.3.37f1\Editor\Data\Managed\UnityEngine.dll
接著,用剛剛開好的 VS Code 打開那個與專案資料夾同名的 .csproj 檔案,目前的檔案內容可能類似於:
1<Project Sdk="Microsoft.NET.Sdk">
2
3 <PropertyGroup>
4 <TargetFramework>net9.0</TargetFramework>
5 <RootNamespace>unity_dll_dev</RootNamespace>
6 <ImplicitUsings>enable</ImplicitUsings>
7 <Nullable>enable</Nullable>
8 </PropertyGroup>
9
10</Project>
我們要做幾件事:
- 將 TargetFramework 改成 netstandard2.1 (如果是 Unity 2020 與更老的版本則是 2.0)。
- 刪除 ImplicitUsings 與 Nullable 兩行,這是新版 C# 才能用的特性,Unity 不支援。
- 在兩個 Project 之間加上 UnityEngine.dll 的引用。
最後的結果如下:
1<Project Sdk="Microsoft.NET.Sdk">
2
3 <PropertyGroup>
4 <TargetFramework>netstandard2.1</TargetFramework>
5 <RootNamespace>unity_dll_dev</RootNamespace>
6 </PropertyGroup>
7
8 <ItemGroup>
9 <Reference Include="UnityEngine">
10 <HintPath>F:\UnityEditors\2021.3.37f1\Editor\Data\Managed\UnityEngine.dll</HintPath>
11 </Reference>
12 </ItemGroup>
13
14</Project>
到目前,我們的 C# .dll 專案理應引用到 UnityEngine 命名空間下的功能,也就是說現在可以在獨立的 C# 專案而不是 Unity 的環境中開發功能。
撰寫可操作 Unity 的 .dll 程式
編輯 Class1.cs 來寫個小功能。如果你需要用到 Coroutine 之類的功能,可以讓方法傳入 MonoBehaviour,再用它來呼叫 StartCoroutine。
程式寫好後,輸入以下指令來將程式編譯成 .dll:
1dotnet pack -c Release
輸出的 .dll 會在專案的 bin\Release\netstandard2.1 裡面,只要複製自己的 .dll 到 Unity 專案的 Assets > Plugins 資料夾下即可。
多說一句,我們其實能更直接些,讓 Class1 繼承 MonoBehaviour,並把它當成一般的 Unity 腳本來使用,Unity 支援直接拖曳 .dll 下的 MonoBehaviour 腳本到物件上!
這時就有個問題了,每一次改動程式都要手動把 .dll 拖到 Unity 中,還要覆蓋掉舊版,這麼麻煩的操作能不能簡化呢?
自動複製編譯好的 .dll 到 Unity
我們真的可以讓它自動在編譯後複製到 Unity!再次開啟 .csproj 檔案,在 Project 之間加入以下內容:
1<Target Name="CopyFile" AfterTargets="Pack">
2 <Copy
3 SourceFiles="$(OutDir)$(TargetName).dll"
4 DestinationFolder="..\TestDLL\Assets\Plugins"
5 />
6</Target>
DestinationFolder 後的路徑可以用絕對路徑或相對路徑,路徑的目的地都是你的 Unity 專案內的 Plugins 資料夾。
現在更新程式碼,再次輸入 dotnet pack -c Release
,新的 .dll 將會自動複製到 Unity 中,並覆蓋掉舊的檔案!
最後,我再提出一個問題:打指令很麻煩,可以再簡化嗎?
Ctrl + Shift + B 快捷鍵編譯設定
VS Code 的 Ctrl + Shift + B 快捷鍵能用來呼叫 dotnet 的編譯功能,當然也能用來執行 dotnet pack -c Release
指令。
在 C# .dll 專案中建立 .vscode 資料夾,裡面再建立 tasks.json 檔案,開啟它並貼上以下內容:
1{
2 "version": "2.0.0",
3 "tasks": [
4 {
5 "label": "Pack",
6 "type": "shell",
7 "command": "dotnet pack -c Release",
8 "group": {
9 "kind": "build",
10 "isDefault": true
11 },
12 "presentation": {
13 "reveal": "always"
14 }
15 }
16 ]
17}
現在,你只要開發中隨時按下 Ctrl + Shift + B,.dll 就會立即開始編譯,最後還會自動幫你更新到 Unity 專案中,我們就能做即時測試!
將 Unity 程式用 .dll 打包主要會用在需要保護核心程式碼的場合,在 .dll 中我們可以保留 1 ~ 2 組主要的對外 API,其他的核心業務邏輯可以用加殼 (加密、混淆) 的方式來讓人不易讀取原始碼。
另一種用處則是在這套 .dll 程式是非常穩定,是個如 DOTween 那樣的可跨專案大量使用的工具,就可以考慮包成 .dll 來方便傳輸與遷移。