「Uncaught ReferenceError」が出たゲームの直し方
遊びをつくる執筆にClaudeを使用しています。

ブラウザのコンソールに Uncaught ReferenceError: ○○ is not defined と表示されたとき、ゲームは動きを止めます。画面が真っ白になる、キャラクターが動かない、スコアが更新されない——症状はさまざまですが、原因の構造は共通しています。「存在しない名前を呼び出した」という一点です。 ReferenceError は「名前の解決に失敗した」エラーです。JavaScriptエンジンが変数・関数・クラスの名前を探したけれど、どこにも見つからなかったときに投げられます。TypoError(型の誤用)や SyntaxError(文法ミス)とは異なり、コードの構文は正しくても、実行時に名前が存在しないと発生します。この性質を理解しておくと、どこを調べるべきかの見当が早くつきます。 この記事では、ゲーム制作でよく起きるパターンに絞って「なぜ起きるか」「どこを直すか」「再発を防ぐには」を順番に説明します。
エラーメッセージから読み取れること
コンソールに表示されるメッセージの構造は次のようになっています。
Uncaught ReferenceError: score is not defined
at updateScore (game.js:42:5)
at game.js:78:3最初の行が「何という名前が見つからなかったか」、その下のスタックトレースが「どのファイルの何行目で呼ばれたか」を示しています。まず score is not defined の score という名前と、game.js:42 という行番号を手元にメモします。この二つを持ってコードを開けば、調査の出発点が決まります。
ブラウザの開発者ツール(F12キーまたは右クリック→「検証」)を開き、Consoleタブを確認してください。エラーの行番号リンクをクリックすると、Sourcesタブで該当箇所が直接開きます。これがもっとも速い確認方法です。
ReferenceError が起きる四つのパターン
パターン1:単純なスペルミス
もっとも多い原因です。score と書くべきところを Score や scroe と書いていると、JavaScriptは別の名前として扱います。変数名は大文字・小文字を区別するため、Player と player は異なる名前です。
確認方法は、エラーに出た名前でコード全体を検索し、定義(let・const・var・function)と呼び出しのスペルを見比べることです。エディタの「すべて検索」(VS CodeならCtrl+Shift+F)が便利です。
パターン2:スコープの外から呼んでいる
let や const で宣言した変数は、宣言したブロック({})の外から読めません。次のケースがゲームでよく起きます。
function startGame() {
let playerHP = 100;
}
function takeDamage() {
playerHP -= 10; // ReferenceError: playerHP is not defined
}playerHP は startGame 関数の中だけに存在するため、takeDamage からは見えません。複数の関数で共有したい状態は、関数の外側(モジュールのトップレベル)で宣言する必要があります。
let playerHP = 100; // 外に出す
function startGame() { playerHP = 100; }
function takeDamage() { playerHP -= 10; }ゲームの「状態」を管理する変数(HP・スコア・ステージ番号など)は、ファイルの先頭近くにまとめて宣言しておくと、このミスを防ぎやすくなります。
パターン3:読み込み順の問題
HTMLファイルで複数の <script> タグを使っているとき、後から読み込まれるスクリプトで定義した関数を、先に読み込まれるスクリプトから呼ぼうとすると ReferenceError になります。
<script src="main.js"></script> <!-- ここで setupEnemy() を呼ぶ -->
<script src="enemy.js"></script> <!-- setupEnemy はここで定義 -->この場合は enemy.js を先に書くか、main.js 側の呼び出しをページ読み込み完了後(DOMContentLoaded イベントや window.onload)に遅らせることで解決します。
ZIPファイルで複数のJSファイルをまとめている場合も同じ原則が当てはまります。HTMLの <script> タグの順番を依存関係に合わせて並べ替えてください。
パターン4:非同期処理のタイミングずれ
fetch や loadImage などの非同期処理でデータを取得し、コールバックや then の外で使おうとすると、変数がまだ代入されていない状態で参照することになります。厳密には undefined が返る場合が多いですが、const で宣言した変数を後から代入しようとしていたり、宣言自体が非同期ブロック内にある場合は ReferenceError になります。
async function loadAssets() {
const imageData = await fetch('sprites.json').then(r => r.json());
}
drawSprite(imageData); // ReferenceError: imageData is not defined非同期処理で得たデータは、その処理が完了した後に実行される場所(await の後、または .then() の中)でしか使えません。ゲームの初期化ロジックを async 関数にまとめ、await で順序を制御するのが現実的な対処です。
原因の特定を速くする手順
- コンソールのエラーメッセージから「見つからなかった名前」と「行番号」を確認する
- その行番号でコードを開き、問題の名前がどう書かれているかを目視する
- その名前が定義されている場所をエディタで全文検索する
- 定義が見つかった場合は「スコープ」と「読み込み順」を確認する
- 定義が見つからない場合はスペルミスか、定義し忘れを疑う
この順で調べると、ほとんどのケースは5分以内に原因が見つかります。「なんとなくコードを眺める」より、エラーメッセージの情報を入口にして絞り込む方が確実です。
再発を防ぐ書き方の習慣
var の代わりに let と const を使うことを基本にしてください。var はスコープが関数単位でブロックを無視するため、意図しない場所から参照できてしまい、バグの発見が遅れます。let と const はブロックスコープなので、使える範囲が明確になり、ReferenceError が早い段階で出て気づきやすくなります。
ESLint などの静的解析ツールをエディタに導入すると、未定義の変数を使っている箇所を保存時に警告してくれます。ゲームの規模が大きくなるほど、実行前に問題を発見できる仕組みの価値が上がります。
また、ゲームの状態変数を一つのオブジェクトにまとめる書き方も有効です。
const gameState = {
playerHP: 100,
score: 0,
stage: 1,
};こうするとどこからでも gameState.score のようにアクセスでき、スコープの問題が起きにくくなります。名前も gameState という一つの変数に集約されるため、スペルミスが起きる箇所を減らせます。
次の行動
ブラウザのコンソール(F12)を開いた状態でゲームを動かし、ReferenceError が出ている行番号に直接ジャンプして確認してみてください。エラーの名前と行番号を手がかりに、この記事の四つのパターンと照らし合わせると原因が特定できます。修正したゲームをアソバースに投稿する際は、別ブラウザや別タブでも動作確認をしてから公開すると、読者への影響を最小限にできます。

