PR
[PR]アフィリエイト広告を利用しています
[PR]アフィリエイト広告を利用しています

【中級★★☆】BizHawkでLuaスクリプトが動かない時の切り分け7項目|エラーの読み方とAIに直させる手順

BizHawkでLuaが動かない時の切り分け7項目を表したアイキャッチ画像 Lua・自動化プログラミング

【難易度】中級★★☆【対象環境】Windows 10(64bit)・Windows 11/BizHawk 2.9系〜2.10系(2026年8月時点の配布版)/Lua Console 同梱版・オフライン/動作させるのは自分で購入・所有しているソフトのみ

【PR】本記事はアフィリエイトリンク(Amazonアソシエイト)を含みます。Amazonのアソシエイトとして、当ブログは適格販売により収入を得ています。

【技術検証に関する法的注意事項・免責事項】
本記事に掲載されているメモリ解析、Luaスクリプト実行、およびAIコード生成に関する技術情報は、自らが所有するゲームソフトおよびオフライン環境における技術研究・教育目的の検証記録です。著作権法第47条の3に配慮し、複製ROMの配布、コピーガードの回避(DRM解除)、オンライン環境での不正操作(BOT化)等に関する情報は一切取り扱いません。掲載情報の実行によって生じた損害について、当ブログは一切の責任を負いかねます。

結論を先に書きます。BizHawkでLuaが動かない原因は、ほぼ7つに収まります。しかも見る順番が決まっています。①コンソールが開けるか → ②開いた瞬間に落ちないか → ③スクリプトが「実行中」になっているか → ④画面が1フレームでも進んでいるか → ⑤描画を毎フレーム呼んでいるか → ⑥参照している記憶領域が合っているか → ⑦その関数名がこのバージョンに存在するか。この順に上から潰してください。

順番が大事な理由は1つです。下の層から先に触ると、原因が混ざって切り分けが効かなくなるからです。たとえば「値が0のまま」を疑ってアドレスを書き換え続けても、そもそもスクリプトが実行状態になっていなければ何を変えても結果は0のままです。逆に、上の層から順に見れば、どこで止まったかがそのまま原因になります。

この記事は手が止まった状態から復帰するための記事です。新しく何かを作る手順は書きません。書くのは「いま出ている症状から原因の層を決める方法」と「エラー文をAIに渡して直させる時の渡し方」の2つです。

なお当ブログは実測値を出すときは自分で計測した記録だけを載せる方針です。この記事の公開時点ではエラー画面の実測キャプチャと動作GIFの取得が終わっていないため、画像は概念図のみです。取得でき次第この記事に追記します。ここに書いてあるのは「この症状ならここを見る」という切り分けの設計までで、「筆者の環境でこう出た」という報告はしていません。

Luaが動かない原因を起動の層・読み込みの層・実行結果の層の3層に分けた切り分け順序の概念図
切り分けは3層に分かれます。A(起動)→B(読み込み)→C(実行結果)の順に見て、通過した層はもう疑いません。番号は本文の見出し番号と対応しています。

前提:BizHawkでLuaが動かない時に、この記事が想定している状態

次の3つが済んでいる人向けです。どれか欠けている場合は、この記事より先に該当記事へ戻ったほうが早く終わります。

前提 確認方法 欠けている場合
BizHawk本体が起動する EmuHawkのウィンドウが出る 本体の入れ方から戻る(記事末尾のリンク)
自分が所有するソフトを読み込めている 画面に映像が出て操作できる コアの対応状況を確認
Luaファイルを1つ書いた(または生成した) 拡張子 .lua のファイルがある 本記事の§8のスクリプトを使う

※本記事のコードとメモリアドレスは、自分で所有しているソフトをオフラインで検証することを前提にしています。特定タイトルの確定アドレスは掲載していません(後述の 0x00XXXX は書き換え用のプレースホルダです)。

§1 コンソールが開けない・メニューに項目が見えない

症状Tools メニューを開いても Lua Console が見つからない、または押せない状態になっている。

原因候補は3つです。

  • ソフトを読み込む前にメニューを開いている。BizHawkは、何も読み込んでいない状態では使えない項目が出てきます。
  • EmuHawk以外の実行ファイルを起動している。配布物には複数のexeが入っています。Luaを使うのはEmuHawk側です。
  • 使用中のコアがスクリプト実行に対応していない。BizHawkは機種ごとに内部のコアを切り替えており、対応状況はコアによって差があります。

確認手順:①先にソフトを読み込む → ②タイトルバーに機種名とコア名が出ているかを見る → ③その状態で Tools を開き直す。

この症状ならここで止まる判別条件:ソフトを読み込んだ直後にコンソールが開けたなら、原因は「読み込み前に押していた」だけです。ここが通ったら、§1はもう疑いません。

§2 コンソールを開いた瞬間に落ちる・DLLが無いと言われる

症状:Lua Console を開いた瞬間にBizHawkごと終了する、または「必要なファイルが見つからない」系のダイアログが出る。

原因候補:実行に必要な共通ランタイムが入っていない、64bit版と32bit版を混ぜている、あるいはzipを展開せずに中から直接起動しているのいずれかです。3つ目は見落としやすく、しかも症状が「落ちる」なので原因を誤解しやすいところです。

確認手順

  1. 配布物をフォルダへ完全に展開してから起動しているか(zipの中身をダブルクリックしていないか)
  2. 配布物に同梱されている前提ランタイムのインストーラを実行したか
  3. OSが64bitか(システム情報で確認)
  4. 展開先のパスに日本語や特殊文字が入っていないか

直し方:4つのうち当てはまったものを直したうえで、いったんBizHawkを終了してから起動し直します。ランタイムを入れた直後は再起動しないと反映されないことがあります。

判別条件:コンソールのウィンドウが開いて、入力欄とログ欄が見えている状態になったら§2は通過です。ここまでが「A:起動の層」です。

§3 スクリプトを開いたのに何も起きない・ログが空のまま

症状:ファイルを開いてリストに名前は出ているのに、画面にもログにも何も出ない。

原因候補で圧倒的に多いのがこれですリストの左端のチェックボックスがOFFになっている。BizHawkのコンソールは「開く」と「実行する」が別操作です。開いただけでは走りません。

次に多いのがファイルパスの問題です。日本語フォルダ名、深い階層、クラウド同期フォルダ配下などは、読み込めても実行時に失敗することがあります。切り分けの段階では C:\lua\ のような浅くて半角のみのフォルダに置いてください。

確認手順:①リスト行のチェックが入っているか → ②入っていなければ入れる(またはトグル操作で入れ直す) → ③それでも無反応なら、ファイルを浅い半角パスへ移して開き直す。

判別条件:ログ欄に1行でも文字が出たら§3は通過です。逆に、ここでエラー文が出たなら、それは大きな前進です。エラーが出るのは実行されている証拠なので、以降は§7の読み方に進めます。

§4 BizHawk全体が固まって応答しなくなる

症状:実行した瞬間にウィンドウが白くなる、タイトルバーに「応答なし」が出る、強制終了しか手がなくなる。

原因はほぼ1つです。繰り返し処理の中でエミュレータに制御を返していないこと。Luaスクリプトはエミュレータと同じ流れの中で動くため、ループから抜けずに回し続けると、映像の更新も入力の受け取りも止まります。

「エミュレータに制御を返す」というのは、1フレーム進めてから続きを実行するという指示を書くことです。これが無いループは、本体ごと止めます。

-- 固まる書き方(ループの中でフレームを進めていない)
while true do
  -- 何か処理
end

-- 固まらない書き方(毎回1フレーム進める)
while true do
  -- 何か処理
  emu.frameadvance()
end

判別条件:無限ループの末尾に emu.frameadvance() があるか。無ければ入れる。あるのに固まるなら、ループが二重になっていて内側に無い可能性を見てください。ここまでが「B:読み込みの層」です。

§5 実行されているのに画面に何も出ない・一瞬で消える

症状:ログには動いた記録が出るが、ゲーム画面には文字が出ない。または一瞬だけ出て消える。

原因候補は3つです。

  • 描画をループの外で1回だけ呼んでいる。画面への描画は基本的に1フレーム分しか残りません。毎フレーム呼び直す必要があります。
  • 座標が画面の外。切り分け中は (10, 10) のような左上の安全な座標に固定してください。
  • 描画先のレイヤ設定が想定と違う。表示領域を広げる設定を入れている場合、想定と違う場所に描かれることがあります。
-- 出ない書き方(ループの外で1回だけ描いている)
gui.drawText(10, 10, "test")
while true do
  emu.frameadvance()
end

-- 出る書き方(毎フレーム描き直す)
while true do
  gui.drawText(10, 10, "test")
  emu.frameadvance()
end

判別条件:固定文字列 "test" が画面左上に出続けたら、描画の経路は正常です。ここが通れば、残るのは「何を読んでいるか」だけになります。

§6 値が常に0になる・ありえない数字になる

症状:描画自体は出ているのに、読み出した数値が0のまま、または明らかに範囲外の巨大な数になる。

原因候補は2つです。

ひとつは参照している記憶領域(メモリドメイン)が違うこと。BizHawkは機種ごとに複数の領域を持っており、どこを基準に数えるかでアドレスの意味が変わります。何も指定しないと既定の領域が使われるため、別の領域を基準にしたアドレスを渡すと当然ずれます。

もうひとつは読み出す幅とバイト順が違うこと。1バイトで足りる値を2バイトで読むと桁が混ざり、2バイト値を1バイトで読むと上限で止まります。並び順(リトルエンディアン/ビッグエンディアン)も機種で異なります。

-- 領域の一覧と、いま使っている領域を確認する
for _, d in ipairs(memory.getmemorydomainlist()) do
  console.log(d)
end
console.log("now: " .. memory.getcurrentmemorydomain())

-- 領域を明示してから読む("WRAM" は機種により名称が異なる)
memory.usememorydomain("WRAM")

-- 幅を変えて見比べる(アドレスは自分の環境で特定した値に置き換える)
local a = 0x00XXXX
console.log(memory.read_u8(a))
console.log(memory.read_u16_le(a))
console.log(memory.read_u16_be(a))

0x00XXXX書き換え用のプレースホルダです。当ブログでは、自分で確認していない具体的なアドレスを載せません。自分の環境で候補を絞り込む手順は、記事末尾の関連記事にまとめています。

判別条件:3つの読み方のどれかで、ゲーム内の表示と連動して変化する値が出れば領域と幅は合っています。全部0のままなら、アドレスそのものが違う段階なので、絞り込みの工程に戻ってください。

§7「attempt to call a nil value」が出る

症状:ログに attempt to call a nil value (field '○○') と出て止まる。

これは最も情報量が多い、当たりのエラーです。意味は単純で、「○○ という名前の関数が存在しない」。存在しない理由は2つに絞れます。

  • バージョン間で名前が変わった。BizHawkは更新のたびに関数名や引数の並びが変わることがあります。古い記事のコードをそのまま使うと、ここで止まります。
  • そのコアには無い関数を呼んでいる。機種依存の機能は、対応していないコアでは存在しません。

確認手順:エラー文の field '○○' の中身を読み、コンソールに付属する関数一覧(Functions List/ヘルプ)で同じ名前が実在するかを確認します。似た名前が並んでいれば、それが現行の名前です。

直し方:AIに直させるのが最も速い場面です。次の節でその渡し方を書きます。

§8 エラー文をAIに直させる:渡し方で結果が変わる

当ブログの主題はここです。エラーをそのまま投げるか、環境ごと投げるかで、返ってくるコードの精度が変わります。

やりがちな失敗は「このコードが動きません、直してください」だけを送ることです。AIはバージョン差を知らないので、また別のバージョンの書き方を返してきます。直させたいのは「このバージョンで動く形」なので、環境と、実際に出たエラー文と、自分が確認できた事実を先に渡します。

実際に使うプロンプト(コピペして角括弧を埋める)

あなたはBizHawkのLuaスクリプトのデバッグを担当します。

【環境】
- BizHawk [バージョン] / Windows [10 or 11] 64bit
- 機種・コア: [タイトルバーに出ている表記]
- 対象は自分が所有するソフト、オフラインで検証

【いま出ているエラー(原文のまま)】
[ログ欄をそのままコピー]

【自分で確認できたこと】
- コンソールは開ける/リストのチェックはON
- ループ末尾に emu.frameadvance() がある
- 固定文字列の描画は画面に出続けている
- console.log は動いている

【やりたいこと】
[例: 指定アドレスの値を毎フレーム画面に出したい]

【依頼】
1. このエラーの原因を1行で説明してください。
2. このバージョンに実在する関数名だけを使って、コード全体を書き直してください。
3. 存在するかどうかが確認できない関数を使う場合は、その旨を明記してください。
4. 動作確認の手順を3ステップで書いてください。

ポイントは【自分で確認できたこと】です。§1〜§6を通過した事実をここに書くと、AIは通過済みの層を疑わなくなり、回答が一発で絞られます。切り分けの記録が、そのままプロンプトの材料になるという関係です。

返ってきたコードをそのまま貼らずに、まず次の最小スクリプトで環境側が生きているかを確かめてください。

§9 コピペ用:切り分け専用の最小確認スクリプト

このスクリプトは何かを実現するためのものではなく、環境が生きているかを確かめるためだけのものです。上から3つの出力が揃えば、§1〜§5は通過済みだと確定できます。

-- 切り分け用 最小確認スクリプト(自己所有ソフト・オフライン前提)
-- 目的: ①実行されているか ②描画が出るか ③領域が取れるか を1度に確認する

console.clear()
console.log("=== check start ===")
console.log("domain list:")
for _, d in ipairs(memory.getmemorydomainlist()) do
  console.log("  - " .. d)
end
console.log("current domain: " .. memory.getcurrentmemorydomain())

local frame = 0
while true do
  frame = frame + 1

  -- ② 画面左上に固定文字列とフレーム数を出し続ける
  gui.drawText(10, 10, "ALIVE " .. frame)

  -- ③ 値の読み出し(アドレスは自分の環境で特定した値に置き換える)
  local a = 0x00XXXX
  local v = memory.read_u8(a)
  gui.drawText(10, 30, "u8@0x00XXXX = " .. v)

  -- 最初の1回だけログにも出す(毎フレーム出すとログが流れて読めない)
  if frame == 1 then
    console.log("first read = " .. v)
  end

  emu.frameadvance()
end

読み方

  • ログに === check start === が出ない → §3(実行されていない)
  • ログは出るが画面に ALIVE が出ない → §5(描画の呼び位置)
  • ALIVE の数字が増えない → §4(フレームが進んでいない)
  • 数字は増えるが読み出し値が常に0 → §6(領域・幅・アドレス)
  • 途中で nil value で止まる → §7(関数名)

この5行が、この記事のいちばん短い要約です。

手を動かして覚えるための書籍

切り分けが早くなるかどうかは、結局のところ「その言語のエラー文を読み慣れているか」で決まります。Luaは仕様が小さい言語なので、1冊通すだけで nil 周りの挙動が体に入り、§7のようなエラーが「読めば分かる」ものに変わります。


Luaを腰を据えて学ぶなら
Programming in Lua(プログラミング言語Lua公式解説書)

Lua作者陣による公式解説書。BizHawkのLua APIは断片的な情報が多く、言語そのものの仕様(テーブル・メタテーブル・コルーチン)を一度通しで押さえておくと、スクリプトが動かないときの原因切り分けが速くなる。

  • 言語作者による公式解説
  • テーブルとメタテーブルの理解が進む
  • 断片的なコピペから抜け出せる

Amazonで詳細を見る

※価格・在庫・仕様は変動します。最新の情報はAmazonの商品ページでご確認ください。

さらに一歩進んで、なぜその領域にその値が置かれているのかを構造から理解したい場合は、解析側の入門書が近道になります。§6でつまずく回数が目に見えて減ります。


Ghidraを触るなら
リバースエンジニアリングツール Ghidra実践ガイド

本ブログで扱うGhidraそのものを主題にした日本語書籍。逆アセンブル結果の読み方や関数の当たりの付け方が体系立っているので、記事の手順をなぞるだけの段階から一歩進める。

  • Ghidraを主題にした日本語書籍
  • 逆アセンブル結果の読み方が体系的
  • 記事の手順と地続きで学べる

Amazonで詳細を見る

※価格・在庫・仕様は変動します。最新の情報はAmazonの商品ページでご確認ください。

よくある質問

Q. 昨日まで動いていたスクリプトが、今日は動きません。

A. まずBizHawkを更新したかどうかを思い出してください。更新していれば§7(関数名の変更)が第一候補です。更新していない場合は、読み込んでいるソフトや機種が変わっていないかを確認します。コアが変われば使える機能も変わります。

Q. エラーが出ないのに何も起きません。これはどこを見ますか。

A. §3です。エラーが出ないのは「実行されていない」ことの典型的な症状です。リストのチェックがONかを最初に見てください。

Q. AIに書かせたコードが、毎回違う関数名を使ってきます。

A. プロンプトにバージョンを書いていない可能性が高いです。§8のテンプレートの【環境】を埋めてから投げ直してください。それでも揺れる場合は、依頼文に「実在が確認できない関数は使わず、その旨を明記する」を必ず残してください。

Q. アドレスはどうやって決めればいいですか。

A. この記事の範囲外です。候補を絞り込む工程は別記事にまとめてあるので、記事末尾のリンクから進んでください。当ブログでは、自分で確認していない具体的なアドレスは掲載しません。

Q. スクリプトやソフト本体はどこで入手できますか。

A. ROM・BIOS・改変データの入手先やその配布については、当ブログでは一切取り扱いません。いわゆるチートや改造を目的とした情報も扱いません。検証の対象は、自分で購入し所有しているソフトに限られます。この方針は全記事で共通です。

まとめ:BizHawkでLuaが動かない時に見る順番

  • 原因は7つに収まり、見る順番が決まっている。上から潰せば、止まった場所がそのまま原因になる
  • A(起動)=§1・§2、B(読み込み)=§3・§4、C(実行結果)=§5〜§7。通過した層は二度と疑わない
  • 「何も起きない」の最頻値はリストのチェックがOFF。「固まる」の最頻値はループ内でフレームを進めていない
  • nil value は当たりのエラー。関数名がこのバージョンに存在するかを一覧で確認する
  • AIには環境・エラー原文・通過した層の3点を渡す。切り分けの記録がそのままプロンプトになる
  • 実測のエラー画面と動作GIFは、計測でき次第この記事に追記します

【予告】この記事の切り分け手順と最小確認スクリプトをまとめた「コピペで動くLua集+環境構築キット」を配布予定です。価格・配布日は未定で、決まり次第この記事に追記します。

次に読む

切り分けが終わったら、次は全体のどこにいるのかを確認してください。この記事は、画面の内側の数値を読んで実験するまでの道筋の一部です。工程表がこちらにあります。

【中級★★☆】ゲームのメモリ解析は5ステップで内部の数値が読める|BizHawk×Lua×AI 手順の全体像
ゲームのメモリ解析は5工程しかありません。①読み出せる状態をつくる ②目的の数値がどこにあるかを絞り込む ③読んだ数値を画面に出し続ける ④AIにコードを書かせて往復を削る ⑤自動化して実験する。この記事は手順そのものではなく「いまどの工程にいるか」「次にどの記事を開くか」を示す地図です。開始地点を事実だけで判定する4問チャート付き。自己所有ソフト・オフライン環境が前提です。

そもそもBizHawk本体の準備段階でつまずいている場合は、こちらへ戻ってください。ここが通っていないと、§1・§2はどうしても抜けません。

【入門】BizHawk 導入・使い方 完全ガイド(Windows)|ダウンロードから日本語化の正解・Lua Console初期設定まで最短4ステップ
BizHawkの導入は「公式GitHubから本体zip+prereqsを入手→prereqsを先に実行→半角英数パスへ展開してEmuHawk.exeを起動→コントローラー割当とLua Console初回起動」の4ステップ。公式の日本語UIは無いため、日本語化の正解は「日本語パスを避ける・対訳表を持つ・表示は半角英数」の3点です。Claudeに動作確認用Luaを書かせた試行錯誤ログと、英語メニュー対訳表つき。

動く状態に戻ったら、いちばん短いコードから組み直すのが確実です。

【入門】BizHawk×Lua×AI(Claude)でゲームのメモリ値を画面表示する最小テンプレ|RAM Searchで所持金アドレスを特定する手順
BizHawkのRAM Searchで所持金アドレスを特定し、Claudeに書かせたLuaで値を画面に常時表示する最小テンプレを解説。改造ではなくメモリ解析・状態検証の入門です。

そして、AIへの指示そのものを設計したい人はこちらです。§8のテンプレートを、より広い用途へ広げた内容になっています。

【中級】Claudeにゲーム制御Luaを書かせるプロンプト術|BizHawkで一発で動くコードを出す指示テンプレ(試行錯誤ログ付き)
AIに書かせたBizHawk用のLuaが動かない原因は、①他エミュAPIの混入 ②常駐ループ欠落 ③型・エンディアンの取り違え ④アドレスの推測記入 ⑤座標系の食い違いの5つにほぼ集約されます。使ってよいAPIの明示・入出力の型・失敗時の再指示を組み込んだコピペ可能なプロンプトテンプレ3種と、動かないコードが直るまでの試行錯誤ログを掲載。改造ではなくメモリ解析と状態検証の技術記録です。

コメント