こんにちは、Unityエンジニアのオオバです。

お悩みさん
お悩みさん
  • AIからUnityを操作していたら急にコマンドが全部タイムアウトする
  • コンパイル中でもないのにUnityが応答しない
  • 画面を見ても何も出ていないのに原因が分からない
  • オオバ
    オオバ
    本記事ではこれらの悩みを解決します。

    最近の僕は、Claude Code に Unity を操作してもらいながら開発しています。

    CLI 経由(uLoopMCP)でコンパイルを走らせたり、コンソールのログを取ってきたり。

    人間がエディタの前にいなくても、AI が勝手に回してくれる状態です。

    ところがある日、AI からのコマンドが全部タイムアウトする ようになりました。

    compile もログ取得も、何を投げても返ってこない。

    「Unity が固まった?コンパイルが終わってないだけ?」って思いますよね。

    僕も最初はそう疑いました。

    結論、どちらでもありませんでした。

    犯人は前面に出ていないモーダルダイアログ。タイムアウトしたらまずモーダルを疑う

    先に結論です。

    Unity のメインスレッドを止めていたのは、別のウィンドウの裏に隠れたモーダルダイアログ でした。

    モーダルが出ている間、Unity は人間がボタンを押すまで待ち続けます。

    当然、AI からのコマンドも処理されません。

    隠れたダイアログは、macOS のウィンドウ一覧を API で取ってくれば見つかります。

    そのウィンドウだけをスクリーンショットで撮って中身を確認し、Unity を前面化してからボタンを押せば復帰。

    この記事のポイント
    • AIのコマンドが全部タイムアウトしたら隠れたモーダルダイアログを疑う
    • CGWindowListCopyWindowInfoでUnityのウィンドウを列挙するとタイトル無しの小さいウィンドウが見つかる
    • screencapture -l でそのウィンドウだけを撮って中身を確認する
    • クリックの前にUnityを前面化する
    • 押すとプロジェクトが変わるダイアログはAIに押させない

    起きたこと

    Claude Code から uLoopMCP 経由で Unity を操作していたところ、compile やログ取得が突然すべてタイムアウトするようになりました。

    返ってくるのは UNITY_EDITOR_UNRESPONSIVE。

    メインスレッドの tick が止まったまま、一向に進みません。

    Unity が応答しなくなる理由として、まず思い付くのは次の2つです。

    ところが今回は、どちらでもありませんでした。

    しかも、画面全体のスクリーンショットを撮っても、それらしいものは何も写っていない。

    「何も起きていないのに Unity だけが止まっている」 という、いちばん気持ち悪い状態です。

    原因:前面に出ていないモーダルダイアログ

    原因は、前面に出ていないモーダルダイアログでした。

    この現象には2回当たっています。

    1回目:Unity Cloud 連携の案内

    1回目は、Unity Cloud との連携を案内するダイアログ「Link your Unity project」です。

    ボタンは「Close」と「Project Settings」の2つ。

    このダイアログがメインスレッドを止めていました。

    2回目:Spine のバージョン不一致の通知

    2回目は、Spine の「Version mismatch! Skeleton data could not be loaded」というダイアログ。

    OK ボタンだけの、ただの通知です。

    サブモジュール側にある古い Spine のデータをインポートしたときに出ていました。

    中身はただのお知らせなのに、OK を押すまで Unity は先に進めません。

    AI にとっては、このお知らせ1枚が壁になります。

    なぜ気付けなかったのか

    どちらのダイアログも、前面にあったブラウザの裏に隠れていました。

    画面全体のスクリーンショットを撮っても、写るのはブラウザだけ。

    osascript の System Events で Unity のウィンドウを調べようとしても、ウィンドウ名が空で返ってくるので、ダイアログの存在が分かりません。

    人間がエディタの前に座っていれば、Unity をクリックした瞬間にダイアログが目に入ります。

    AI はエディタを「見ていない」ので、気付くきっかけがありません。

    つまり、人間なら一瞬で片付く問題が、AI には見えない壁になるということです。

    見つけ方:Unity のウィンドウを列挙する

    隠れたダイアログを見つけるには、macOS のウィンドウ一覧を直接取ってくるのが確実でした。

    swift で CGWindowListCopyWindowInfo を回し、owner が Unity のウィンドウについて、ウィンドウ名と位置・大きさ(bounds)を列挙します。

    コードは10行程度で済みます。

    イメージとしては次のような形です。

    import CoreGraphics
    
    let options: CGWindowListOption = [.optionOnScreenOnly, .excludeDesktopElements]  
    let list = CGWindowListCopyWindowInfo(options, kCGNullWindowID) as? [[String: Any]] ?? []  
    for w in list where (w[kCGWindowOwnerName as String] as? String) == "Unity" {  
        let number = w[kCGWindowNumber as String] as? Int ?? 0  
        let name = w[kCGWindowName as String] as? String ?? ""  
        let b = w[kCGWindowBounds as String] as? [String: Any] ?? [:]  
        print(number, "[\(name)]", b["X"] ?? "", b["Y"] ?? "", b["Width"] ?? "", b["Height"] ?? "")  
    }
    

    出力の中で、メインのエディタウィンドウとは別に タイトル無しの小さいウィンドウ が見つかります。

    それがモーダルの正体です。

    中身の確認:そのウィンドウだけを撮る

    ウィンドウが見つかったら、次は中身の確認です。

    列挙で取れたウィンドウ番号を screencapture -l に渡すと、そのウィンドウだけを撮れます。

    screencapture -l <windowNumber> modal.png  
    

    ほかのアプリの裏に隠れていても、指定したウィンドウだけが写るのがポイント。

    撮った画像を AI に読ませれば、何のダイアログか、どのボタンがあるかを AI 自身で判断できます。

    閉じ方:前面化してから座標でクリック

    ダイアログの中身が分かったら、ボタンを押して閉じます。

    ここで2つハマりました。

    Unity を前面化してからクリックする

    1つ目は、クリックの前に Unity を前面化すること。

    前面化しないまま座標をクリックすると、その位置に重なっている別のアプリにクリックが入ってしまいます。

    ダイアログの裏にいるブラウザを押しても、Unity は止まったまま。

    OK ボタンの位置はウィンドウ高さの約88%

    2つ目は、ボタンの位置です。

    OK だけのダイアログでは、OK ボタンはウィンドウの中央下、高さの約88%の位置にありました。

    最初は78%付近をクリックしていたのですが、そこはボタンの上にある本文で、何も起きません。

    bounds から座標を計算するときは、少し下寄りを狙うのがコツです。

    AI に押させないダイアログを決めた

    モーダルを AI が閉じられるようになると、次に怖いのは「押してはいけないボタンまで押してしまう」ことです。

    そこで、自動で押してよいダイアログに線を引きました。

    「Script Updating Consent」や「API Update Required」のように、押すとプロジェクトの中身が変わるもの は、自動では押さないルールです。

    これらは、スクリプトの書き換えや API の更新を伴います。

    AI が勝手に「Yes」を押すと、気付かないうちにコードが書き換わるかも知れません。

    一方で、今回の「Link your Unity project」や Spine の Version mismatch のような、閉じてもプロジェクトに影響しない案内・通知は、閉じて先に進めます。

    人間に判断を戻すべきダイアログかどうかを、ボタンを押す前に見分けるということです。

    まとめ

    AI に Unity を操作させていて、一番やっかいだったのはエラーそのものよりも 人間向けのモーダル でした。

    エラーならログに出てくるので、AI が自分で読んで直せます。

    モーダルは画面の裏で黙って待っているだけなので、AI からはタイムアウトとしか見えません。

    今回のポイントをまとめます。

    今回のまとめ
    • AIからのコマンドが全部タイムアウトしたらまずモーダルを疑う
    • 画面全体のスクショやSystem Eventsでは隠れたダイアログに気付けない
    • CGWindowListCopyWindowInfoでUnityのタイトル無しの小さいウィンドウを探す
    • screencapture -l でそのウィンドウだけを撮って中身を確認する
    • クリック前にUnityを前面化しOKボタンは高さの約88%を狙う
    • 押すとプロジェクトが変わるダイアログは自動で押さない

    Unity は、人間がエディタの前にいる前提で作られています。

    AI に任せる範囲を広げるほど、こうした「人間向けの作り」にぶつかる場面は増えていくと思います。

    タイムアウトに当たったら、エラーを探す前に隠れたウィンドウを探してみてください。

    Claude Code のような生成AIと Unity を組み合わせた開発の進め方は、僕が執筆した 「AI×Unityゲーム開発スターターキット」 で体系的にまとめています。

    AI×Unityゲーム開発スターターキット
    Claude Codeなどの生成AIとUnityを組み合わせて、AIが育てやすいゲームコードを作る方法を学べる一冊です。...
    詳しくはこちら
    AI×Unityゲーム開発スターターキット

    uLoop まわりでは、Unity 7 で起動しなかったトラブルも記事にしています。

    オススメ記事
    検証環境
    参考サイト