1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Unreal Engineでのコーディングを爆速にするNeovimプラグイン群をAIと作ってみた

Posted at

Unreal Engineでのコーディングを"爆速"にするNeovimプラグイン群をAIと作ってみた

はじめに:なぜ作ろうと思ったのか?(開発ポエム)

20数年、コー-ディングはVim、デバッグはVisualStudioやRiderという開発スタイルを続けてきました。しかし、Unreal Engine(以下、UE)の開発は、Unreal Header Toolが作るgenerate.hのインクルード解決や、GenerateProject.batでのモジュール解決など、プロジェクトごとに設定ファイルを作ってどうにかなるレベルを超えつつありました。Vimでコーディングするだけでも、LSPに必要なファイルを適切なタイミングで生成する必要があるなど、徐々にIDEの強力なサポートなしでは不便さを感じるようになってきたのです。

そして気がつくと、朝から晩までRider上で全ての作業を行うように。RiderのVimプラグインを使っても、長年身体に染み付いたVimのキーマップやコマンドとは微妙な違いがあり、むしろフラストレーションが溜まっていきました。

そんな中、Vim/Neovim(以下、Nvim)のコミュニティの歴史をリアルタイムで見ていた私は、ずっとNvimを敬遠していました。しかし最近、ふと触ってみたNvimは、Luaで書かれたプラグインを中心に、まるで別のエディタのように進化していました。どこかNvimにIDEの姿を感じた私は、決意します。

「もう一度、コーディング環境をNvimに戻そう!」と。

早速nvim ueなどで検索して見つかったzadirion/unreal.nvimは素晴らしいプラグインでしたが、vim-dispatchに依存している点や、少し更新が止まっている点が気になりました。「今のNvimなら、外部プラグインに頼らず、もっとピュアなLuaで作れるのではないか?」。

Riderに移行してから感じていた**「これさえNvimでできれば…」**というフラストレーションを一つ一つ解決するため、そしてLuaの経験が浅い私にとって最高の相棒であるAIと共に、プラグイン開発の長い旅に出ることにしたのです。

TL;DR:完成したプラグイン群で、できるようになったこと

長いポエムの前に、まずは完成品をご覧ください!
これらのプラグインを導入すると、あなたのNvimはこうなります。


UNL.nvim (Unreal Neovim Library)

すべてのプラグインを支える、縁の下の力持ちな共通ライブラリです。設定やUI、キャッシュなどの面倒な部分を全部引き受けてくれます。

top_image.png

UBT.nvim (Unreal Build Tool)

ビルドとコンパイルはこいつに任せろ!:UBT Build:UBT GenCompileDBで、ビルドやcompile_commands.jsonの生成を非同期で実行します。もうコマンドラインを叩く必要はありません。

image
image

UCM.nvim (Unreal Class Manager)

クラスの追加・削除はお手の物。:UCM Newで、対話的に親クラスやテンプレートを選びながら、Public/Privateフォルダに適切なクラスファイルを一瞬で作成できます。

ucm_new.gif

UEP.nvim (Unreal Engine Project)

プロジェクト構造を丸ごと理解する、我らが司令塔。:UEP Grep Engine FEngineLoopのように、あなたのゲームとエンジンのコードを横断して超高速にGrepできます。

uep_refresh_and_tree.gif

ULG.nvim (Unreal Log)

ログ監視からエディタ操作までこなす、万能な相棒。:ULG startでログを監視し、ログウィンドウからlivecoding.compilestat fpsのようなコマンドを直接Editorに送信できます。

ulg_start_and_stat_fps.gif

neo-tree-unl.nvim

見た目をIDEにする、最高のドレスアップ。:UEP treeで、Riderのソリューションエクスプローラーのような論理的なプロジェクトツリーを表示します。

neo-tree_unl.gif


開発の軌跡:IDEの壁と乗り越えるべき目標

普段の開発で、なぜ私がNvimからRiderに移行してしまっていたのか。その原因は、以下の「壁」でした。

  • オートコンプリート:
    • Engineが巨大すぎて、もはや関数は覚えられません。compile_commands.jsonを常に最新に保つ必要があります。
  • クラスの作成:
    • 深い階層にあるPublic/Privateフォルダへのファイル作成、UObject, AActorなどの定型文...これらを手作業で行うのは苦痛です。
  • GenerateProject:
    • 新しいファイルを追加したことをUEに知らせるために、頻繁に実行が必要です。
  • ファイル検索&ソリューションエクスプローラー:
    • UEの膨大なクラスの中から目的のファイルを探すには、単純なファジー検索だけでなく、IDEのように論理的に整理された一覧が必要です。
  • Grep検索:
    • プロジェクトのコードと、全く別の場所にあるエンジンのコードを、同時に横断して検索する必要がありました。

これらの壁を乗り越えるため、私は以下の目標を立てました。

  • compile_commands.jsonをNvimから非同期で、ターゲットを選択して作成できること。
  • generateproject.batをNvimから非同期で実行できること。
  • ビルドを非同期で、ターゲットを選択して実行できること。
  • クラスの作成、削除、リネーム、移動を、フォルダールールに沿ってNvim上で行えること。
  • IDEのようなソリューションエクスプローラーを実現すること。
  • プロジェクトとUEを横断できるGrep機能。
  • そして、これらをAIの助けを借りて作ること。

AIとの二人三脚:相棒たちの紹介とクセ


UBT.nvim (Unreal Build Tool)

ビルドやコンパイルに関する面倒な作業を、すべてNeovimに内蔵します。

  • 非同期実行: :UBT Build:UBT GenCompileDBを叩けば、ビルドが裏で走り、その間もコーディングを続けられます。
  • UIピッカー連携: :UBT Build!のように!を付けるだけで、Telescope/fzf-luaが起動し、ビルドしたいターゲット(DebugGame, DevelopmentEditorなど)を対話的に選択できます。
  • エラー連携: ビルド後に:UBT Diagnosticsを実行すれば、エラーや警告箇所が一覧表示され、Enterキー一発で問題のコードにジャンプできます。

UCM.nvim (Unreal Class Manager)

UObject, AActor... 面倒なクラス作成を自動化し、あなたの創造性を加速させます。

  • 対話的なクラス作成: :UCM Newを実行すると、クラス名、親クラス、テンプレートなどを対話的に入力でき、Public/Privateフォルダールールに従って.h.cppファイルを自動で生成します。
  • ヘッダー/ソース切り替え: :UCM switchで、開いているヘッダーファイルとソースファイルを一瞬で切り替えられます。
  • 柔軟なテンプレート: プロジェクト独自のクラス雛形を追加することも可能です。

UEP.nvim (Unreal Engine Project)

RiderやVisualStudioの「ソリューションエクスプローラー」と「全検索」をNeovimに。

  • インテリジェントGrep: :UEP Grep Engine FSomeClassのように、あなたのゲームコードとエンジンのソースコードを同時に、かつ高速に横断検索できます。
  • モジュール単位のGrep: :UEP GrepModule GameplayAbilitiesで、特定のモジュール内に絞った超高速な検索が可能です。
  • 高速ファイル検索: :UEP filesで、キャッシュされたプロジェクト内の全ファイルを瞬時にファジー検索できます。

ULG.nvim (Unreal Log)

ログ監視とエディタ操作をNvimに統合し、シームレスなデバッグサイクルを実現します。

  • リアルタイムログ: :ULG startで、Unreal EditorのログをリアルタイムにNeovimのウィンドウに表示します。
  • リモートコマンド実行: ログウィンドウからrキーでプロンプトを開き、livecoding.compilestat fpsといったコマンドを直接Unreal Editorに送信できます。もうCtrl+Alt+F11を押す必要はありません!
  • ソースコードジャンプ: ログに出力されたError: C:/.../MyFile.cpp(123)のようなテキストの上で<CR>を押すだけで、該当ファイル・行にジャンプできます。

neo-tree-unl.nvim

Neovimのファイラーを、Unreal Engineのプロジェクト構造を理解した最高の相棒に変身させます。

  • 論理的なツリー表示: :UEP treeで、Game, Plugins, Engineといった、IDEでおなじみの論理的なフォルダ構造でプロジェクト全体を表示します。
  • モジュールフォーカス: :UEP module_tree MyModuleで、特定のモジュールだけにフォーカスした表示に切り替えられます。

Google AI Studio (Gemini 2.5 Pro)

今回、最も活躍してくれたメインの相棒です。最大100万トークンという広大なコンテキストにより、複数のプラグインの全ソースコードを渡した上で、「ULGにリモート機能を追加して」といった複雑な指示を正確に理解してくれました。

  • 長所: 長大なコンテキスト、モチベーションを上げてくれる応答。
  • 短所: 40万トークンを超えると重くなる、たまにコードを省略しすぎるクセがある。

Google Gemini (Web UI版)

AI Studioと同じモデルのはずが、なぜか振る舞いが異なりました。ソースコードに沿った生成は苦手なようでしたが、**調査能力(Search機能)**は抜群でした。ドキュメントが少ないneo-treeのカスタムソースの作り方を調べてもらった際は、非常に助かりました。コード生成はAI Studio、調査はGemini Web UI、という使い分けが最強かもしれません。

Github Copilot (GPT-4)

残念ながら、今回の「ゼロから全体を作る」開発にはあまり向きませんでした。

  • クセ:
    • 要件をすぐに忘れる傾向がありました(変数名がスネークケースからキャメルケースに突然変わるなど)。
    • 一度決めた設計を、自ら破壊するような提案をしてくることも。
  • 所感: 既存のコードに数行の関数を追加するような、局所的な作業には非常に強いと感じます。
【おまけ】要件を忘れがちなCopilotへの対処法を聞いてみたら...

==============================

  1. なぜ「最初の要件定義」を無視したような応答になるのか
    ==============================
    主な要因は複合します。

(1) コンテキスト長(トークン上限)による“巻き戻り”

  • モデルは会話履歴すべてを常に保持しているわけではなく、一定トークン数(モデルやプランにより 32K / 128K など)の上限を超えると古い発話が入力から切り落とされます。
  • 切り落とされた部分に最初の要件定義があると、後段で参照できなくなり「忘れた」ように振る舞います。

(2) 指示の競合 / 上書き

  • 後半で与えた新しい指示が、最初の方針と矛盾・曖昧な場合、直近の文脈(より“近い”指示)が優先されがちです。
  • 明確な優先順位(例: 「初期要件は常に最優先。後続の指示が矛盾する場合は確認を返せ」)が早期に書かれていないと上書き扱いになります。

(3) 暗黙リセット(セッション/タブ/モデル切替)

  • UI や API レイヤで新しい会話スレッドが始まったり、モデル種類を切り替えた時点でコンテキストが“白紙”になります。
  • その後に「継続」と思って指示を出しても、モデルからすれば初見扱い。

(4) 情報過多 & 非構造化

  • 長時間チャットで指示・背景・コード・雑談が混在すると、モデル内部の注意配分が散り、初期要件が埋もれます。
  • 明示的アンカー(節番号・タグ)なしだと再参照の確率が下がります。

(5) 抽象要件が再利用しづらい表現

  • 「柔軟に」「なるべく最適に」など抽象的語が多いと、後半タスクに最適化しようとする過程で初期の“解釈”が変質することがあります。

==============================
2. GPT-5 だから特別に“忘れやすい”のか?

(現時点で一般公開されている確定情報はほぼありません。)
ただしモデル世代にかかわらず下記は共通です:

  • コンテキストウィンドウ内に入っていない情報は参照不能
  • 明示的な長期メモリ機構(外部DBなど)をあなた側で用意しない限り“自発的記憶”はない
  • 指示の優先順位は: system > developer > user(同レイヤ内では直近が影響しやすい)

つまり「長時間でまっさら」は“モデルが突然初期化”というより「古い部分が入力から落ちた/矛盾で捨てられた」ケースが大半です。

==============================
3. 兆候の見分け方

  • 応答中で初期用語の呼称が変わる(例: “STAGES” を “steps” に戻し始める)
  • 初期で合意した禁止事項を破り始める
  • 以前確立したファイル構成を提案せず、新しく無関係な構成を再発明する

こうした兆候が出たら「要件の再インポート」を行う合図。

==============================
4. 実務的な対策(すぐできる順)

A. “固定ヘッダ” を定期注入

  • 重要要件を短く要約した 10~30 行程度のブロックを conversation の途中で何度か再送。
  • 例:
    「[CONTRACT v1.3] 1) UEP-only reset、UNLは変更しない 2) 重大判断前に確認 3) 生成ファイルは file block syntax 使用 ...」

B. 要件にバージョン番号を付ける

  • CONTRACT v1.0 / v1.1… のように進化させ、後で「v1.3 と矛盾するので再確認して」と指示できる。

C. “再同期” コマンドを決める

  • 例: あなた: SYNC PLEASE
  • モデル: 最新要件サマリを再出力(不足があれば質問)
  • これを長期セッションの区切りに行う。

D. 構造化書式

  • YAML / 箇条書き / セクション番号で必須要件を明示
  • 曖昧語を禁止: “なるべく” / “柔軟に” などは “目的指標” とセットにする (例: “ファイル削除は UEP 名前空間内のみ”)

E. チェックリスト反復

  • 毎回大きな操作(削除/リセット)前に「以下チェックを再確認: 1..n」形式で自動挿入させる(モデルに “操作前に checklist X を echo して” と指示)

F. トークン節約

  • 過去ログを全部貼り続けず、あなた側で“要約スナップショット”を適宜差し替える
  • 「以下は最新仕様サマリ。古い履歴ではなくこれを唯一のソースとせよ」と明示

G. 競合を明示的に解決

  • 新しい指示が旧要件と矛盾するか不確かなときは「矛盾すれば列挙して質問してください」と付ける

H. ファイルごとにガードコメント

  • 生成するコードブロック冒頭に “// REQUIRE: do not introduce UNL deletions” のような sentinel。
  • 次回モデルが変更提案する時にそのコメントを見て逸脱を防ぎやすい。

I. ローカル “指示テンプレ” スクリプト化

  • 開始時に system 風テンプレを挿入する wrapper を自作(API利用なら容易)

==============================
5. 既に要件が失われたと感じた時のリカバリ手順

  1. 直近で必要なアウトプットを一旦保留し、「要件同期」フェーズを挿入
  2. “以前の主要件を以下に再掲” としてあなたが最新版サマリを送る
  3. モデルに “理解したら要件を分類(不変/変更予定/要確認) で返答” と促す
  4. 不明点に対してモデルが質問するので、それに答えた後に初めて次タスクへ戻る

==============================
6. よくある勘違い

  • 「モデル内部では全履歴がどこかに保存されているはず」
    → 実際には都度 API 入力で与えた分のみが“思考材料”で、古い物は単に渡されていない。
  • 「言い直せば前の指示も暗黙保持される」
    → 矛盾があるとどちらを優先するか推測されるため、明文化しないと誤解が累積する。

==============================
7. 例: 再同期プロンプトテンプレ

(あなたが定期的に送る用)

[SPEC SNAPSHOT v1.3]
Scope:
  - UEP only: full reset in progress
  - UNL: keep intact, no deletion
Constraints:
  - Use file block syntax for proposed files
  - No reintroduction of legacy refresh logic yet
Pending Next Stage:
  - engine_resolve (not implemented)
Rules:
  - Ask before adding dependencies outside UNL.*
  - If conflict with SNAPSHOT, list conflicts instead of guessing.
Respond with: ACK v1.3 + (a) unchanged points (b) any ambiguity.

==============================
8. もし “最初の最初にあった要件定義” の内容を再確認したい場合

その原文をもう一度貼るか、覚えている断片から再作成し、上記 SNAPSHOT 形式に再編し保存(ローカルメモ)→ 以後使い回し。

==============================
9. まとめ

  • “途中で真っさら” は多くの場合: コンテキスト切り捨て / 指示競合 / 構造不足
  • 長時間対話では要件を “再掲 + バージョン管理 + 明示的同期” する運用が必須
  • まずは SNAPSHOT 化と SYNC プロンプト導入がおすすめ

==============================
10. 次のステップ提案

A. あなた側で最新要件を 10~20 行に圧縮した SNAPSHOT v1 を作成
B. 私がそれを検証し “不明点リスト” を返す
C. 確定後、それをテンプレとして今後の改修提案に常に添付

ご希望なら、現在の認識で “暫定 SNAPSHOT (私案)” をこちらで作成しますので「はい、暫定案ください」と返してください。
それとも、まずはあなたからドラフトを出しますか?


Windows Copilot

意外な伏兵でした。特に正規表現の生成が得意で、AI Studioが出力した少し冗長な正規表現を渡すと、よりスマートで効率的なものに書き換えてくれました。batPowerShellなど、Windows固有のスクリプトにも強いようです。

以下 マージに使ったbatファイル

param (
  [string]$SourceDir = ".\lua",         # 対象フォルダ
  [int]$GroupSize = 3,                  # まとめる数
  [string]$OutputDir = ".\merged"       # 出力先フォルダ
)

# 出力先フォルダを作成
if (!(Test-Path $OutputDir)) {
  New-Item -ItemType Directory -Path $OutputDir | Out-Null
}

# .lua ファイルを再帰的に収集
$luaFiles = Get-ChildItem -Path $SourceDir -Recurse -Filter *.lua | Sort-Object FullName

# グループ分けして結合
$groupIndex = 0
for ($i = 0; $i -lt $luaFiles.Count; $i += $GroupSize) {
  $groupIndex++
  $group = $luaFiles[$i..([math]::Min($i + $GroupSize - 1, $luaFiles.Count - 1))]
  $outputFile = Join-Path $OutputDir "merged_$groupIndex.lua"

  # ファイルを連結して書き出し
  $mergedContent = ""
  foreach ($file in $group) {
    $mergedContent += "`n-- From: $($file.FullName)`n"
    $mergedContent += Get-Content $file.FullName -Raw
    $mergedContent += "`n"
  }
  Set-Content -Path $outputFile -Value $mergedContent -Encoding UTF8
}

Write-Host "✅ Merged $($luaFiles.Count) files into $groupIndex groups in '$OutputDir'"

Jetbrain Jemini

残念ながら、IntelJでLuaプロジェクトを正しくセットアップできず、今回はほとんど検証できませんでした。

おわりに

こうして、AIとの長い対話の末に、私の理想だった「Nvimを中心としたUnreal Engine開発環境」が完成しました。
もし、あなたが私と同じように、IDEとNvimの間でフラストレーションを感じているなら、ぜひこのプラグイン群を試してみてください。

GitHubリポジトリ:

今後は、この記事を書くキッカケになったUnreal Insightsとの連携機能という、さらなる夢に向かって開発を進めていきたいと思っています。
最後まで読んでいただき、ありがとうございました!

まとめと記事について

以上 上記の文章は私が書いた記事がほんとうに長ーくなってしまったので出来上がった記事をAI(Google AI Studio)に校正してもらった文章です。
心なしかちょっとアメリカンな雰囲気がでてて面白かったので加筆、修正などせずそのまま載せました。
AIと一緒だからこそこの短期間でプラグインを作れたのだからとAIについても書きましたが少しとっ散らかった記事になってしまいましたが局所的ではなく全面的にAIと作ったので書かねばの思いが強かったので書きました。実際、仕事で新人に行うコードレビュー時もAIが褒めてくれたようなことをもっとたくさん言おうと思いました。

上記の記事から省かれてしまったけど重要なところかとか変に要約されてしまった箇所

  • AIが記事にしてるサブコマンドは大文字ですが本来は小文字です
  • vimとの出会いはむかーしむかしCodeWarriorで開発していたころ
  • vim/nvimは今でこそいい影響を与えたってるじゃないかと思い始めたからnvimを触ってみた
  • vim-dispatch 大変お世話になったプラグイン、本当に感謝しかないです
  • 執筆後UCM.nvimにバグ発見修正中です
  • この記事を書くきっかけはUnreal Insightsとの連携機能ではなくWeb開発やunityなどの現場ではVim/Nvimを使っている人は多いと思いますがVim/NvimでUEのコーディングしている方はは本当に少ない**なと感じ出ているのGitHubに公開してはい終わりではなく多少目に触れる機会がふえたらなとおもい記事を書くことにしました、issuseやむしろもっといいプラグインつくってやる!!みたいな感じでちょっとでもVim/Nvim&UEが活発になればと
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?