😈
【Unity】コンパイル結果に応じて歓声か罵声を浴びせる拡張機能
結論、何を作ったのか
- コンパイル結果に応じて以下のようなウィンドウを出す

エラーがあれば鞭を与える

エラーがなければ飴を与える
何故作ったのか
- コンパイル(ドメインリロード)終了を検知して何かするプログラムを作ってみたかった
このエディタ拡張を構成するプログラム
CompileWatcher.cs
-
コンパイルパイプラインを監視
- コンパイル開始時にエラーカウントをリセット
- 各アセンブリのコンパイル完了でエラー数を集計
-
コンパイル終了後に結果を判定
- エラーなし:成功フラグを SessionState に保存
- エラーあり:乱数でウホまたはアホを選び、結果表示ウィンドウを呼び出し
- 成功フラグが有効なら成功用ウィンドウを呼び出し
実際のプログラムはこちら
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditor.Compilation;
using UnityEngine;
[InitializeOnLoad]
public static class CompileWatcher
{
private static int errorCount;
private static readonly System.Random rng = new(); //static で同シードによる偏りを防ぐ
static CompileWatcher()
{
CompilationPipeline.compilationStarted += _ => errorCount = 0;
CompilationPipeline.assemblyCompilationFinished += (path, msgs) =>
{
foreach (var m in msgs) if (m.type == CompilerMessageType.Error) errorCount++;
};
CompilationPipeline.compilationFinished += obj =>
{
//エラー数を SessionState に保存
if (errorCount == 0)
{
SessionState.SetInt("CompileSucceeded", 0);
}
else
{
string msg = (rng.NextDouble() < 0.3) ? "ウホ" : "アホ";
CompileResultWindow.Show(msg, Color.red, 1200, 700, 300);
}
};
}
[DidReloadScripts]
private static void OnScriptsReloaded()
{
var isSucceeded = SessionState.GetInt("CompileSucceeded", 0) == 0;
if (isSucceeded) CompileResultWindow.Show("なかなかやる", Color.green, 1200, 700, 150);
}
}
CompileResultWindow.cs
- ウィンドウを生成し、渡されたメッセージを中央に表示(アニメーション有)
-
Show():- ウィンドウインスタンス作成、メッセージ, 色, フォントサイズ設定
- 画面中央へ固定サイズで配置
-
Animate(): 文字拡大アニメーション, 一定時間後に自動でウィンドウ削除 -
OnDisable(): アニメーション更新解除
実際のプログラムはこちら
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
public class CompileResultWindow : EditorWindow
{
private const float DURATION = 3.0f;
private Label messageLabel;
private string message;
private Color textColor;
private int fontSize;
private double startTime;
public void CreateGUI()
{
var root = rootVisualElement;
root.style.flexDirection = FlexDirection.Column;
root.style.alignItems = Align.Center;
root.style.justifyContent = Justify.Center;
messageLabel = new Label(message);
messageLabel.style.unityTextAlign = TextAnchor.MiddleCenter;
messageLabel.style.color = textColor;
messageLabel.style.fontSize = fontSize;
messageLabel.style.opacity = 1.0f;
root.Add(messageLabel);
EditorApplication.update += Animate;
}
/// <summary>
/// ウィンドウを画面中央に表示
/// </summary>
public static void Show(string msg, Color col, float width, float height, int fontSize)
{
//インスタンス作成
var w = CreateInstance<CompileResultWindow>();
w.message = msg;
w.textColor = col;
w.fontSize = fontSize;
w.startTime = EditorApplication.timeSinceStartup;
w.titleContent = new GUIContent(""); //タイトルバー非表示
//画面中央に配置, サイズ固定
float sw = Display.displays[0].systemWidth;
float sh = Display.displays[0].systemHeight;
w.position = new Rect((sw - width) * 0.5f, (sh - height) * 0.5f, width, height);
w.minSize = new Vector2(width, height);
w.maxSize = new Vector2(width, height);
w.ShowUtility();
w.Focus();
}
void Animate()
{
float elapsed = (float)(EditorApplication.timeSinceStartup - startTime);
float t = Mathf.Clamp01(elapsed / DURATION);
float scale = 1f + 0.5f * t;
//拡大アニメーション
messageLabel.style.scale = new Scale(new Vector2(scale, scale));
if (elapsed > DURATION + 0.5f)
{
EditorApplication.update -= Animate;
Close();
}
}
void OnDisable() { EditorApplication.update -= Animate; }
}
処理の流れ
実装のポイント
static な System.Random 型メンバ変数
CompileWatcher.cs
private static readonly System.Random rng = new(); //static で同シードによる偏りを防ぐ
- 短時間に
newを複数呼び出すと同じシードになる可能性アリ - 共有の rng を使い回してシード重複を根本から防ぐ
歓声ウィンドウ(なかなかやる)だけ別の関数にしている理由
CompileWatcher.cs
static CompileWatcher()
{
(前略)
CompilationPipeline.compilationFinished += obj =>
{
//エラー数を SessionState に保存
if (errorCount == 0)
{
SessionState.SetInt("CompileSucceeded", 0);
}
else
{
string msg = (rng.NextDouble() < 0.3) ? "ウホ" : "アホ";
CompileResultWindow.Show(msg, Color.red, 1200, 700, 300);
}
};
}
[DidReloadScripts]
private static void OnScriptsReloaded()
{
var isSucceeded = SessionState.GetInt("CompileSucceeded", 0) == 0;
if (isSucceeded) CompileResultWindow.Show("なかなかやる", Color.green, 1200, 700, 150);
}
- Unity はエラーなしでアセンブリをビルドすると、ドメインリロード実行する
-
EditorApplication.delayCallはドメイン内キュー - delayCall に登録したコールバックは、現ドメインが生きている間だけ実行されるため、
再読み込み中にリセットされて消失する
改善点
- ハードコーディングしている部分が多い
- ちょっと適当に作りすぎましたかね…
余談

一定確率でアホの代わりにウホが出るようになっています
Discussion