Claude Media
Claude CodeでGameMakerのGMLとRPGツクールMZのプラグインを書く — 約束はCLAUDE.mdに

Claude CodeでGameMakerのGMLとRPGツクールMZのプラグインを書く — 約束はCLAUDE.mdに

GameMakerのGMLとRPGツクールMZのプラグインは、一般的なJavaScriptやC#とは書き方の約束が違います。その約束をCLAUDE.mdとpathsつきrulesに分けて置き、hookで構文を確かめる方法をまとめます。

GameMakerのGMLも、RPGツクールMZのプラグインも、Claude Codeは書けます。つまずくのは「書けるか」ではなく、そのツール固有の約束を毎回守らせられるかです。GMLは一般的な言語に似ていて、微妙に違います。ツクールのプラグインはJavaScriptですが、先頭のコメントが設定画面の部品になります。

この記事では、独自形式のプロジェクトでCLAUDE.mdに何を書き、何をrulesとhookに回すかを、GameMakerとツクールMZの2例で組みます。

独自形式のプロジェクトでは何がずれるのか

汎用のコーディング支援が外しやすいのは、言語の文法よりも「その環境が決めている置き場所と書式」です。両者のマニュアルから、約束事を拾うと次のようになります。

くらべる

2つの環境で守らせたい約束

コードの置き場所と関数の形

GameMaker(GML)

コードはスクリプトアセットと、オブジェクトのイベントに書きます。関数の宣言は2通りあり、形で扱いが変わります。

ファイルの置き場所とコメント書式

RPGツクールMZ(プラグイン)

ファイルはプロジェクトのjs/pluginsに置きます。先頭の/*:コメントが、エディタのヘルプと設定項目になります。

この2つは、Claude Codeが最初に見つけにくい情報です。CLAUDE.mdは、まさにそのために使います。メモリーのページによれば、CLAUDE.mdは同じ間違いを2度したとき、同じ訂正を前回のセッションでも入力したときに追記する場所とされています。

GameMaker側の約束 — 関数の宣言形式とglobal

GMLのスクリプト関数には2つの書き方があります。マニュアルは次のように区別しています。

// 形式1: スクリプト関数になる
function move(spd, dir = 90)
{
    speed = spd;
    direction = dir;
}
 
// 形式2: メソッド変数になる
move2 = function(spd, dir)
{
    speed = spd;
    direction = dir;
}

形式1で定義すると、その関数はグローバルスコープのスクリプト関数となり、globalの接頭辞なしで呼べます。形式2はグローバルスコープのメソッド変数になり、IDEにはスクリプト関数として認識されません。参照にはglobalの接頭辞が要ります。

さらに、オブジェクトのイベント内で形式1の関数を作ると、グローバルにはならず、そのインスタンスの文脈でだけ使えます。「スクリプトアセットのなかでは形式1を使う」と決めておけば、この食い違いは避けられます。

引数の既定値はdir = 90のように書けます。渡さなかった引数はundefinedになり、argument_countやargument[]で可変個の引数も受けられます。こうした記法は、JavaScriptやC#の癖で書くと外しやすい箇所です。マニュアルによれば、補完とエラー表示にはFeatherが使えるので、IDE側の確認とも組み合わせられます。

プロジェクトファイルとビルドの確認

GMLを書くときに触るのは、.gmlだけではありません。GameMakerのプロジェクトは、ルートの*.yypが中心です。リソースの一覧とプロジェクト固有のメタデータを持ちます。アセットごとに*.yyがあり、アセットのデータと、そこに属するスクリプトや画像などのファイルを記述します。形式はJSONに近いものです。

Project Formatのページは、読み込みに失敗したときのリソースのリンク解除を、ソース管理の代わりにはならないと明記しています。そこでCLAUDE.mdには「.yypと.yyは手で編集しない。アセットの追加や名前変更はIDEで行う」と書いておく運用が考えられます。これは公式の記述から導いた案で、公式の指示ではありません。

動作確認には、コマンドラインのIgorがあります。ランタイムフォルダの中にあり、macOSでは/Users/Shared/GameMakerStudio2/Cache/runtimes/runtime-[version]/bin/igor/osx/<architecture>/が既定の場所です。事前にIDEへアカウントでログインしておく必要があります。Windows向けのコンパイルは、マニュアルの例では次の形です。

Igor.exe /uf=[user_folder] /rp=[runtime_path] \
  /project=[project_YYP_file] /cache=[cache_dir_path] \
  /temp=[temp_dir_path] -- Windows Compile

角括弧の部分は環境に合わせて置き換えます。パスにruntime-[version]が含まれるため、IDEがランタイムを更新するとCLAUDE.mdに書いたパスが古くなります。ビルドが急に失敗したら、まずパスの実在を確かめます。

RPGツクールMZ側の約束 — 注釈コメントとコアの再定義

MZのプラグイン講座は、公式プラグインのTextPicture.jsを手本に書き方を説明しています。守らせたいのは次の4点です。

約束

MZプラグインで守らせたい書き方

  • 全体を即時関数で囲む

    変数が他のプラグインと衝突しないよう、(() => { ... })();の中に処理を書きます。関数内に'use strict'を置くと、宣言なしの変数をエラーにできます。

  • ヘルプコメントは/*:で始める

    @target MZは固定で書き、MV向けのプラグインと区別します。@plugindescと@author、@helpは管理画面に表示されます。

  • コアの関数は退避してから再定義する

    元の関数を変数に控え、新しい関数の中でapply(this, arguments)で呼び出してから、追加の処理を書きます。

  • コマンドはregisterCommandで登録する

    PluginManager.registerCommandにプラグイン名、コマンド名、関数を渡します。引数は配列ではなくオブジェクトで届きます。

コマンドの注釈とregisterCommandの対応

プラグインコマンドは、注釈とコードの2か所に同じ名前を書いて初めて成り立ちます。MVではコマンド名も引数も直接入力でしたが、MZでは注釈で定義します。

/*:
 * @target MZ
 * @command set
 * @text 文字列を設定
 * @desc ピクチャとして描く文字列を保存します。
 *
 * @arg text
 * @text 文字列
 * @desc 描画したい文字列です。
 */

@commandはコマンド名で、呼び出すときの識別子になります。@argは引数名で、@textや@desc、@type、@defaultを添えると、イベントコマンドの入力欄の表示名や型が決まります。

PluginManager.registerCommand("TextPicture", "set", args => {
  textPictureText = String(args.text);
});

registerCommandの第2引数が@commandのset、argsのキーが@argのtextに当たります。名前が食い違うと、コマンドは呼ばれません。コマンド内でGame_Interpreterのメソッドを使うときは、アロー関数ではなくfunction(args)で書き、thisから呼びます。

注釈は、パラメータにも使います。@param、@text、@desc、@type、@defaultを並べると、設定ダイアログの部品ができあがります。@typeにはnumberやbooleanのほか、actorやswitchのようなデータベース選択もあります。依存関係は@base、@orderAfter、@orderBeforeで書けます。

画像を直接読み込むプラグインでは、@requiredAssetsの指定が要ります。指定がないと、デプロイ時の「未使用ファイルを除外する」で素材が消えるおそれがあります。MVにあった@requireは廃止されています。

有効化とUpdateは人が行う

jsファイルを書くところまでは、Claude Codeに任せられます。ヘルプによれば、自作プラグインはjsファイルをプロジェクトのjs/pluginsへ置き、[Tools]の[Plugin Manager]で空の行をダブルクリックして名前を選ぶと有効になります。一覧のチェックボタンでON/OFFも切り替えられます。

見落としやすいのが更新です。jsファイルを上書きしたあと[Update]を選ぶと、パラメータ設定と説明の変更が反映されます。Claudeが引数を増やしたのにエディタの設定画面に出てこないときは、この更新を疑います。CLAUDE.mdには「有効化と[Update]は人が行う。引数や@commandを変えたら、そう報告する」と書いておくと、行き違いを防げます。

CLAUDE.mdに書く内容と、rulesに分ける内容

CLAUDE.mdは短く保つ前提です。1ファイル200行以下が目安で、部分的にしか要らない指示はパス指定のrulesに移すよう案内されています。そこで、ゲーム全体の事実はCLAUDE.mdに、拡張子ごとの書式はrulesに分けます。

まずプロジェクト直下のCLAUDE.mdです。以下はドキュメントの書き方に沿った例で、自分のプロジェクトに合わせて書き換えてください。

# プロジェクト概要
- RPGツクールMZの本編プロジェクト。プラグインは js/plugins/ に置く
- js/plugins/ の既存ファイルは編集しない。変更は新規ファイルで行う
- 動作確認はツクールのテストプレイで行う。結果は私がログを貼る
 
# 禁止
- プラグインは MZ 専用。MV 向けの @require は書かない
- 使い方の説明は @help に書く。管理画面に表示される

次に、.claude/rules/にパス付きのrulesを置きます。パス指定はpathsのYAMLフロントマターで書き、Claude Codeが一致するファイルを読んだり編集したりしたときに読み込まれます。

---
paths:
  - "js/plugins/**/*.js"
---
 
# MZプラグインの書式
- 全処理を (() => { 'use strict'; ... })(); で囲む
- 先頭に /*: で始まるヘルプコメントを置き、@target MZ を書く
- コアの関数を変える場合は、元の関数を const に控えて apply で呼ぶ
- コマンドは PluginManager.registerCommand で登録する
- @command と @arg で注釈を書き、registerCommand のコマンド名と一致させる
- 画像を直接ロードする場合は @requiredAssets を書く

GameMakerのほうは、同じ形でGML用のrulesを作ります。

---
paths:
  - "scripts/**/*.gml"
---
 
# GMLの書式
- スクリプトアセットの関数は function name() 形式で書く(global 接頭辞を不要にするため)
- メソッド変数 name = function() は、オブジェクトのイベント内だけで使う
- 引数の既定値は function f(a, b = 0) の形で書く

scripts/**/*.gmlは一例です。GameMakerのプロジェクトで実際にGMLファイルがどこに置かれるかは、自分のプロジェクトのフォルダで確かめて合わせてください。

hookで構文だけは機械的に確かめる

CLAUDE.mdの指示は、設定ではなく文脈です。厳密な遵守は保証されないとも明記されています。たとえば「ファイルを編集したら毎回確かめる」ような処理はhookに回します。

ツクールのプラグインはJavaScriptなので、Nodeの構文検査が使えます。.claude/settings.jsonにPostToolUseのhookを置きます。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": ".claude/hooks/check-plugin.sh" }
        ]
      }
    ]
  }
}

スクリプトは、編集されたファイルのパスを標準入力のJSONから取り出して判定します。

#!/bin/bash
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
 
case "$FILE_PATH" in
  */js/plugins/*.js)
    if ! OUT=$(node --check "$FILE_PATH" 2>&1); then
      echo "$OUT" >&2
      exit 2
    fi
    ;;
esac
exit 0

PostToolUseでexit 2を返すと、ツールがすでに実行された後でも、stderrの内容がClaudeに渡ります。構文エラーをその場で直させる使い方に向いています。node --checkが見るのは構文だけです。コアの関数名や$gameVariablesが正しいかは、ツクール側のテストプレイが必要です。

GMLにnode --checkのような手軽な構文検査はありません。マニュアルにあるのは、IDE内のFeatherによる補完とエラー表示と、先に書いたIgorのビルドです。ビルドは時間がかかるので、毎回の編集に挟むhookにするより、CLAUDE.mdで「まとまった変更のあとにIgorで確認し、IDEのエラーは貼り返す」と取り決めておくのが現実的です。

実際の頼み方 — 約束を引用させる

約束を置いたら、依頼文では「何を作るか」だけを書きます。

js/plugins/ に MZ のプラグインを新規作成して。
ゲーム内の変数 ID を指定して、その値をピクチャとして表示するコマンドを1つ持たせたい。
パラメータでフォントサイズを変えられるようにして。

この依頼で期待するのは、新規ファイルの先頭に@target MZつきのヘルプコメントが付き、フォントサイズが@type numberのパラメータになり、コマンドが@commandとregisterCommandの組で作られることです。結果が違えば、rulesの文言を具体化します。

つまずきやすい点

落とし穴

rulesが読み込まれたか分からない

サブディレクトリのCLAUDE.mdは、起動時の「Memory files」には出ません。作業中にそのディレクトリのファイルを読んだときに読み込まれます。パス付きrulesも、一致するファイルをClaudeが読むか編集してからです。InstructionsLoadedのhookでログを取ると、いつ読み込まれたかを確かめられます。

CLAUDE.mdが肥大化したときは、/doctor prompt-auditで古い指示や矛盾する指示を洗い出せます。変更は、依頼するまで反映されません。ほかのツールの指示ファイルを持っているなら、/initが取り込んでくれます。

引き継ぎ先のプロジェクトで既にプラグインが何十本もあるときは、「既存ファイルは編集しない」を先頭に置くのが安全です。プラグインはコアの関数を再定義して動きを変えるため、既存のプラグインと同じ関数を触ると競合のもとになります。順序が要るときは、競合対策として@orderAfterや@orderBeforeで明示します。

まとめ

GMLとMZプラグインの違いは、Claude Codeの能力ではなく「その環境の約束を知っているか」で決まります。約束はCLAUDE.mdに全体の事実として置き、書式はパス付きrulesに分け、検査できるものだけhookにします。GameMakerの検証はIDEの実行に頼る形になる、という違いも先に決めておけば、往復が減ります。

同じ「独自のプロジェクト規約をCLAUDE.mdに固める」やり方は、Solidityのプロジェクトやoclif製のCLIでも扱っています。ゲームの別エンジンでは、Godotの翻訳対応があります。CLAUDE.mdが長くなりすぎたときの見方は上限と確認法にまとめています。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. code.claude.com/docs/en/memory
  2. code.claude.com/docs/en/hooks-guide
  3. manual.gamemaker.io/monthly/en/GameMaker_Language/GML_Overview/Script_Function…
  4. manual.gamemaker.io/monthly/en/GameMaker_Language/GML_Overview/GML_Overview.htm
  5. rpgmakerofficial.com/product/mz/plugin/make/annotation.html
  6. rpgmakerofficial.com/product/mz/plugin/make/koushiki.html
  7. manual.gamemaker.io/beta/en/Additional_Information/Project_Format.htm
  8. manual.gamemaker.io/lts/en/Settings/Building_via_Command_Line.htm
  9. rpgmakerofficial.com/product/MZ_help-en/01_11_04.html
この記事を共有:XはてブLinkedIn