やらなイカ?

たぶん、iOS/Androidアプリの開発・テスト関係。

Test Helper v1.6 : UIレイアウト系のカスタム制約

Unity Test Frameworkによるテストを書くときに便利な Test Helper パッケージの v1.6 をリリースしました。 本バージョンでは、UIレイアウトを検証するための4つのカスタム制約(constraint)を追加しています。

github.com

UIレイアウトは変更される可能性が高いため、テストを書く投資対効果(ROI)が低く「書くべきでない」領域でした。 しかし、昨今、AIコーディングエージェントにsceneやprefabを編集させるようになると、人間では起こさないようなミス(コンポーネントの重なり、はみ出しなど)が発生します。 これを自律的かつ低コストで修正させるため、最低限の検証を機械的に行うために使用します。

WithinScreen制約

WithinScreen制約は、モーダルダイアログなどのUI要素が画面内に表示されている(はみ出ていない)ことを検証します。

次のように使用します。

using Is = TestHelper.Constraints.Is;

[TestFixture]
public class MyTestClass
{
    [Test]
    public void MyTestMethod()
    {
        var confirmDialog = GameObject.Find("ConfirmDialog");

        Assert.That(confirmDialog, Is.WithinScreen);
    }
}

失敗した場合は、次のように具体的なメッセージが出力され、コーディングエージェントに修正を促します。

Expected: RectTransform within screen (0, 0, 640, 480)
 But was: "ConfirmDialog" (-12.0, 437.0, 50.0, 50.0) exceeds the left edge by 12.0px and the top edge by 7.0px

なお、検証したいUI要素が複数あるときは、AllオペレーターでAssert.That(elements, Is.All.WithinScreen);と書けます。

また、修飾子.Within(float)で許容誤差を指定できます。デフォルトは 0.5f です。

WithinContainer制約

WithinContainer制約は、UI要素がダイアログなどのコンテナ要素内に表示されている(はみ出ていない)ことを検証します。

次のように使用します。

using Is = TestHelper.Constraints.Is;

[TestFixture]
public class MyTestClass
{
    [Test]
    public void MyTestMethod()
    {
        var viewport = GameObject.Find("Viewport").GetComponent<RectTransform>();
        var card = GameObject.Find("Card (0)");

        Assert.That(card, Is.WithinContainer(viewport));
    }
}

修飾子.Horizontally()もしくは.Vertically()で、横方向・縦方向のみ検証できます。 また、.Within(float)で許容誤差を指定できます。デフォルトは 0.5f です。

Overlapping制約

Overlapping制約は、UI要素同士の重なりを検証します。

次のようにNotオペレーターで「重なっていない」ことを検証できます。

using Is = TestHelper.Constraints.Is;

[TestFixture]
public class MyTestClass
{
    [Test]
    public void MyTestMethod()
    {
        var cards = GameObject.Find("CardGrid").GetComponentsInChildren<Image>();

        Assert.That(cards, Is.Not.Overlapping);
    }
}

修飾子.Ignoring(group)で、group間の重なりを検証から除外できます。 また、.Within(float)で許容誤差を指定できます。デフォルトは 0.5f です。

TextOverflowing制約

TextOverflowing制約は、TextおよびTMP_Textのテキストのはみ出し/切り詰めを検証します。

次のようにNotオペレーターで「はみ出し/切り詰めされていない」ことを検証できます。

using Is = TestHelper.Constraints.Is;

[TestFixture]
public class MyTestClass
{
    [Test]
    public void MyTestMethod()
    {
        var flavorText = GameObject.Find("Flavor Text");

        Assert.That(flavorText, Is.Not.TextOverflowing);
    }
}

また、修飾子.Within(float)で許容誤差を指定できます。デフォルトは 0.5f です。

補足:アサーションでやらないこと

今回追加した制約で検証するのは、恒久的に変更されないであろう(テストが壊れにくいであろう)観点のみです。 このほか、たとえばCanvasScalerの設定などプロジェクトでルールがあるものは、scene/ prefabのバリデーションを行なうテストを書くとよいでしょう。

筆者が公開しているunity-coding-skillsでは、機械的なアサーションをパスした後、コンポーネントの位置関係、色(コントラスト)、タイポグラフィなどの観点について、画像認識で検証させています*1

github.com

参考

カスタム制約、scene/ prefabのバリデーション方法などは、こちらで解説しています。

www.nowsprinting.com

*1:CIなどで毎回実行するのではなく、セッション内で変更があったときのみ実行

BannedApiAnalyzers.Unity を公開しました

Microsoft.CodeAnalysis.BannedApiAnalyzers は、プロンプトで使用を禁止したいAPIを設定ファイルに書くことでコンパイル時に診断できるRoslynアナライザです。

もちろんUnityでも使用できますが、Unityでは自動生成される .csproj ファイルに設定ファイルのパスを追加する必要があります。 これはエディタスクリプトで .csproj ファイル生成をフックして書き換えれば実現できますが、少し面倒です。

このエディタスクリプト実装が不要になるようフォークして変更した BannedApiAnalyzers.Unity を作ったので紹介します。

github.com

nugte.org にも公開済みです。

www.nuget.org

BannedApiAnalyzers.Unity の利点

.csproj ファイルの書き換えが不要

素の Microsoft.CodeAnalysis.BannedApiAnalyzers では、設定ファイルとして BannedSymbols.txt もしくは BannedSymbols.\*.txt を要求します。 ファイル名は固定ですが、ファイルパスを .csproj ファイルに AdditionalFiles として追加しなければなりません。

一方、Unityでは2021.3から、特定のファイルパスを .csproj ファイルに AdditionalFiles として追加する機能が使えます*1。 しかしファイル名に制約があり、Filename.[Analyzer Name].additionalfile フォーマットでないと処理されません。つまり、BannedSymbols.txt というファイルをアナライザに渡す手段はありません。

BannedApiAnalyzers.Unity は、UnityのAdditional files機能で渡されたファイルを設定ファイルとして読みます。 つまり、プロジェクトの Assets/ 下に <Filename>.BannedApiAnalyzers.Unity.additionalfile という名前のファイルを置くだけで、禁止するAPIを指定できます。

なお、次の点に注意してください。

  • <Filename> 部分は必須で、かつ途中にピリオドを含んではいけません
  • Assets/ 直下だけでなくサブディレクトリのどこに置いたファイルでも、プロジェクト全体に適用されます。ディレクトリやアセンブリごとに設定を使い分けることはできません
  • Packages/ 下にある組み込みパッケージにも適用されます

v3.11 ベース

nuget.orgに公開されている Microsoft.CodeAnalysis.BannedApiAnalyzers は v3.3.4 です。 次バージョンである v3.11 は安定版をリリースされないまま dotnet/roslyn に統合され、パッケージとしての公開はされていません。

BannedApiAnalyzers.Unity は、v3.11 のソースをベースにしています。v3.3.4 との差異は次のとおりです(Claude Code調べ)。

新機能(v3.11.0 で追加)
機能 概要
名前空間の ban 対応 BannedSymbols.txtN:SomeNamespace と書くと、そのネームスペース内のすべての型参照に RS0030 が発火する。v3.3.4 では未対応(エントリが無視された)。
継承元の ban 検出 class Derived : BannedBase {} のような継承構文に対しても RS0030 が発火するようになった。Base-type syntax node への RegisterSyntaxNodeAction が追加。
// コメントのサポート BannedSymbols.txt で行末に // 説明 と書けるようになった。v3.3.4 では // 以降が DocumentationCommentId の一部として解釈され、シンボル解決が静かに失敗していた。
内部変更(挙動に影響する可能性あり)
変更 影響
シンボル解決の遅延化 解決を Lazy<ImmutableArray<ISymbol>> に変更。パフォーマンス改善で機能差異はほぼないが、曖昧な ID のエッジケースで挙動が変わる可能性あり。
重複検出ロジック変更 v3.3.4: 解決済み ISymbol オブジェクトの比較。v3.11.0: 生の DeclarationId 文字列(trim 後)で grouping。未解決シンボルでも重複が検出されるように。

使いかた

インストール

アナライザのインストールは、NuGetForUnity か UnityNuGet (OpenUPM)*2 からがおすすめです。

NuGetForUnity の場合、次の手順でインストールすると、全アセンブリに適用されます。

  1. Open the NuGetForUnity window via NuGet > Manage NuGet Packages
  2. Search "BannedApiAnalyzers.Unity" and click Install

UnityNuGet の場合、openupmコマンドかPackage Managerウィンドウでインストールしてから、適用させたいアセンブリの Assembly Definition References に BannedApiAnalyzers.Unity_Unity を追加します。

openupm add org.nuget.BannedApiAnalyzers.Unity

NuGetForUnity と UnityNuGet の違いについて詳しくは、次の記事を参照してください。

www.nowsprinting.com

設定ファイル

Assets/ 下の任意のディレクトリに設定ファイルを作ります。ファイル名はフォーマット <Filename>.BannedApiAnalyzers.Unity.additionalfile を守る必要があります。

設定ファイルにはDocumentation Comment ID形式で禁止したいAPIを列挙します。書式は ID string format を参照してください。

たとえば、Assets/BannedSymbols.BannedApiAnalyzers.Unity.additionalfile に次のように書きます。

M:UnityEngine.GameObject.Find(System.String)

すると、次のように GameObject.Find(string) メソッドの使用箇所で警告が出ます。

なお、Documentation Comment ID の後ろに ; を挟んでメッセージを書けるので、どう書き換えるべきかを書いておくとよいでしょう。特にAI Coding Agentを使用するなら必須といえます。

補足

重大度の変更

デフォルトでは、BannedApiAnalyzers.Unity による診断の重大度(severity)は Warning です。 これは .ruleset.globalconfig、もしくは .editorconfig ファイルで変更できます。詳しくは、それぞれ過去記事を参照してください。

www.nowsprinting.com

www.nowsprinting.com

サポートUnityバージョン

BannedApiAnalyzers.Unityが動作するのは、Unity 2021.2 以降です。 Additional files 機能が(おそらく)Unity 2021.3 からなので、それに合わせてあります。

*1:https://docs.unity3d.com/Manual/roslyn-analyzers-additional-files.html

*2:執筆時点ではOpenUPMはリリース待ち

Unityプロジェクトでテスト設計+テストファースト開発のための Agent Skills

昨年、Unityプロジェクトでも Claude Code に自走させるワークフロー という記事を投稿しました。 その後も .claude/ 下を公開したリポジトリのメンテナンスを継続していましたが、改めて Agent Skills/ Claude Code プラグインとして公開しました。

github.com

実装計画スキル

代表スキルは /plan-feature <SPEC> で、これをプランモードで使用すると実装前にテスト設計を行い、プランファイルにテストケースを含めてくれます。また実装をテストファースト(TDDではなく)で進めるワークフローもプランファイルに含めます。

このスキルおよびワークフローには、次の特徴があります。

  • テスト設計
    • 保守性の高いテスト:AIが書きがちな、冗長なテスト、アサーションのないテスト、不要なテストダブル(モック/ スタブなど)を削減
    • 手動検証は最低限:統合テストレイヤでUI操作を含めたテストを実施、目視検証が必要なテストは画像解析による検証を実施*1。アニメーションや手触り感などの観点のみ人間に評価を移譲
    • プラン時点でテストケースをレビュー可能:検証内容も含めてプランファイルに出力されるので、テストケースをレビューすることでプランが仕様を満たしているかどうか判断できる
  • テストファースト
    • テストコードを先に実装・実行して、テストが失敗することを確認します。テストファーストには次の利点があります
      • テストコードの正当性(少なくとも、ちゃんと失敗できるテストである)が担保される
      • プロダクトコード(ゲーム本体コード)実装の完了基準を定義できる
      • テスト駆動開発(TDD)と比べ、変更ループが少なくトークン消費も抑えられる
  • コーディングガイドライン
    • Unity Test Framework向けテスト実装についてのガイドライン(非同期テストは UnityTest 属性でなく async + Test 属性を使う、など)
    • Unity向けコード実装についての最低限のガイドライン(Unity 2021.1以降ではオブジェクトプーリングを自前実装しないで ObjectPool<T> を使う、など)
    • Scene/ Prefab の編集は、エディタスクリプトをアドホックに書いてUnityエディタで実行
    • ScriptableObject などの複雑でないアセットファイルを直接編集するガイドライン
  • 内部品質(保守性・可読性など)
    • テスト可能なコードであるという点で、最低限の内部品質は確保
    • ワークフローの最後に、決定論的ツールによる静的解析を実行(後述)

インストールと設定

プラグイン/ スキルのインストール

Claude Code プラグインとしてプロジェクトスコープにインストールする場合は、次のスラッシュコマンドを実行します。

/plugin marketplace add nowsprinting/unity-coding-skills
/plugin install unity-coding-skills@nowsprinting-unity-coding-skills --scope project

その他、スキル単位でインストールするツールなどが使えるはずです。

MCPサーバの設定

一部のスキルは、JetBrains RiderビルトインのMCPサーバ及び、Unityエディタを操作する拡張を使用します。 設定方法は次の記事を参照してください。 なお、設定ファイルを手書きする場合は、MCPサーバの名称は必ず jetbrains にしてください。

www.nowsprinting.com

Riderを使いたくない場合、一部のスキルだけ書き換えてもらえれば動作はするはずです。 しかし、JetBrains MCPサーバにはファイル検索などCoding Agentを効率化するツールも含まれていますので、Riderの使用をお勧めします。 無料トライアルもあります。

静的解析の設定

強制したいコーディングルールは、コンパイラやRoslynアナライザで warning 以上の診断になるように設定しておくと、ワークフローの最終フェーズで修正されます。

たとえば、(AIがやりがちな)変更で使わなくなった型やメンバを削除せず放置するのを禁止するには、次の設定を .editorconfig ファイルに追加します。

resharper_unused_type_local_highlighting = warning
resharper_unused_type_global_highlighting = warning
resharper_unused_member_global_highlighting = warning
resharper_unused_member_local_highlighting = warning

.editorconfig ファイルについて詳しくは次の記事を参照してください。

www.nowsprinting.com

またRiderを使用できる場合、プラグインによる診断も対象になります。 (これもAIが書きがちな)長く複雑なメソッドを分割させるために、サイクロマティック/ コグニティブ複雑度を診断するプラグインが利用できます。 詳しくは次の記事を参照してください。

www.nowsprinting.com

注意事項

  • ドキュメントに "TBD" と書いてある仕様は無視されます(詳細を詰められたりしない)
  • Scene/ Prefab 編集スクリプトは、コミットも削除もしません(中身を見たいことがあるため)
  • スキルにはコーディング規約を含まないため、たとえば割とカジュアルにリフレクションが使われたりします。まず静的解析で縛ることを考え、決定論的に書けないものはプロジェクト固有のガイドラインを書くなどしてください
  • 構造的な問題や落とし穴などの観点は人間によるコードレビューが必要です
  • コード品質は既存のコードベースに引っ張られます。既存プロジェクトに導入する場合は、まず /refine-tests スキルでテストコードをリファインすることをお勧めします

使用感・メトリクス

主に、Slay the Spireクローンの制作に使用したときのもの。

  • プルリクエストにするくらいの粒度で使用して、プラン承認後1〜2時間(最近遅いので)自走して、実装・テストを完了します
    • UIレイアウトは雑なものですが、ちゃんと操作できるものが出来上がります(他のGameObjectによってブロックされていないことなどもテスト済み)
    • バグは出ますが仕様定義の漏れが支配的で、仕様レビューを厳密にすることで防げるはず(本プラグイン対象外)
  • テストケースレビューだけで、コードレビューは(テストコードもプロダクトコードも)省略することがほとんど*2。テストケースレビューも全ケースではなく、統合テストや受け入れテスト*3を中心にレビューしています
  • 内部品質は十分とは言えませんが、Claude Codeで開発イテレーションを繰り返しても崩壊しない程度の品質はキープできています*4
  • ステートメントカバレッジは 97.5% *5
  • code : test ratio は 1 : 5.0 *6

参考

本プラグインのスキルがベースとしているテスト設計・実装、メトリクス、Roslynアナライザなどについて詳しくは、次の同人誌を参照してください。 テストケースをレビューする際の観点・判断基準や、スキルをカスタマイズする助けになるはずです。

www.nowsprinting.com

*1:スクリーンショットを撮るテストコードに検証観点を残しているので、目視検証を含めたリグレッションテストを指示すれば再実行も可能。将来的に初回パスしたら以降は正解画像との画像比較によるビジュアルリグレッションテストに変換する案もあり

*2:スキル改善のために見ることはあります

*3:まだ精度が低いのですが、要求に対応したテストに「受け入れテスト」マーカーがつきます

*4:仕事で書くライブラリなどのコードは、ここから温かみのある手作業でリファクタリングしています

*5:ゲームの特性と、I/Oや通信がなく例外処理がほぼないためカバレッジは高め

*6:AIはテストを書きすぎる傾向があるので、この code : test ratio を低く抑えることが直近の課題

Gameplay MCP Server for Unity を公開しました

Unity製ゲームのランタイムに組み込んでAIエージェントにゲームをプレイさせるためのMCPサーバパッケージを公開しました。 Unityエディタを操作するMCPサーバは多数公開されていますが、これは操作対象がゲーム本体で、エディタ内だけでなくプレイヤービルドでも動作するものです*1

基本的なツールの実装を終えた状態で、バージョンは 0.3.0 です*2

github.com

MCPサーバ部分には MCP C# SDK を採用、ゲームタイトルで自由にカスタムツールを追加できます。 uGUI要素の操作は DeNA/Anjin でも使用されている UI Test Helper を使用しており、GameObjectの出現を(固定時間でなく)ポーリングで待ったり、ユーザの操作をブロックするオブジェクトがあるときは操作できないと判断するなど、UIテストに必要な機能を備えています。また、ゲームタイトル固有のカスタムUIフレームワークにも対応できます。

ビルトインツール

ビルトインで提供しているツールは次の5つです。

list_available_actions

画面に表示されている操作可能な*3 GameObject と、可能な操作(クリック、ドラッグなど)の組み合わせを返します。 モデルはこの中から次のアクションを選択できます。

invoke_action

対象 GameObject と操作を指定して、操作を実行します。

inspect_game_object

GameObject を、name, path*4, text*5, texture*6 を指定して検索し、プロパティを取得します。 表示まで時間がかかる場合でも GameObject の出現を一定時間ポーリングして待ちます。 操作の結果、表示されるはずの GameObject を確認するのに使用できます。

take_screenshot

スクリーンショットを撮影して返します*7

list_scenes

ロードされているSceneを返します。簡易的なゲームの状態確認ツールです。

カスタムツールの実装

ゲームタイトル固有のカスタムツールを追加できます。たとえば次のような用途を想定しています。

  • 現在のゲームの状態を返す
  • uGUI以外の操作
  • デバッグコマンドの実行

カスタムツールは、次のように MCP C# SDK の [McpServerToolType] および [McpServerTool] 属性を配置することで認識されます。

// Assets/Scripts/Runtime/MyGameTools.cs (custom tools created by the game title)
// Just add [McpServerToolType] and it's registered automatically!
[McpServerToolType]
public class MyGameTools
{
    [McpServerTool(Name = "get_player_status", ReadOnly = true, Destructive = false)]
    [Description("Returns the player's current status as JSON.")]
    public async Task<string> GetPlayerStatus(CancellationToken ct = default)
    {
        await UniTask.SwitchToMainThread(ct);
        var player = GameObject.FindWithTag("Player");
        return JsonSerializer.Serialize(new { hp = player.GetComponent<Health>().Current });
    }
}

なお、たとえばゲームの状態を返すツールを実装したことによって list_scenes ツールが不要になったときは、次のように無効化できます。

var config = new McpConfig();
config.DisabledTools.Add("mygame.list_scenes");

使用方法

インストール

あらかじめ MCP C# SDK v1.0.0 以降をインストールします。 NuGetパッケージなので NuGetForUnity などを利用してください*8。NuGetForUnityの使いかたについては過去記事を参照してください。

www.nowsprinting.com

続いて Gameplay MCP Server for Unity パッケージをOpenUPMからインストールします*9

  1. Project Settings ウィンドウを Editor > Project Settings で開き、Package Manager タブを選択
  2. Scoped Registries 下の + ボタンをクリックして次のように入力します
    1. Name: package.openupm.com
    2. URL: https://package.openupm.com
    3. Scope(s): com.nowsprinting and com.cysharp
  3. Package Manager ウィンドウを Window > Package Manager で開き、My Registries タブを選択
  4. Gameplay MCP を選択して Install ボタンをクリック

MCPサーバの起動

MCPサーバは次のコードで起動できます。 [RuntimeInitializeOnLoad] 属性などでゲーム起動時に常に起動したり、デバッグメニューから起動するなどします。

var config = new McpConfig
{
    OperatorPool = new OperatorPool()
        .Register<UguiClickOperator>()
        .Register<UguiDragAndDropOperator>()
        .Register<UguiTextInputOperator>()
};
var server = new McpServer(config);
server.StartAsync().Forget();

McpConfigには使用するオペレーターのほか、GameObjectを探す GameObjectFinder、操作可能の判断に使用するストラテジ関数などを指定できます。 これらはUI Test Helperパッケージの機能なので、詳細はUI Test Helperのドキュメントを見てください。

なお、サーバの待ち受けポートは McpConfig のほか、コマンドライン引数でも指定できます。マルチプレイヤーでも安心。

AIエージェントの設定

次のように設定します。Claude Codeの例:

{
  "mcpServers": {
    "gameplay": {
      "type": "http",
      "url": "http://localhost:8010/mcp"
    }
  }
}

Agent Skills

MCPサーバだけでも操作手順を指示する形であれば動くはずですが、ゲームを自律的プレイさせるには、ゲームのルール、目的などをAgent Skillsとして与える必要があります。

また、uGUIの操作ができなかったときなどの問題の切り分けのために、UI Test Helperのトラブルシュート資料もスキル化しておくと役立つはずです。

今後の展望

あといくつかツールを追加*10するほか、UI Test Helperの Paginator 関連の機能追加などを予定しています。

*1:IL2CPPビルドでも動作しますが、仕組み上WebGLは対象外です

*2:中途半端ですが、なんとなく今週アナウンスしようという気持ちになたっため

*3:interactable=true かつ、カメラからレイキャストが通る(ほかのオブジェクトにブロックされていない)もの

*4:ヒエラルキーのパス

*5:ボタンなどの表示テキスト

*6:ボタンなどのテクスチャファイル名

*7:スクリーンショットは補助的な、結果確認用ツールの位置づけなのですが、エージェントは割と安易に使ってきます。無駄なトークン消費を抑えるためには、Agent Skillsで用途を限るように書くなどして抑制してください

*8:UnityNuGetには登録済みで、レジストラへの反映待ち

*9:git urlでもインストールできますが、その場合は依存パッケージを手動でインストールしてください

*10:persistentDataPathアクセスなどを予定

MCP Server Extension for Unity v1.0.0 リリース

Unity Editor 向けの新しい MCPサーバ、MCP Server Extension for Unity をリリースしました。 JetBrains Riderにインストールするプラグインで、RiderにビルトインされているMCP Serverにツールを追加する拡張機能です*1

特徴は次の3点です。

  • UnityプロジェクトへのUPMパッケージインストールなし*2で利用可能
  • ビルトインの MCP Server を設定済みであれば、追加設定なしで利用可能
  • 複製したワークスペース*3からの同時使用も追加設定なしで可能

提供するツールは次の4つ。MCP Serverは21のツールを提供していますが、それに+4されます。

  • run_unity_tests: Unity Test Runnerでテストを実行します
  • run_method_in_unity: 任意のstaticメソッドを実行します*4。このツールで、sceneおよびprefabファイルを編集できます
  • get_unity_compilation_result: アセットをリフレッシュし、コンパイル結果を取得します
  • unity_play_control: 再生モードを操作します

プラグインページはこちら。

plugins.jetbrains.com

リポジトリはこちら。

github.com

利用方法

プラグインのインストール

  1. Settings > Plugins を開く
  2. Marketplace タブで "mcp unity" などで検索
  3. Install をクリック

MCPサーバの設定

ビルトインの MCP Server を設定済みであれば、すぐ使えます。

未設定の場合は

  1. Settings > Tools > MCP Server を開く
  2. Enable MCP Server をクリック
  3. お使いのCoding Agentに応じた Auto-Configure をクリック

詳しくは MCP Server ドキュメントを参照してください。

Agent Skills

必須ではありませんが、テスト実行ツールで指定するテストモードやアセンブリ名の求め方を指示するAgent Skillを設定すると便利です。 リポジトリのREADMEにサンプルを掲載していますので、参考にしてください。

アーキテクチャ

JetBrains IDEs 2025.2以降でビルトインされているMCPサーバには、カスタムプラグインでツールを拡張できる仕組みがあります。 Coding Agentとのインタフェースはこれを利用しています。個別に設定する手間を省けるほか、複数ワークスペースからの同時使用も考慮されており、ツールの実装に集中できます。

Rider向けプラグインは、JetBrains IDEsプラグインとして振る舞うためのKotlinフロントエンド層と、C#バックエンド層の2レイヤ構成です。 本プラグインで提供しているツールは、RiderのUnityプラグインが備えている(開かれている)インタフェースを呼び出し、Unityプラグインを経由してUnity Editor側のRider Editorパッケージと通信し、Unity Editorを操作しています。

この構造のため、Rider Editorパッケージの機能の範囲でしかツールを提供できません。従って今後機能追加の予定はなく、バグフィックス中心のメンテナンスになります。 また近い将来、JetBrains公式で同じ機能が提供されるはずです。そうなれば、このプラグインの公開も終了するつもりです。IDEバージョンの追随がめんどくさいので早く手放したい(本音)。

所感

コードはほぼClaude Codeさんが書きました。メンテしない前提なので内部品質は気にしない、ということで、狭義のvibe coding寄りで。 でもテストは書かせているしテストケースをレビューしているので、vibe codingとは言えない。普段の自分からはだいぶ寄っているという程度です。

Plan modeでテスト設計させたスキル*5やPlanファイルもリポジトリに入れてあるので、雰囲気わかっていただけるかと。

関連記事

www.nowsprinting.com

www.nowsprinting.com

*1:従って、正しくは「MCPサーバ」そのものは提供していません

*2:ビルトインのRider Editorパッケージは使用します

*3:cp, git clone, git worktree, claude --worktree などによる

*4:引数なしのメソッドのみで、戻り値は受け取れません

*5:Unity向けの移植なのでだいぶ適当

Test Helper v1.4.0 : FLIPによるTexture2D比較子

Unity Test Frameworkによるテストを書くときに便利な Test Helper パッケージの v1.4.0 をリリースしました。 本バージョンでは、ビジュアルリグレッションテストなどに便利なカスタム比較子 FlipTexture2dEqualityComparer を追加しました。

github.com

FlipTexture2dEqualityComparer

FlipTexture2dEqualityComparer は、2つの Texture2D を制約モデルで検証するときに使用できる比較子(comparer)です。 次のように使用します。

[Test]
public async Task MyTestMethod()
{
  // テスト実行(省略)

  // スクリーンショットを撮影
  await Awaitable.EndOfFrameAsync();
  var actual = ScreenCapture.CaptureScreenshotAsTexture();

  // 期待する画像をロード
  var expected = AssetDatabase.LoadAssetAtPath<Texture2D>(ExpectedImagePath);

  // 比較子を使って検証
  var comparer = new FlipTexture2dEqualityComparer(meanErrorTolerance: 0.01f);
  Assert.That(actual, Is.EqualTo(expected).Using(comparer));
}

通常、オブジェクト同士を Is.EqualTo で比較すると参照の比較になりますが、Using モディファイアで比較子を渡すことで比較方法を指定できます。 FlipTexture2dEqualityComparer はFLIP(後述)を使用して2つの Texture2D を比較し、平均エラー値がしきい値(上例では 0.01f)より高ければテストを失敗させます。

また失敗時には、エラー値から生成したマップファイルを出力します。差異の大きいところが暖色になるマグママップ(ヒートマップ)です。

FlipBinding.CSharp

FlipTexture2dEqualityComparer を使用するには、Test Helperパッケージのほかに、FlipBinding.CSharp NuGetパッケージのインストールが必要です。

FlipBinding.CSharp は、FLIP(後述)の C#バインディングAPIで、株式会社サイバーエージェント SGEコア技術本部が公開しているOSSです。 Windows, macOS, Linuxで動作します。

www.nuget.org

FlipBinding.CSharp は、UnityNuGet(OpenUPM)もしくは NuGetForUnity からインストールできます。 UnityNuGet および NuGetForUnity の使いかたは次の記事を参照してください。

www.nowsprinting.com

なお、UnityNuGet(OpenUPM)以外 からインストールしたときは、カスタムスクリプティングシンボル ENABLE_FLIP_BINDING の設定も必要です。

FLIP

FLIP は、NVIDIA Research Projects が公開している画像比較アルゴリズムです。 Python および C++ 実装がOSSとして公開されています。

github.com

FLIP は人間の目が認識する差異を評価してくれるアルゴリズムで、従来手法のようにパラメタ調整に悩むことなく、しきい値の決定だけで精度の高いビジュアルリグレッションテストを運用できるのが特徴です。 FLIP によるビジュアルリグレッションテストについて詳しくは、次の清原氏によるブログ記事および CEDEC 2025 の講演資料が参考になります。

blog.sge-coretech.com

cedil.cesa.or.jp


Unity Test Framework によるテストについては同人誌を頒布していますので、こちらも参考にしてください。

ikagoya.booth.pm

ikagoya.booth.pm

UnityではRoslynアナライザーのどのバージョンを使えばいいのか

Unity 2020.2 からUnityエディターでRoslynアナライザー(およびソースジェネレーター)がサポートされましたが、Unityエディタに同梱されているMicrosoft.CodeAnalysis.CSharpのバージョンよりも新しいバージョンでコンパイルされたアナライザーは動作しません。

どのバージョンが動作するのか、過去記事などで紹介したものもありますが、できるだけ最新のバージョンを使いたいと考えると都度確認せざるを得ず、とても面倒です。 そこで、Unity の各バージョンに対してどのバージョンが使用できるかを網羅したリファレンスリポジトリを作りました。

github.com

アナライザーごとのUnityで動作するバージョン情報

たとえばIDisposableAnalyzersページを見ると、最新バージョンは4.0.8ですが、 Unity 2022.2以降ではv4.0.4、 Unity 2022.2未満ではv3.4.15 を使用しなければならないことがわかるようになっています。

Version Microsoft.CodeAnalysis.CSharp Unity 2020.2 Unity 2021.2 Unity 2022.2 Unity 6000.0
4.0.8 4.5.0.0
4.0.7 4.5.0.0
4.0.6 4.5.0.0
4.0.5 4.5.0.0
4.0.4 4.0.0.0
4.0.3 4.0.0.0
4.0.2 4.0.0.0
4.0.1 4.0.0.0
4.0.0 4.0.0.0
3.4.15 3.5.0.0

なお、Microsoft.CodeAnalysis.CSharp以外の依存はチェックしていませんのでご注意ください。 たとえばNUnit.Analyzersは最新のv4.11.2までUnity 2022.2以降で動作するように見えますが、v4系はNUnit 4が前提であり、Unity Test FrameworkではNUnit 3ベースのv3.9.0*1までしか使えません。

アナライザーの追加・更新

アナライザーの検証および表の生成はGitHub Actionsワークフローになっており、リポジトリに無いアナライザも次の手順で確認できます。

  1. リポジトリをフォークします
  2. フォークリポジトリGitHub Actions workflowを実行します
    1. リポジトリのページで Actions > Check Roslyn Analyzer Versions を開く
    2. Run workflow ドロップダウンをクリック
    3. NuGetのパッケージIDを入力 (e.g., Microsoft.Unity.Analyzers)
    4. Run workflow ボタンをクリック
  3. ワークフローが実行され、新しいブランチにMarkdownファイルがpushされます

ぜひ実行結果をプルリクエストで送ってください。

なお、一度アナライザのページを追加すると、定期的に自動更新されるようになっています。

UnityエディタのMicrosoft.CodeAnalysis.CSharpバージョン確認

Unityエディタに同梱されているMicrosoft.CodeAnalysis.CSharpバージョンを確認するツールを tools/check-unity-roslyn-version.ps1 に置いてあります。

実行結果(すべてのUnityバージョンではない)は docs/unity_roslyn_versions.md に残しています。

こちらはまだ手動実行ですが、そのうち自動化したいと思っています。

関連記事

UnityプロジェクトへのRoslynアナライザー導入方法は、同人誌および過去記事で紹介しています。

www.nowsprinting.com

www.nowsprinting.com

www.nowsprinting.com

www.nowsprinting.com

*1:v3.10.0からNUnit 4に依存