【PR】本記事にはアフィリエイトリンク(Amazonアソシエイト)を含みます。難易度:中級★★☆/対象環境:BizHawk 2.9〜2.10(Windows 10/11・EmuHawk・Lua Console)。
【技術検証に関する法的注意事項・免責事項】
本記事にはは掲載されているメモリ解析、Luaスクリプト実行、およびAIコード生成に関する技術情報は、自らが所有するゲームソフトおよびオフライン環境における技術研究・教育目的の検証記です。著作権法第47条の3に配慮し、複製ROMの配布、コピーガードの回避(DRM解除)、オンライン環境での不正操作(BOT化)等に関する情報は一切取り扱いません。掲載情報の実行によって生じた損害について、当ブログは一切の責任を負いかねます。
BizHawkのLuaで、RAM Searchで特定したHPアドレスを読み取り、画面左上に「HPバー」として常時オーバーレイ表示する最小の手順をまとめます。使う関数はmemory.read_u16_le()(値の読み取り)とgui.drawRectangle()+gui.text()(描画)の3つだけ。ポイントは、描画を毎フレーム呼び直すことと、座標のズレ・点꼅(チラつき)をemu.frameadvance()のループ設計で解消することです。コピペで動く最終コードと、Claudeに書かせてエラーを直していった試行錯誤ログを載せます。値の「特定」がまだの場合は、下の関連記事(RAM Search編)を先に読むとスムーズです。
前提環境と、この記事でやること
この記事は「RAM Searchでアドレスはもう特定した。あとはそれを画面に見やすく出したい」という段階の人向けです。まだアドレスを探せていない場合は、下の関連記事(RAM Search編)を先に済ませてから戻ってきてください。
| 項目 | 内容 |
|---|---|
| エミュレータ | BizHawk 2.9〜2.10(EmuHawk) |
| OS | Windows 10 / 11 |
| 使う機能 | Lua Console(Tools → Lua Console) |
| 前提データ | RAM Searchで特定済みのHPアドレス(本記事では仮に 0x00XXXX と表記) |
| 難易度 | 中級★★☆(Luaの基本ループとメモリ読取が分かればOK) |
| 使用ソフト | 自分が所有するゲームソフトのみ(オフライン環境) |
※0x00XXXX はプレースホルダです。実際のアドレスはゲーム・リビジョンごとに異なるため、必ずご自身のRAM Searchで特定した値に置き換えてください。確定アドレスの実測例はオーナーの事後検証で追記します。

試行錯誤プロセス:AIにHPバーのLuaを書かせて、詰まりを直していく
ステップ1:目的を決める
やりたいことを一文にします。「特定済みのHPアドレスの値を毎フレーム読み、画面左上に数値とバーで常時表示する」。これをそのままAI(Claude)へのプロンプトにします。曖昧なまま投げると汎用的なサンプルが返るので、アドレス・読み取りサイズ・表示位置・表示形式を具体で渡すのがコツです。
ステップ2:Claudeへの実プロンプト
BizHawk(EmuHawk)のLuaで、次の仕様のスクリプトを書いてください。
- アドレス 0x00XXXX から2バイト(リトルエンディアン)でHP値を読む
- HPの最大値は仮に 9999 とする
- 画面左上(x=8, y=8)に「HP: 現在値」をテキストで表示
- その下に、最大値に対する現在値の割合で横バーを描画する
- 毎フレーム更新し続ける
- BizHawk 2.9 の client/gui/memory/emu APIを使う
ステップ3:最初に返ってきたコード(未修正)
AIは、だいたい次のような素直なコードを返してきます。まずはこれをLua Consoleで動かして、何が起きるかを見ます。
-- v1: 最初の生成コード(このあと2箇所つまずく)
local addr = 0x00XXXX
local hpMax = 9999
local hp = memory.read_u16_le(addr)
local ratio = hp / hpMax
gui.text(8, 8, "HP: " .. hp)
gui.drawRectangle(8, 26, 120, 10, 0xFF000000, 0xFF33D0FF)
gui.drawRectangle(8, 26, math.floor(120 * ratio), 10, 0xFF000000, 0xFF9BE15D)
ステップ4:動かした結果と、2つのエラー
このv1を実行すると、次の症状が出ました。
- 症状A:一瞬しか表示されない/すぐ消える。 描画はしているのに、1フレーム分で終わってしまう。
- 症状B:ゲームを進めてもバーが動かない。 HP値が変わっても表示が更新されない。
原因はどちらも同じで、「1回だけ描いて終わっているから」です。BizHawkのgui.text/gui.drawRectangleは「そのフレームに1回描く」命令なので、毎フレーム描き直すループが必要です。ここをAIに追加で直してもらいます。
ステップ5:修正プロンプトと、直ったコード
今のコードは一瞬で消えます。while true と emu.frameadvance() で
毎フレーム描画し続けるループにしてください。値の読み取りもループ内で行い、
HPが最大値を超えたり0未満になっても割合が0〜1に収まるようにしてください。
この指示で、ループと値のクランプ(0〜1に丸める処理)が入り、症状A・Bが両方解消しました。
ステップ6:座標ズレ・点滅(チラつき)の微調整
ループ化で常時表示にはなったものの、環境によっては次の細かい詰まりが残ります。ここは「AIの一発回答」より、自分で数値を触って合わせるのが速い部分です。
| 症状 | 原因 | 直し方 |
|---|---|---|
| バーが画面外/位置が微妙 | 解像度・表示倍率で原点感覚がズレる | x, y を8刻みで調整。まず左上(8,8)基準で合わせる |
| 文字とバーが重なる | 行間が足りない | バーのyをテキストより18px以上下げる |
| たまにチラつく | 描画順とフレーム更新の噛み合わせぬアループ | 読み取り→描画→emu.frameadvance()の順を固定する |
色は0xAARRGGBB(先頭2桁が不透明度)で指定します。当ブログの配色に寄せるなら、枠は不透明の黒0xFF000000、背景バーはシアン0xFF33D0FF、HPバー本体はライムグリーン0xFF9BE15Dあたりが視認性が高くおすすめです。
コピペで動く最終スクリプト
下記が最終形です。addrとhpMaxだけ自分の値に書き換えれば、そのまま常時オーバーレイ表示になります。BizHawkの Tools → Lua Console → 新規スクリプトに貼り付けて実行してください。
-- BizHawk Lua: HPバー 常時オーバーレイ表示(最小テンプレ)
-- 使い方: addr と hpMax を自分の値に置き換えて実行
local addr = 0x00XXXX -- RAM Searchで特定したHPアドレスに置換
local hpMax = 9999 -- そのゲームのHP最大値に置換
local X, Y = 8, 8 -- 左上の表示基準
local BAR_W, BAR_H = 120, 10
while true do
-- 1) 値を読む(2バイト・リトルエンディアン)
local hp = memory.read_u16_le(addr)
-- 2) 割合を0〜1にクランプ
local ratio = hp / hpMax
if ratio < 0 then ratio = 0 end
if ratio > 1 then ratio = 1 end
-- 3) 描画(テキスト → バー枠 → バー本体)
gui.text(X, Y, string.format("HP: %d / %d", hp, hpMax))
gui.drawRectangle(X, Y + 18, BAR_W, BAR_H, 0xFF000000, 0xFF33D0FF)
gui.drawRectangle(X, Y + 18, math.floor(BAR_W * ratio), BAR_H, 0xFF000000, 0xFF9BE15D)
-- 4) 1フレーム進める(このループが常時表示の肝)
emu.frameadvance()
end
※memory.read_u16_leは「2バイト値」を読む例です。HPが1バイトのゲームならmemory.read_u8、4バイトならmemory.read_u32_leに変えてください。読み取りサイズはRAM Search時に判明しているはずです。
よくあるエラーと対処
Q. 実行した瞬間にエミュが固まる/操作できない
while true do ... end の中にemu.frameadvance()(またはemu.yield())が入っていないと、無限ループでフレームが進まず固まります。ループ内に必ず1回だけemu.frameadvance()を入れてください。
Q. 値がいつも0、または明らかに変な数字になる
アドレスか読み取りサイズが合っていません。0x00XXXXを自分のRAM Searchで確定した値に置き換え、1バイト/2バイト/4バイトの読み取り関数を実際のサイズに合わせてください。メインメモリ以外のドメインを読む場合はmemory.usememorydomain()でドメイン指定が要ることもあります。
Q. バーの色が思った通りにならない
色は0xAARRGGBB形式です。先頭のAA(不透明度)を00にすると透明で見えません。まずは0xFF始まりで指定してください。
Q. スクリプトを止めたい
Lua Consoleの「Stop」ボタン(または該当スクリプトのチェックを外す)で停止します。閉じても残る場合はConsoleを一度閉じてください。
まとめ
HPバーのオーバーレイ表示は、①memory.read_*で値を読む、②gui.text/gui.drawRectangleで描く、③emu.frameadvance()のループで毎フレーム描き直す——この3点が骨格です。詰まりどころは「1回しか描いていない(=ループ化)」と「座標・色の微調整」に集約されます。AIには仕様を具体で渡し、返ってきたコードを実際に動かして症状ベースで直していくのが、結局いちばん速い進め方でした。
次に読む(関連記事)
まだHP・座標のアドレスを特定していない人は、まずこちら(値の「特定」編)。本記事はその続き(特定した値の「表示」編)です。

メモリ値の読み取り・画面表示の基礎から確認したい人はこちら。

エミュレータ側の基本操作・メニューの使い方はこちら。

実験環境・学習用の道具(PR)
Amazonのアソシエイトとして、当ブログは適格販売により収入を得ています。
Lua+メモリ解析の実験を快適にするなら、処理落ちしないゲーミングPCや、検証画面をきれいに記録するキャプチャボードがあると捗ります。
- ゲーミングPCをAmazonで探す
- I-O DATA HDMIキャプチャーボード(USB 3.0接続)(Amazon)
- Programming in Lua(プログラミング言語Lua公式解説書)(Amazon)
教材
「コピペで動く最小Lua集(メモリ監視・オーバーレイ・入力自動化)」を教材としてのnoteにまとめています(準備中・公開後にリンク追加)。本記事のコードもこのキットに収録予定です。
この記事の親ガイド

【予告】この5工程で使うスクリプトをまとめた「コピペで動くLua集+環境構築キット」を配布予定です。価格・配布日は未定で、決まり次第この記事に追記します。



コメント