はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【Unity】SourceGeneratorを実装してUnityで動作させるまでやってみた(Unity2021.2以上で利用可)

はじめに

Unity2021.2以上からSourceGeneratorを利用することができるようになりました。

ソース ジェネレーターを使用すると、C# の開発者がコンパイル時にユーザー コードを検査できます。 ソース ジェネレーターは、ユーザーのコンパイルに追加される新しい C# のソース ファイルを即座に作成します。 この方法で、コードをコンパイル中に実行することができます。

ソース ジェネレーター - C# | Microsoft Learn

コンパイルをするときにC#コードを自動生成することができる機能といったところでしょうか。

SourceGeneratorの動作フロー

ソース ジェネレーター - C# | Microsoft Learn


またUnityにはILPostProcessorというコンパイル後のILをいじれる機能があります。以下のブログのILPostProcessorSourceGeneratorの箇所が面白いので、よければ是非一読してみてください。
forpro.unity3d.jp

Unity公式でもILPostProcessorからSourceGeneratorに移行している箇所もあるようです。(ただSourceGeneratorILPostProcessorの完全上位互換というわけではないので注意)

今後利用が増えていく可能性が高そうなので、SourceGeneratorを今のうちに学んでおくのも良さそうかもしれません。

対応環境

公式ドキュメントではUnity2021.3からSourceGeneratorの記載があります。
docs.unity3d.com

自分の目で確かめたわけではないですが、おそらくUnity2021.3 or newerかと。

実験環境

Rider2022.3.2
Unity2022.2.1f1

Source Generatorの実装

Source Generatorを実装にはUnityを使うわけではなく、Visual Studio等を利用します。私の場合はRider2022.3.2を利用していきます。

最初に流れを書いておくと、以下のことをしていきます。

  • .NET Standard 2.0のプロジェクト作成
  • Microsoft.CodeAnalysis 3.8 のインストール
  • ISourceGeneratorを実装し、Generator属性がついたクラスを作成
  • プロジェクトをビルド、Dllを出力

まず.NET Standard 2.0向けのプロジェクトを作成します。ここで.NET Standard 2.0以外のものを選ばないよう注意してください。

Riderでプロジェクト作成

次にMicrosoft.CodeAnalysis 3.8をプロジェクトにインストールします。
Riderの場合はメニューバーよりTools -> NuGet -> Show NuGet Tool Windowを選択し、Microsoft.CodeAnalysisを検索、v3.8を選びインストールします。

Microsoft.CodeAnalysis v3.8のインストール

次にISourceGeneratorを実装したクラスを作成し、Generator属性をclassにつけます。

using Microsoft.CodeAnalysis;

[Generator]
public class SourceGeneratorSample : ISourceGenerator
{
    public void Initialize(GeneratorInitializationContext context)
    {
        // Source Generatorを初期化するときに実行
        // 例:元のソースコードからコード自動生成のためのコードを収拾するために ISyntaxReceiverを行う
    }

    public void Execute(GeneratorExecutionContext context)
    {
        // コード生成を行うときに実行
        // 例:実際にコード生成のロジックを記述
    }
}

今回は動作するまでの手順の説明記事にしたいので、コードの説明はしません。(私自身もっと理解してからSourceGeneratorに関する細かい記事も書きたいです)
ひとまずUnity公式のサンプルを参考にしながら以下のようなコードを記述してみました。

using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;

namespace SourceGeneratorTest
{
    [Generator]
    public class SamplePropertyGenerator : ISourceGenerator
    {
        // SampleAttributeのコード本体
        private const string AttributeText = @"
using System;
namespace Sample
{
    [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
    sealed class SampleAttribute : Attribute
    {
        public SampleAttribute()
        {
        }
    }
}
";

        public void Execute(GeneratorExecutionContext context)
        {
            // hintName : 任意の一意の名前を指定する 名前には".g.cs" や ".generated.cs" などの明示的な C# ファイル拡張子を指定するのが一般的
            // NOTE : https://learn.microsoft.com/ja-jp/dotnet/csharp/roslyn-sdk/source-generators-overview
            context.AddSource(
                hintName : "Sample.Generated.cs",
                sourceText: SourceText.From(AttributeText, Encoding.UTF8));
        }
        
        public void Initialize(GeneratorInitializationContext context) { }
    }
}

コードが書けたらビルドして、.dllを出力します。

ビルド

Unity側での設定

Unity側では以下の操作を行います。

  • .dllのインポート
  • Asset LabelsRoslynAnalyzerを設定

先程生成した.dllをUnityへインポートします。

.dllのインポート

最後に.dllを選択してインスペクターより、Asset LabelsRoslynAnalyzerを設定すれば完了です。

インスペクターより設定を行う

また公式ドキュメントではEditorStandaloneを設定してと記載されていますが、Select platforms for pluginは選択しなくても動作するらしいです。
Unity 2022におけるC#ランタイムスクリプトについて – Unity for Pro

5. Go to Select platforms for plugin and disable Any Platform.
6. Go to Include Platforms and disable Editor and Standalone.

Roslyn analyzers and source generators - Unity マニュアル

以下のようなコードを書いてもエラーが出力されないので、Sample属性が生成されていることが分かります。

public class Sample
{
    [Sample]
    private int value;
}
// RiderでGo to Declaration or Usagesで自動生成されたコードの中身を見れる
using System;
namespace Sample
{
    [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
    sealed class SampleAttribute : Attribute
    {
        public SampleAttribute()
        {
        }
    }
}

しっかりとSourceGeneratorが動作していることが確認できました。