はじめに
Unity2021.2からSourceGeneratorが動作するようになりました。
これを使うことでコンパイル時にソースコードを自動生成することができます。
「面倒なボイラープレートを書きたくない!」
そんな面倒くさがりなそこのあなた!
「デバッグコマンドやオレオレスクリプトを実装するのにリフレクションは使いたくない!
だけどコマンドの追加のたびに呼び出しコードを書きたくないし式木のコンパイル時間も嫌だしIL2CPPの恩恵も受けたい!」
そんな欲張りさんなあなたももう大丈夫!
SourceGeneratorを使えるようになればクラスや関数にアトリビュートをつけるだけで必要なコードを生成したり出来ちゃうんです!
あらゆるコードが生成できちゃう!
InterfaceやGenericsといったC#の言語仕様上の限界に囚われない圧倒的自由!
コンパイル時だからIL2CPPの恩恵も受けで実行時コストも少ない!
まさに無敵の力!
どうです? 試したくなってきたんじゃありませんか?
そんな力の片鱗を味わってみたい方は先へお進みください。
1.プロジェクトの作成
SourceGenerator
はUnityのプロジェクトとは独立したC#クラスライブラリ
を作成し、
バッチビルド
したDLLをUnityにインポートして使う形になります。
ということでまずはUnityプロジェクトとは別にVisualStudio
でC#のクラスライブラリ
を作成します。
新しいプロジェクト
を作成
C#クラスライブラリ
を選択
プロジェクト名はSourceGeneratorExample
としました。
フレームワークは.NET Standard2.0
を選択します。
プロジェクトが作成できたらツールバーからプロジェクト→NuGetパッケージの管理
と進んでください。
「ぬーげっと」ってなんかユルくておもしろいですよね。
NuGetパッケ-ジマネージャ
が開いたら参照
タブに切り替え、検索窓でMicrosoft.CodeAnalysis
と検索してください。
Microsoft.CodeAnalysis
とMicrosoft.CodeAnalysis.Common
の二つをインストールします。
二つともバージョンは3.9.0
を選択してください。
OK
I Accept
ひとまずプロジェクトのセットアップはここまでです。
2.下準備
ここからコードです。
ソリューションエクスプローラー
でプロジェクトを右クリック→追加→新しい項目
と進んでください。
C#クラス
を選択し、名前はCodeBuilder
とします。
作成したファイルの中身をいったん削除してこちらのリポジトリからCodeBuilder.cs
をコピペしてください。
CodeBuilder
はコード生成を少し楽にするためのクラスになります。
3.コード生成
いよいよコード生成の本体に入っていきます。
最初から作成されているClass1
を使いますが、ダサいのでリネームしておきましょう。
ひとまずExampleClassGenerator
にしておきます。
先にコード全文を貼っておきますのでこれをリネームしたExampleClassGenerator
の中に貼り付けてください。
(もともと書かれているコードは削除してください。)
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using System.Text;
using Tsukuru;
namespace SourceGeneratorExample
{
[Generator]
public class ExampleGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
}
public void Initialize(GeneratorInitializationContext context)
{
var cb = new CodeBuilder();
cb.AddCode("//Auto Generated Class");
cb.NewLine("using UnityEngine;");
using (new BlockScope(cb, "internal class ExampleGeneratedClass"))
{
using (new BlockScope(cb, "internal static void ExampleGeneratedFunction()"))
{
cb.NewLine("Debug.Log(\"I am auto generated class!!\");");
}
}
context.RegisterForPostInitialization(_ =>
{
_.AddSource("ExampleGeneratedClass_g.cs", SourceText.From(cb.ToString(), Encoding.UTF8));
});
}
}
}
ビルド
コードの解説はいったん後にしてUnity上で動作させましょう。
ソリューションエクスプローラーでソリューション名を右クリックし、「バッチビルド」を選択してください。
エクスプローラーでソリューションファイルを作成したフォルダを開き、
ソリューション名と同名のフォルダ
→bin
→Release→netstandard2.0
と進んでください。
netstandard2.0
フォルダ内の.dll
ファイルをUnityのAssets
フォルダ内の好きな場所にインポートします。
インポートしたDLL
を選択し、Inspector
でAnyPlatform
のチェックを外し、IncludePlatforms
もすべてチェックを外します。
欲しいのはSourceGeneratorの生成したコード
だけであり、SourceGenerator自体
がアセンブリに含まれてしまうと邪魔になるのでこのように設定する必要があります。
次にAssetLabel
を追加します。
Inspector
の右下のアイコンをクリックし、入力欄にRoslynAnalyzer
と入力してください。
「ろずりん」ってかわいくない?
VisualStudioでUnity側のC#ソリューションを開き,Assembly-CSharp
→参照
→SourceGeneratorExample
の中にExampleGeneratorClass_g.cs
が生成されていれば成功です。
このようにMonoBehaviour
からもアクセスできるはずです。
コードの解説
SourceGeneratorExample
のソリューションに戻ってコードの中身を解説していきます。
[Generator]
public class ExampleGenerator : ISourceGenerator
[Generator]
属性とISourceGenerator
インターフェースを使用することでソース生成を行うクラスを定義できます。
ISourceGenerator
には以下の二つの関数が宣言されているのでこれらを実装します。
public void Execute(GeneratorExecutionContext context)
public void Initialize(GeneratorInitializationContext context)
今回はInitialize
のみ利用します。
var cb = new CodeBuilder();
cb.AddCode("//Auto Generated Class");
cb.NewLine("using UnityEngine;");
using (new BlockScope(cb, "internal class ExampleGeneratedClass"))
{
using (new BlockScope(cb, "internal static void ExampleGeneratedFunction()"))
{
cb.NewLine("Debug.Log(\"I am auto generated class!!\");");
}
}
Initialize関数の前半では先ほどのCodeBuilderクラスを使用してソースコードの文字列を生成しています。
この部分は文字列さえ生成できればどんな形で記述してもOKです。
context.RegisterForPostInitialization(_ =>
{
_.AddSource("ExampleGeneratedClass_g.cs", SourceText.From(cb.ToString(), Encoding.UTF8));
});
Initialize
関数の引数にはGeneratorInitializeContext
が渡されてくるので、
RegisterForPostInitialization
を呼び出してコールバックを渡すことでSourceGenerator
が読み込まれた直後の処理を設定できます。
生成結果が常に同じ内容(アトリビュートクラスの生成など)の場合にはこれを利用するのが良いでしょう。
コールバックの引数にはGeneratorPostInitializationContext
が渡されますので、
AddSource
を呼び出すことでソースコードの生成を行うことができます。
第一引数にはファイル名を渡します。
自動生成されるソースのファイル名にはサフィックスとして_g
.g
.generated
等をつける慣習があるっぽいです。
第二引数にはソースコードの文字列を渡します。
UnityとVisualStudioで正しく日本語を表示するためにはUTF8
で保存する必要があるため、SourceText.From
を利用してUTF8
を指定しています。
今回のコードの全文は以下のリポジトリにあります。
まとめ
Unity
でSourceGenerator
を使う手順は
・.netstandard2.0
のC#クラスライブラリ
を作成する
・Microsoft.CodeAnalysis(3.9)
とMicrosoft.CodeAnalysis.Common(3.9)
をインストールする
・ISourceGenerator
を継承し、[Generator]
属性を付与したクラスを作る。
・バッチビルド
を行い、出力されたDLL
をUnity
にインポートする
・DLL
のImportSettings
ですべてのプラットフォームを除外
する。
・DLL
のImportSettings
でAssetLabel
にRoslynAnalyzer
を付与する。
コード生成やメタプログラミングと聞くと難しく思えるかもしれませんが、実際に試してみると手順も簡単でおまじない的なコードも殆ど無いので気軽に遊べます。
コンパイル時コード生成
という響きだけでもなんかカッコいいですし、自分が全知全能の無敵になった感覚が味わえます。
これから盛り上がっていく最先端コンテンツの一つでもあるので周囲からUnityめっちゃできる人
だと思われたい人にもうってつけです!
皆SourceGenerator
を始めてこの圧倒的なC#新時代の力に酔いしれましょう!
今回はとりあえず導入編ということで決め打ちのソースコードを生成するだけにとどめましたが、実際にはUnityプロジェクト内のクラスの解析結果を受け取ってそれに合わせたコード生成をリアルタイムに行うことで真価を発揮します。
ということで次回はUnity側でクラスや関数に付与した属性を読み取ってそれに合わせたコード生成を行う方法を解説したいと思います。