オレンジブログ

オレンジブログ

GameMakerのちょっとしたTipsとか

【GMS2】Time Sourceの使い方


はじめに

前回、alarmイベントの代わりに使える関数としてcall_laterを紹介しました。

orangelily.hatenablog.com

今回の記事では、それの発展であるtime_source系の関数について紹介しようと思います。
一定時間後に処理を実行するだけでなく、ループ回数の指定や親子の設定
一時停止や再開処理を行うことが出来ます。


複雑な処理をさらにシンプルに記述することが出来るようになるので、使えるようになると便利です。


Time Sourceについて

Time Source系の関数の使い方を教える前に、まずは軽く説明をします。
詳しく知りたい方は公式マニュアルを参考にしてください。

manual.gamemaker.io


Time Sourceとは

一言で説明すると、カスタムタイマーのことです。
指定した一定時間だけ動作し、時間が経過すると関数を実行する処理になります。
また、指定した回数だけ繰り返すことや無限に繰り返すことも可能です。


使い方

Time Sourceを使うためには、time_source_create関数(後述)を呼び出して、Time Sourceを作る必要があります。
作成する際に、実行時間呼び出す関数繰り返し回数を指定します。


次に、time_source_start関数(後述)を呼び出すことで、指定したTime Sourceを動作させることが出来ます。


関数実行後、まだ指定した回数繰り返していなければ一定時間後に再度関数が呼び出されます
指定した繰り返し回数を超えたらTime Sourceは停止します。

フローチャート


重要な仕様

処理が行われるタイミング

呼び出されるタイミングは、BeginStepStepの間です。
つまり、登録された関数はStepイベントの前に実行されるので注意してください。

リソースの管理

Time Sourceグローバル空間にメモリが確保されます。
そのため、オブジェクトのCreateEventで作成したとき、そのオブジェクトがDestroyされた後も残り続けます
なので、必ずCleanUpイベントDestroyイベントtime_source_destroy(後述)を呼び出す必要があります。


基本的なTime Source関数説明

全ての関数を紹介すると長くなるので、よく使う関数のみ紹介します。
全ての関数を知りたい方は公式マニュアルを参考にしてください。

manual.gamemaker.io


time_source_create

manual.gamemaker.io

Time Sourceを作ります。
この関数の戻り値としてTime Source IDを受け取れるので、それを変数に格納しておきましょう。

time_source_create(parent, period, units, callback, [args, repetitions, expiry_type]);
引数名 説明
parent Time Source Time Source
period Real Time Sourceが実行される時間(unitsで指定した単位)
units Time Source Unit 定数 period に指定した時間の単位
callback メソッド または スクリプト関数 Time Sourceが期限切れになったときに呼び出されるメソッド/関数
args Array (省略可) メソッドに渡す引数を格納した配列
repetitions Real (省略可) 繰り返し回数。-1 を指定すると無限に繰り返す。デフォルトは1。
expiry_type Time Source Expiry 定数 (省略可) 時間経過後最も近いフレームで終了するか、次のフレームで終了するかを指定

サンプルコード

// コールバック関数
var _my_method = function()
{
    instance_destroy();
}
// 300フレーム後に関数を実行
time_source = time_source_create(
    time_source_game,
    300,
    time_source_units_frames,
    _my_method
);
// 開始
time_source_start(time_source);

いくつか引数の説明を行います。

親のTime Sourceについて

親のTime Sourceについてですが、以下の3つを指定できます。

  • time_source_global
  • time_source_game
  • 自身で作成したtime_source

time_source_globaltime_source_gameデフォルトでGML側が用意しているTime Sourceです。つまり定数ですね。
親を指定すると、開始や停止を行う際にその子にも同時に処理することが出来ます。
後述するtime_source_pausetime_source_resumeなどで役立つ機能です。


公式では、ゲーム内の動作であればtime_source_gameを、通信やデバッグ用などではtime_source_globalを使うのをオススメしています。
もし使い分けが難しかったら、「フレーム依存ならtime_source_game」「時間依存ならtime_source_global」を使うといいと思います。

manual.gamemaker.io

Time Source Unit定数

時間指定の方法についてです。
以下の2つの定数から1つを選びます。

定数名 説明
time_source_units_seconds タイムソースの期間に を使用する(フレームレート非依存) 0
time_source_units_frames タイムソースの期間に フレーム を使用する(フレームレート依存) 1

time_source_units_secondsを指定した場合は、game_fpsや処理落ちなどに依存せず時間指定で処理を実行することが出来ます。
時間制限などの処理に向いています。


time_source_units_framesを指定した場合は、game_fpsに依存したフレーム経過で処理を実行することが出来ます。
例えばgame_fpsが60で60フレーム後を指定したら約1秒後
game_fpsが30で60フレーム後を指定したら約2秒後になります。
処理落ちなどにも影響するため、ゲーム内での動作を処理したい場合に有効です。

manual.gamemaker.io

Time Source Expiry 定数

Time Source Unitで時間を指定した場合、フレームの途中で経過時間に達したとき今のフレームで実行する次フレームで実行するかを選べます。

定数名 説明
time_source_expire_nearest 期限切れ時刻に 最も近いフレームでタイムソースが期限切れになる 0
time_source_expire_after 期限切れ時刻の 次のフレームでタイムソースが期限切れになる 1

という仕様ですが、何度試しても意図して実行フレームをズラすことが出来ませんでした。
あまり影響がないと思われるので、いったんデフォルトでいいと思います。

manual.gamemaker.io


time_source_destroy

manual.gamemaker.io

指定したTime Sourceを破棄します。
time_source_createで作成したTime Sourceオブジェクトが破棄されてもメモリ上に残り続けるため、
CleanUpイベントなどで必ず破棄してください

time_source_destroy(id, [destroy_tree]);
引数名 説明
id Time Source 破棄する Time Source
destroy_tree Boolean 【省略可】子 Time Source も含めて破棄するかどうか(デフォルトは false)
/// Create event
time_source = time_source_create(time_source_game, 1, time_source_units_seconds, my_method, [], -1);

/// Clean Up event
time_source_destroy(time_source);

Time Source操作系の関数

Time Source操作系の関数です。
停止や再開などの処理を行えます。


time_source_start

manual.gamemaker.io

指定したTime Sourceを開始させます。
time_source_createで作成したTime Sourceは、time_source_start関数を呼び出さないと実行されないので気を付けてください。

引数名 説明
id Time Source 開始する Time Source
var _my_method = function()
{
    instance_destroy();
}

var _time_source = time_source_create(time_source_game, 300, time_source_units_frames, _my_method);

time_source_start(_time_source);

time_source_stop

manual.gamemaker.io

指定したTime Sourceを停止し、そのタイマーをリセットします。
停止後は再度time_source_start関数を呼び出すことで開始できます。
その際、タイマーはリセットされているので気を付けてください。

引数名 説明
id Time Source 停止する Time Source

例えば、60秒後に実行するTime Sourceを作ったとします。

// コールバック関数
var _my_method = function()
{
    instance_destroy();
}
// 60秒後に関数を実行
time_source = time_source_create(
    time_source_global,
    60,
    time_source_units_seconds,
    _my_method
);
// 開始
time_source_start(time_source);

このTime Sourceですが、Stopさせる場合

// 停止状態じゃないなら
if (time_source_get_state(time_source) != time_source_state_stopped)
{
    // 停止する
    time_source_stop(time_source);
}

このように呼び出します。
再度このTime Source開始したい場合

// また60秒後に処理を実行させる
time_source_start(time_source);

このように処理します。
この場合はタイマーがリセットされているので、time_source_stopしたタイミングに関わらず、
再度60秒後に実行されるようになります。
もし一時停止後に再開をしたい場合time_source_pause/time_source_resumeを使いましょう。


また、そのTime Sourceに子が存在した場合はその子も停止されます

// time_source_gameに登録したTime Sourceすべてを停止させる
time_source_stop(time_source_game);

自分で作ったTime Sourceも指定できるため、グループごとに管理することが出来ます。


time_source_pause

manual.gamemaker.io

この関数は、指定したTime Source一時停止させます。
time_source_stopとの違いは、time_source_resume関数と組み合わせることで、
一時停止したTime Sourceを再開を行えます。

time_source_pause(id);
引数名 説明
id Time Source 一時停止する Time Source

詳しい使い方は次の time_source_resumeに載せています。


time_source_resume

manual.gamemaker.io

この関数は、time_source_pauseで一時停止したTime Source途中再開を行う関数です。

time_source_resume(id);
引数名 説明
id Time Source 再開する Time Source
// スペースキー押すたびに停止と再開を切り替える
if (keyboard_check_pressed(vk_space))
{
    // 現在のステートを取得
    var _state = time_source_get_state(time_source);
    // 実行中なら
    if (_state == time_source_state_active)
    {
        // 一時停止する
        time_source_pause(time_source);
    }
    // 停止中なら
    else if (_state == time_source_state_paused)
    {
        // 再開させる
        time_source_resume(time_source);
    }
}

上記のような処理を書くことで、一時停止再開を行うことが出来ます。
例えばtime_source60秒後に実行される場合、
20秒後にpauseした後再度resumeで再開すると
さらに40秒後に処理が実行されます。


メニュー画面を開いた時だけ一時停止したい場合などに活用できます。
もちろん子のTime Sourceにも影響するため、例えば下記のような処理にも活用できます。

親子関係と一時停止/再開を利用したサンプル

ミニゲームの時間が60秒で、残り30秒、20秒、10秒で警告を表示する処理を実装します。
まずは60秒で実行されるTime Sourceを作り、
そのTime Sourceを親にして子のTime Sourceを作ります。

// 60秒のミニゲーム
#macro GAME_TIME 60
// ゲーム終了時の処理
function OnGameEnd(){
    // サンプルではログを出すだけ
    show_debug_message(("Game End!!"));
}
// 親をglobalにして60秒後にOnGameEndを実行
game_time_source = time_source_create(time_source_global, GAME_TIME, time_source_units_seconds, OnGameEnd);
// 残り時間警告関数
function ShowWarningTime(_time){
    // 今回はサンプルなのでログだけ
    show_debug_message($"{_time} seconds");
}
// 警告を出す時間を残り10,20,30秒で定義
var warning_time = [10,20,30];
// 警告Time Source配列
show_warning_time_source = [];
// ループで回す
for (var i = 0; i < array_length(warning_time); i++) {
    // 残り時間
    var time = GAME_TIME - warning_time[i];
    // 親をgame_time_sourceにしてTime Sourceを作成。ShowWarningTimeを呼び出すようにして引数にtimeを送る
    var time_source = time_source_create(game_time_source, time, time_source_units_seconds, ShowWarningTime, [time]);
    array_push(show_warning_time_source, time_source);
}

そしたら、一時停止と再開をgame_time_sourceを指定して行います。

function GameTimePause(){
    time_source_pause(game_time_source);
}

function GameTimeResume(){
    time_source_resume(game_time_source);
}

このような関数を、メニュー画面表示のタイミングで呼び出せば
子のTime Source含めて一時停止/再開するので、まとめて管理することが出来ます

これが非常に便利です。


まとめ

他にもTime Sourceに関する関数はありますが、この記事での紹介はここまでとします。
一定時間後に処理を行いたい場合であれば call_later関数を使うだけでいいですが、 親子関係一時停止/再開ループ回数の指定などを行いたい場合はTime Sourceが使えます。


その代わり管理する部分が増えるため注意が必要ですが、非常に便利な機能なので
是非使いこなしましょう!

【GMS2】一定時間後にコールバック関数を実行するcall_laterを解説


はじめに

GameMakerには一定フレーム後にイベントの実行を予約できるalarmイベントが存在します。

alarmイベントに処理を登録して

// 60フレーム後に呼び出す
alarm[0] = 60;

と書くことで、そのalarmイベントに記述した処理を60フレーム後に呼び出すことができる機能です。


ただ、alarm機能はイベントの数に制限があったり、
イベントと紐づいているので処理をインスタンス毎に用意しないといけません。
そのため、簡単に実装できるものの汎用性に乏しいです。


そこで、alarm機能と同様に一定時間後に処理を実行する処理を組めるcall_laterという関数について紹介します。
個人的にはalarmより使い勝手がいい機能だと思っています。


call_later関数

call_later関数とは、一定時間後に呼び出すコールバック関数を登録できる関数です。
指定した時間が経過した後、または一定間隔ごとに登録した関数が実行されます。

manual.gamemaker.io

call_later(period, unit, callback, [loop=false]);

・引数

引数名 説明
period 数値 指定した単位での実行間隔
units Time Source Unit 定数 時間の単位(秒 / フレーム)
callback 関数 時間経過後に呼び出される関数
loop Boolean(省略可) true の場合、繰り返し実行

・戻り値
Time Source


と言った関数です。


例えば

call_later(60, time_source_units_frames, function(){
    show_debug_message("Call Func!!");
});

と記述すると、60フレーム後に登録した関数が実行され、ログが表示されます。


call_later関数の使い方

下記のサンプルコードは全てオブジェクトのCreateイベントに記述しています。


例えば、5秒後に処理を実行したい場合は下記のように書きます。

// ログを出す関数
showLog = function(){
    show_debug_message("5秒経過しました");
}
// 5秒後にshowLog関数を呼び出す
call_later(5, time_source_units_seconds, showLog);

第二引数をtime_source_units_secondsにすることで、第一引数で指定した秒数で計測して処理を呼び出せます。
この方法だと、処理落ちなどでフレームレートが落ちても、正しく5秒で処理を実行してくれます。


また、他にも2フレーム毎にオブジェクトの表示非表示を切り替える処理も書くことができます。

// 表示非表示を切り替える
toggleVisible = function(){
    visible = !visible;
}
// 2フレーム毎に表示非表示を切り替える
toggleVisibleCaller = call_later(2, time_source_units_frames, toggleVisible, true);

第二引数をtime_source_units_framesにすることで、第一引数で指定したフレーム数が経過したとき処理を呼び出すようにできます。
第四引数をtrueにすることで、この処理をループさせることができます。


call_laterを使うと、このように一定時間後に処理を行うことができます。
alarmと違ってイベントではなく、コールバック関数を登録する形なので、
登録する個数に制限がなかったり、scriptで宣言したグローバルな関数も登録することができたりするメリットがあります。


さらに、コールバック関数を登録する形なので、method関数を使えばローカル変数などもその場でキャプチャして扱うことができます

orangelily.hatenablog.com

そのため、不要な変数を減らすことが出来るため、コードが複雑になることも防げます。


ループさせる際の注意点

第四引数をtrueにすると、登録された関数が一定時間ごとに呼び出されます。
便利な機能ですが、1点注意する必要があり、


オブジェクトが破棄された後も登録した関数が呼び出され続けてしまう


という現象が発生します。

これを解決するためには、call_laterで受け取った戻り値(TimeSourceハンドル)に対して、
call_cancelを行う必要があります。

/// CleanUpイベントなどで
call_cancel(toggleVisibleCaller);

これを行うことで、オブジェクトが破棄された後も呼び出しが行われる問題を解決できます。
逆に言えば、ループ処理を止めたい場合call_cancel関数を呼び出せばOKです。


まとめ

call_later関数とcall_cancel関数を用いることで、
alarmを使わずに一定時間後に処理を行うことが出来ます。
さらにループ処理なども組むこともでき、alarmより汎用的に組むことが出来ます。


さらにcall_laterから機能が増えたtime_source機能がGMLには存在しています。

manual.gamemaker.io

time_sourceはループだけでなく、ループ回数の指定や親子関係の設定
一時停止や再開処理などが用意されており、さらに便利に扱える機能になっています。


本ブログでもtime_sourceについて今後まとめようと思いますが、すでに気になった方がいらっしゃいましたら
使ってみることをオススメします。

【GMS2】エラー落ちする直前のゲーム画面を録画する拡張機能を作ってみた

この記事はGameMaker Advent Calendar 2025 13日目の記事です。
今回初参加させていただきました!

adventar.org


今回作った拡張機能の紹介

今回私は、GameMakerStudio2での起動画面をキャプチャする拡張機能を作りました。

GameMakerでのゲームと連動して動作するようになっており、
例えば「エラー落ちした時に直前15秒をキャプチャする」みたいな機能が備わっています。

例えば上記のように、20秒ほどプレイしていたらブロックを叩いた時にエラー落ちしてしまった!みたいな時、
自動で直前の15秒だけの動画をキャプチャして残してくれます。

今回はお試しで作ったのでまだ機能は最小限となっております。
C++拡張機能を使えばこんなこともできるんだ~って関心を持っていただけたら幸いです。

注意:この拡張機能Windows 専用 となっております。
Mac ではご利用できません。
また、現状は 機能不足や安定性が不安定なベータ版 となっております。 
導入する際は 必ずバックアップを取ってから 導入してください。

導入方法

今回の拡張機能ですが、ffmpegという外部ツールを使用します。

ffmpegとは?
ffmpegは、動画や音声の変換・結合・録画・編集などを行えるオープンソースのマルチメディアツールです。
コマンド1つで多くの形式を扱える柔軟さが特徴で、開発・配信・解析など幅広い用途で使われています。

こちらのffmpegをまずダウンロードし、パスを通す必要があります。
ffmpegを使ったことがない方は

taziku.co.jp

こちらのリンクを参考にパスを通す部分まで行ってください。


ffmpegのパスが通っているか確認

もしかしたら以前にffmepgを使ってパスを通した方もいらっしゃるかもしれません。
その場合の確認方法を記載しておきます。


まずはコマンドプロンプトを表示します。
Windowsの検索バーから「コマンドプロンプト」と検索し、ヒットしたこいつをクリックします。

こんなやつ

そしたら黒いウィンドウが出てくると思うので、そこに

ffmpeg version

と入力してEnterを押します。


その時に、長々とバージョン情報が表示されればOKです。
もし「コマンドが見つかりません」みたいに表示されたら

taziku.co.jp

こちらのリンクで紹介されている方法でダウンロードし、パスを通してください。


拡張機能のダウンロード

こちらのリンクからパッケージをダウンロードできます。

drive.google.com

リンクをダウンロードしたら、GameMakerStudio2のプロジェクトを開き、パッケージをインポートします。

これ

そしたら先程ダウンロードしたパッケージファイルを選択します。

DebugCaptureStreamingです

出てきたウィンドウで、真ん中の全て追加を押します。
すると、ウィンドウの右にインポートするフォルダが表示されます。

日本語だと全て追加と書かれています

そしたら、右下のインポートを押します。

ウィンドウの右を確認してから

DebugCaptureStreamingというフォルダーができていればインポート成功です。

こんな感じのフォルダーが出来ます


拡張機能のセットアップ

DebugCaptureStreamingフォルダ内の「objDebugCaptureStreaming」を一番最初のRoomに配置します。

こいつを

こんな感じに!

これだけです!


使い方


録画方法

セットアップが正しく成功していれば、ゲーム起動時に専用のウィンドウが表示されます。

こんな感じの

使い方自体はシンプルで、録画開始ボタンを押したタイミングから開始され、
録画停止ボタンを押したら停止できます。

ここを押せば録画が開始されます

録画中は停止ボタン以外は触れなくなります。

録画停止を押すと止まる

停止したらその場で動画が生成されます。
保存先は、デフォルトでは

PC/ビデオ/GameMakerStudio2/<projectName>/YYYYMMDD_hhmmss.mp4

の形式で保存されます。


フレームレート変更方法

動画のフレームレートを変更できます。
自分のゲームに合ったフレームレートにしてください。

15,30,60しか用意できませんでした


出力先変更

動画の保存先を変更できます。
残念ながら...まだセーブ機能がないためいちいち設定しないといけないため、
現状は需要がないです。ごめんなさい。

セーブさせれたら便利だろうなぁ...


エラー時に自動でキャプチャさせる

この拡張機能の目玉機能です。
下のチェックボックス(文字見切れてる)をチェックすると、エラー時に直前の15秒を自動でキャプチャする機能がオンになります。

ここのチェックを押す!

この機能をオンにすると、自動で録画が始まります。
一応手動で止めれるようにはしていますが、基本は止めないようにしてください


また、自動でキャプチャする時の時間を指定できます。

こんなメニューです

デフォルトは15秒ですが、最大で10分までキャプチャできます。


稀にしか起きないエラーを追う時等に便利な機能となっています。


まとめ

今回作ったキャプチャ機能ですが、まだ機能としては足りない部分が多く、
現状ベータ版のようになっています。
今後の展望として、

  • 設定のセーブ機能
  • ショートカットキー対応
  • ウィンドウ無しモードの実装

あたりを検討しています。


とはいえ、現状でも基本機能が使えるので、
もし気に入った方がいらっしゃいましたら使ってくださると助かります。


また、変な動作をしたときや、こんな機能が欲しい!みたいな報告や要望も受け付けております。
もし何かあったら私のX、もしくはMissyky.io垢までご連絡ください。


X: https://x.com/hemuhununeOrang

Misskey.io: オレンジ:kasu: (@hemuhununeOrange) | Misskey.io

【GMS2】GMLでのstaticの解説と使い方


はじめに

GameMakerのサンプルコードなどを読んでいると、staticというキーワードをよく見かけると思います。
特にコンストラクタ内の関数につけられている事が多く公式でも推奨されています。
しかし、何でつけるのか?どういう用途なのか?と言った事がよくわからなかったりしますよね。


そこで、今回はGameMakerでのstaticについて解説しようと思います。
基本的な知識から、関数やコンストラクタでの使い方についてまとめます。


今回はGMLでのstaticの説明になるため、他言語でのstaticとは異なります
他言語を使っている方は注意してください。


staticとは

manual.gamemaker.io

GMLstatic(スタティック)とは、関数やコンストラクタ全体で共有される変数(関数)を定義するための修飾子です。


関数内のローカル変数の場合

例えば関数内で通常のローカル変数を定義する際は下記のように定義しますね。

function AddLocalCounter() {
    // ローカル変数として定義
    var local_value = 0;
    local_value ++;
    return local_value;
}

このようなローカル変数は「関数が呼び出されるたびに初期化される」ため、
値の保持」は行われません。


つまり、上の関数を10回連続で呼び出したとしても値は変わりませんね。

repeat(10){
    var local_value = AddLocalCounter();
    show_debug_message($"local_value = {local_value}");
}
local_value = 1
local_value = 1
local_value = 1
local_value = 1
local_value = 1
local_value = 1
local_value = 1
local_value = 1
local_value = 1
local_value = 1

これは当たり前のことです。


関数内のstatic変数の場合

しかし、static修飾子をつけた変数はstatic専用の領域に確保されます。*1
さらに一度初期化されると、それ以降は再初期化されない
と言った特徴があります。

function AddStaticCounter(){
    // static変数として定義
    static static_value = 0; // ←のコードは最初の1回しか実行されない
    static_value ++;
    return static_value;
}

このようにstatic変数を定義した場合、「関数が最初に呼び出された時」しか「static変数は初期化」されません。


つまり、上記の関数を10回呼び出すと、下記のような結果になります。

repeat(10){
    var static_value = AddStaticCounter();
    show_debug_message($"static_value = {static_value}");
}
static_value = 1
static_value = 2
static_value = 3
static_value = 4
static_value = 5
static_value = 6
static_value = 7
static_value = 8
static_value = 9
static_value = 10

こんな感じに、static_valueの値が保持されているため、
呼び出すたびにインクリメントされていきます。


これがstaticの仕組みです。


コンストラクタでのstatic

上記のような関数での使い方だと、「グローバル変数とあまり変わらないのでは?」と思ってしまうかもしれません。
実際ほぼ仕組みとしてはあまり変わらず名前空間のあるグローバル変数みたいなものですね。


しかし、コンストラクタ内で使用すると大きく利便性がアップします。
それについて解説していきます。


メンバ変数でのstatic

まずはコンストラクタ内のメンバ変数でのstaticについて解説します。
今回はサンプルコードとして、以前説明でも使ったアイテムコンストラクタを使用します。

// アイテムの名前と値段を定義する
function Item(_name, _price) constructor {
    name = _name;
    price = _price;
}

上記のようなコードでは、nameprice共にstaticはついていません。
そのため、 new をする度にそれらのインスタンスnamepriceが定義されますね。

// ポーションと毒消しをそれぞれnewする
var potion = new Item("ポーション", 50);
var antidote = new Item("毒消し", 100);
// ログに出す
show_debug_message($"potion = {potion.name}, {potion.price}");
show_debug_message($"antidote = {antidote.name}, {antidote.price}");
potion = ポーション, 50
antidote = 毒消し, 100

これが通常です。

staticをつけていない例


では、name変数staticにするとどうなるか見てみます。

// アイテムの名前と値段を定義する
function Item(_name, _price) constructor {
    // staticにする
    static name = _name;
    price = _price;
}

name変数にのみ、staticをつけてみました。
これで先ほどのコードを実行してみます。

// ポーションと毒消しをそれぞれnewする
var potion = new Item("ポーション", 50);
var antidote = new Item("毒消し", 100);
// ログに出す
show_debug_message($"potion = {potion.name}, {potion.price}");
show_debug_message($"antidote = {antidote.name}, {antidote.price}");
potion = ポーション, 50
antidote = ポーション, 100

nameがどちらもポーションになっている!!


・解説

今回namestaticがついているため、最初にnewをした時には初期化処理が通り
nameには「回復薬」と言った値がstatic専用領域に代入されます。

nameにstaticをつけた例

しかし、次にnewをした毒消しの時にはname初期化処理が通らないため、
nameに「毒消し」は代入されず「回復薬」が代入されたままになります。

2回目以降はnameの初期化が行われない!
故に、2回目も名前がポーションのままだったのです。


関数の時と同じく初回のみ初期化され、それ以降は再初期化されず
static専用領域に確保されるわけです。


コンストラクタでのstatic変数の使い方

主に、同コンストラクタ内で同じ値を持ちたい時に使用します。
例えば、最後に使ったアイテム名を保持しておくとかですね。

// アイテムの名前と値段を定義する
function Item(_name, _price) constructor {
    // 最後に使ったアイテム名を保持する
    static last_use_item = "";
    
    name = _name;
    price = _price;
    
    static Use = function() {
        // アイテム使用時、static変数に自身の名前を格納する
        self.last_use_item = name;
        show_debug_message($"{name}を使用しました。")
    }
}

上記のような関数を作り、下記のように実行します。

var potion = new Item("ポーション", 50);
var antidote = new Item("毒消し", 100);

// ポーションを使用
potion.Use();
show_debug_message($"last_use_item = {potion.last_use_item}");
// 毒消しを使用
antidote.Use();
show_debug_message($"last_use_item = {antidote.last_use_item}");

すると、下記のようなログが表示されます。

ポーションを使用しました。
last_use_item = ポーション
毒消しを使用しました。
last_use_item = 毒消し

とはいえ、この程度であれば「グローバル変数で良くない?」となってしまいますね。
私もそう思います。


しかし、staticの真価を発揮するのはコンストラクタ内の関数定義になります。


メンバ関数でのstatic

ここからがstaticの本領発揮です。
GMLでstaticを使用する9割以上が「コンストラクタ内での関数」になります。


まずは使い方から解説します。


メンバ関数でのstaticの使い方

とはいえ、メンバ変数の時と特に使い方は変わりません
関数を定義するときにstaticを頭につけるだけです。

// アイテムの名前と値段を定義する
function Item(_name, _price) constructor {
    // 最後に使ったアイテム名を保持する
    static last_use_item = "";
    
    name = _name;
    price = _price;
    
    // staticをつけるだけ
    static Use = function() {
        // アイテム使用時、static変数に自身の名前を格納する
        self.last_use_item = name;
        show_debug_message($"{name}を使用しました。")
    }
}

呼び出し方も通常の関数と変わらず呼び出せます

var potion = new Item("ポーション", 50);
var antidote = new Item("毒消し", 100);

// ポーションを使用
potion.Use();
// 毒消しを使用
antidote.Use();

これだけです。


メンバ関数にstaticをつけるメリット

先ほども言いましたが、「大きなメリット」があり、「コンストラクタ内のメンバ関数にはstaticは必須」と覚えておくと良いです。


理由として、先ほどから説明しているように、staticをつけると初期化が1度だけ行われて
それ以降は同じ値を参照するのがわかったと思います。


この仕様と、コンストラクタの関数が持つ「全て同様の処理が実行されることが当たり前」という仕様がマッチするため、
コンストラクタ内の関数にはstaticをつけても何も不便なことはありません
つけてもつけなくても動作に変わりが無いわけですね。


むしろ、本来関数を定義する度にメモリが消費されますが、
staticで宣言した場合は先ほど申し上げた通り初期化処理は1度しか通らないため、


どんなに同じコンストラクタをnewしても、staticをつけた関数は1回分しかメモリを消費しないのです


staticの有無による消費メモリ比較

試しに、staticをつけてない関数とつけた関数でメモリを計ってみましょう。

今回はメモリの差がわかりやすくするために、同じコンストラクタを10000個ほど生成します。

関数にstaticがついていない場合

// アイテムの名前と値段を定義する
function Item(_name, _price) constructor {
    // 最後に使ったアイテム名を保持する
    static last_use_item = "";
    
    name = _name;
    price = _price;
    
    // staticをつけないで関数を宣言
    Use = function() {
        // アイテム使用時、static変数に自身の名前を格納する
        self.last_use_item = name;
        show_debug_message($"{name}を使用しました。")
    }
}

var array = array_create(0);
// 10000ぐらい作ってみる
repeat(10000){
    array_push(array, new Item("item", 0));
}

上記のコードを実行してメモリを確認してみます。

18.41MBメモリを使用している


次はstaticを関数につけて実行してメモリを確認してみます。


関数にstaticがついている場合

// アイテムの名前と値段を定義する
function Item(_name, _price) constructor {
    // 最後に使ったアイテム名を保持する
    static last_use_item = "";
    
    name = _name;
    price = _price;
    
    // staticをつける
    static Use = function() {
        // アイテム使用時、static変数に自身の名前を格納する
        self.last_use_item = name;
        show_debug_message($"{name}を使用しました。")
    }
}

var array = array_create(0);
// 10000ぐらい作ってみる
repeat(10000){
    array_push(array, new Item("item", 0));
}

17.52MBメモリを使用している

なんと、1MBほどの差が出ています

今回は関数1つでしたが、これを1つのコンストラクタに関数を10個など宣言していたら大量のメモリを消費してしまいます
わかりやすくするために今回は10000も作りましたが、
冗談ではなく実際にこの程度のコンストラクタを使用するケースはあります


このような無駄なメモリを消費しないためにも、
必ずコンストラクタ内の関数にはstaticをつけましょう


終わりに

今回はGMLでのstaticについて解説しました。

正直コンストラクタ内の関数以外ではあまり用途はありませんが、
それだけでも大きなメリットがあります。


関数内での変数としてstaticを使用する時は、
グローバル変数を増やしたく無い時や名前空間をつけたい場合に限るでしょう。


個人的にグローバル変数を増やしたく無いため、static変数もよく使っていますが、
好みだと思います。


慣れないうちは、「コンストラクタ内の関数にstaticをつける」と覚えておけば問題ないです。
絶対につけてください。

*1:便宜上「static専用領域」と呼んでいるだけで、公式でそう呼ばれているわけでは無い

【GMS2】C++を使った拡張機能の作り方


はじめに

この前、初めてC++でGameMakerStudio2の拡張機能を作りました。
その時なんですが、実は拡張機能の作り方がちょっと難しくて苦戦しちゃいました。(ジョウホウガスクナイ)
そこで、自分の備忘録として今回はC++での拡張機能(.dll)の作り方についてまとめようと思います。


今回の記事はWindows専用です。

C++のビルド環境について

今回の記事では、C++のビルド環境にVisualStudioを用いようと思います。
無料で使えて高性能ですので、まだC++のビルド環境が無かったら、VisualStudioを使用することをオススメします。
既にC++のビルド環境が存在するのであれば、この項目はスキップしてOKです


VisualStudio2022のインストール

現在(2025/08/14)で最新版のVisualStudio2022をインストールします。

下記サイトにアクセスして、VisualStudio2022 Communityインストーラーをダウンロードします。

visualstudio.microsoft.com

これをクリック

ダウンロードされたインストーラーをダブルクリックします。

こんな感じのアイコン

インストール前にこのような画面が出てきたら「続行」を選択してください。

続行でOK

そしたら下記のような、インストールする項目を選ぶ画面が出てきます。

結構選択肢がある

今回は .dllファイルのビルドさえ出来ればよいので、
デスクトップとモバイル」の中にある「C++によるデスクトップ開発」にチェックを入れます。

これだけでOK

そしたら、右下インストールクリックします。

他の項目は変更しなくてOKです

インストール完了までは時間かかりますが、待ちましょう...。

これなっがい...

下記のようなウィンドウが表示されたらインストール完了です。OKを押しましょう

OK!

もし仮に、さらにVisualStudioで使いたいテンプレートがあっても後からインストールできるので安心してください。


Dllプロジェクトの作成

では次は、VisualStudioでDllをビルドできるプロジェクトを作成しましょう。

Windows検索画面から「Visual Studio 2022」と検索して、ソフトを起動します

検索!

初回の起動の場合、Microsoftアカウントのサインインなどの手続きがあります。
手順に従ってサインインしてください。


サインイン後、プロジェクト起動画面になります。
新しいプロジェクトの作成を選択してください。

新規作成!

次はテンプレート選択画面になるため、
ダイナミック リンク ライブラリ(DLL)」を選択して「次へ」を選択します。

DLLを選択して次へ

このような画面が表示されればプロジェクトの作成に成功しています。

初期画面


C++で関数を実装する

さて、ここからが本番ですね。
C++で関数を実装していきましょう。


GML関数公開用ファイル作成

GMLに公開する関数をまとめるファイルを作ります。
右の「ソリューションエクスプローラ」から「ヘッダーファイル」と書かれたパッケージを選択肢、
右クリックをします。

ここで右クリック

右クリックするとメニューが出てくるので、「追加」->「新しい項目」を選択します。

これ

すると作成コンパクトビューが表示されるので、「すべてのテンプレートを表示」をクリックします。

他は変更しなくてOKです

次はテンプレート選択画面が表示されるので

  • テンプレートから「ヘッダーファイル」を選択
  • ファイル名は「GmlExportedFunction.h」に指定
  • 右下の「追加」をクリック

してください。

流れはこう

ヘッダーファイル内に「GmlExportedFunction.h」ファイルが作成されていればOKです。

これで成功!

C++では、このように作成したヘッダーファイル(.h)に関数の宣言を書いていきます


ヘッダーファイルに関数を宣言する

次は先ほど作成したヘッダーファイルに関数を宣言します。
ソリューションエクスプローラ」から「GmlExportedFunction.h」をダブルクリックして開きます。

このような画面になればOK


公開用のマクロ宣言

GMLに関数を公開するには、関数を定義する前
extern "C" __declspec(dllexport)」と記述する必要があります。


とは言えこれを毎回書くのはめんどくさいので、
冒頭に関数公開用のマクロを定義して簡略化します。

// GameMakerStudio2向けにDLLを作る際、
// 関数をGMLから呼び出せるようにするためのエクスポートマクロ。
// extern "C"     : C言語形式でエクスポートし、名前修飾を防ぐ
// __declspec(dllexport) : この関数をDLL外部に公開する(Windows専用)
#define GMS2EXPORT extern "C" __declspec(dllexport)

GML側に公開する関数を宣言する際は、このマクロを用います

// 例
GMS2EXPORT void Function();

使用できる型

GMLに公開する関数の戻り値引数には「double」型と「char*」型しか指定できません


GMS2EXPORT double AddValue(double a, double b);

double型は数値受け取る or 返す時に使います。
整数小数どちらも使用することが出来ます。
GameMakerでいう「Real」型と同じ扱いが出来ます。


GMS2EXPORT const char* ChangeText(const char* text);

char*型は文字列受け取る or 返す時に使います。
GameMakerでいう「String」型と同じ扱いが出来ます。
charではなくchar*なので注意してください。
※「*」を付けないと正しく文字列を受け取れません。

▶なんで「*」を付ける必要があるのか?


C++に慣れていない方にはなかなか見慣れない書き方だと思います。
実は変数に「*」を付けるか付けないか変数の扱い方が変わります。


charは1バイト分の値しか保持できません。文字で言えば1文字分です。
そのため、文字列全体を扱うことはできません。


char*は「char型のデータが並んでいる場所の先頭アドレス」を保持します(所謂、ポインタ)。
文字列は複数の文字が連続して並んでおり、その先頭位置さえわかれば、
後ろの文字も順番に読み取ることができます。
* をつけることで、char型のポインタを保持できるようになるわけです。
なので、今回は * が付いているわけですね。


とは言えC++に慣れていないとよくわからないと思うので、
GMLの「String」はC++だと「char*」で、
char」は1バイトしか値が入らないものだ、と覚えておけばOKです。



サンプル関数を宣言

では、上記を踏まえていくつか関数を定義してみます。
先程のファイルに対して下記を記述してください。

/// <summary>
/// 複数の double 値を受け取り、その合計を返す関数
/// </summary>
/// <param name="a">1つ目の数値</param>
/// <param name="b">2つ目の数値</param>
/// <param name="c">3つ目の数値</param>
/// <returns>引数の合計</returns>
GMS2EXPORT double AddThreeNumbers(double a, double b, double c);

/// <summary>
/// 文字列を受け取り、"Hello, " を先頭につけて返す関数
/// </summary>
/// <param name="text">文字列</param>
/// <returns>"Hello, " + text の文字列</returns>
GMS2EXPORT const char* SayHello(const char* text);

こんな感じで宣言すればGMLから呼び出すことが出来ます


ソースファイルに関数を実装する

次はソースファイル(cpp)に関数の中身を実装します。


C++ではヘッダーファイル(.h)に関数を定義し、ソースファイル(.cpp)に中身の処理を実装します。

ソースファイルを作成するため、「ソリューションエクスプローラ」から
ソースファイル」フォルダを選択して右クリックをし、新しい項目の追加を行います。

右クリック!

テンプレート作成画面では「C++ファイル(cpp)」を選択し、
ファイル名を「GmlExportedFunction.cpp」に変更して「追加」をクリックしてください

名前は必ずヘッダーと同じにするように

このような画面になればOKです!

GJ!!


コード実装

ソースファイルを作成できたので、先程作った関数の中身を実装していきます。
先ほどヘッダーファイルで定義した2つの関数を実装したコードが下記です。

#include "pch.h"
#include "GmlExportedFunction.h"
#include <string>

double AddThreeNumbers(double a, double b, double c) {
    return a + b + c;
}

const char* SayHello(const char* text) {
    // GMLから呼び出す場合、返すポインタは関数終了後も有効である必要があるため、
    // 静的な std::string に文字列を保持し、その内部バッファへのポインタを返す。
    static std::string result;
    result = "Hello, ";
    result += text;
    return result.c_str();
}

こちらをcppファイルに書き込んでください。


プロジェクトのビルド

次はビルドを行います。
デフォルトだとDebugビルドになっていると思うので、
それをReleaseビルドに変更します。


画面上の「Debug」と書かれた部分をクリックしてください。

メニューが開きます

それを「Release」に変更してください。

こうなればOK

この状態で「Ctrl + B」もしくは、
上のメニューの「ビルド」をクリックして、出てきたメニューから「ソリューションのビルド」を選択してください。

これ

ビルドが完了して、出力ウィンドウが下記のように表示されればビルド成功です!

ビルド成功!


GameMakerプロジェクトにDllファイルをインクルードする

次は、先程作ったdllファイルをGameMakerに追加します。

ソリューションエクスプローラ」のプロジェクトを右クリックし、
エクスプローラーで開く」を選択します。

わかりにくいけどこれ

そしたら開いたフォルダーからx64」->「Release」と移動します。
すると、中に「プロジェクト名.dll」というファイルが存在するフォルダーに遷移します。

これがあればOK

そしたら、このdllファイルをGameMakerのプロジェクトまでドラッグしましょう。

こんな感じに追加する

これで追加が完了です。


GMLからC++の関数を呼び出す

ついにここまできました。
最後に、GMLC++の関数を呼び出すようにします。


GMLからC++の関数を呼び出すには下記の流れが必要です。

  • C++の関数をGMLで定義する
  • GMLからC++の関数を呼び出す
  • ゲーム終了時、ロードしたdllファイルをアンロードする

といった手順です。


C++の関数をGMLで定義する

まずはC++の関数をGMLで定義します。


そのために、まずはオブジェクトを1つ作りましょう。
Createイベントを追加してください。

オブジェクト名は何でもOKです

C++の関数をGMLで定義するには「external_define」関数を使います。


external_define

manual.gamemaker.io

external_defineDLLの関数を呼び出すための設定を行う関数です。
関数名呼び出し規約戻り値の型引数の数を指定します。

// 構文
external_define(dll, name, calltype, restype, argnumb, argtype[0], argtype[1], ...);
引数名 説明
dll 文字列 DLLファイル名(パス込み可)
name 文字列 DLL 内の関数名
calltype 定数 呼び出し規約(dll_cdecl または dll_stdcall)
restype 定数 戻り値の型(ty_real または ty_string)
argnumb 数値 引数の数(0〜15)※4つ以上はすべて ty_real
argtype[...] 定数 各引数の型(ty_real または ty_string)

呼び出し規約とは
* dll_cdecl - C/C++標準の呼び出し規約(通常はこちらを使用)
* dll_stdcall - Windows API 標準の呼び出し規約(Windows専用)


について
* ty_real … 実数(double)
* ty_string … 文字列(char*)


この関数を使って定義します。


external_defineを使って関数の定義

では、先程C++で宣言した関数を定義してみましょう。
オブジェクトのCreateイベントに下記を記述してください。

// AddThreeNumbers 関数を DLL から読み込み
// 引数: 実数3つ、戻り値: 実数
AddThreeNumbers = external_define(
    "GameMakerDll.dll",    // DLLファイル名
    "AddThreeNumbers",     // DLL内の関数名
    dll_cdecl,             // 呼び出し規約(C/C++標準)
    ty_real,               // 戻り値型(実数)
    3,                     // 引数の数
    ty_real, ty_real, ty_real // 各引数の型
);

// SayHello 関数を DLL から読み込み
// 引数: 文字列1つ、戻り値: 文字列
SayHello = external_define(
    "GameMakerDll.dll",    // DLLファイル名
    "SayHello",            // DLL内の関数名
    dll_cdecl,             // 呼び出し規約(C/C++標準)
    ty_string,             // 戻り値型(文字列)
    1,                     // 引数の数
    ty_string              // 引数の型
);

これでOKです!


GMLからC++の関数を呼び出す

次は、external_defineで定義した関数を呼び出します

呼び出すにはexternal_call関数を使用します。


external_call

manual.gamemaker.io

external_callは、external_define()で登録したDLLの関数を実際に呼び出すための関数です。
事前に登録した関数IDと、必要な引数を渡します。

//構文
external_call(id, args[0...15]);
引数名 説明
id External Function external_define() で取得した関数ID
args[...] 実数 または 文字列 関数に渡す引数(型は登録時の指定と一致させる)

戻り値 登録時に指定した戻り値の型に応じて、実数(real)または文字列(string)が返ります。


この関数を使用することで、先ほどexternal_defineで定義した関数を呼び出せます。


external_callを使って関数を呼び出す

では実際にexternal_callを使って関数を呼び出してみましょう!
先ほどのオブジェクトのCreateイベントの続きに、下記を記述してください。

// DLL内の AddThreeNumbers 関数を実行(10 + 20 + 30)
var _addThree = external_call(AddThreeNumbers, 10, 20, 30);

// DLL内の SayHello 関数を実行("Hello, hogehoge" を返す想定)
var _sayHello = external_call(SayHello, "hogehoge");

// 結果をデバッグ出力
show_debug_message($"AddThreeNumbers {_addThree}");
show_debug_message($"SayHello {_sayHello}");

これで呼び出しはOKです!


ロードしたdllファイルをアンロードする

最後に、ロードしたdllファイルをゲーム終了時にアンロードする方法です。
これを行わないとメモリが残り続ける可能性があるため注意が必要です。

アンロードするためにはexternal_free関数を使用します。


external_free

manual.gamemaker.io

external_freeは、指定したDLL使用を終了し、そのメモリを解放します。
ゲーム中でそのファイルが不要になった時に呼び出します。

// 構文
external_free(name);
引数名 説明
name 文字列 解放したい DLL または dylib の名前(パス込み可)

これだけです。


external_freeを使ってDLLのアンロード

先ほど作ったオブジェクトにClean Upイベントを追加し、
そこに下記のコードを追加します。

// GameMakerDll.dll のメモリを解放
external_free("GameMakerDll.dll");

これで解放は完了です。


実践

では、先程作ったオブジェクトをルーム内に配置してログをチェックしてみましょう!

AddThreeNumbers 60
SayHello Hello, hogehoge

このようなログが表示されれば成功です!!


まとめ

今回はGameMakerStudio2でC++を使った拡張機能の作り方についてまとめました。
色々セットアップや組み方が特殊でなかなかわかりにくいんですよね。


今回はセットアップの部分から説明したので、ある程度わかりやすく書けたと思います!
これを経て、C++を使った拡張機能がもっと増えてくれたらなと思います。


今後はこの拡張機能でのC++での組み方について、Tipsなどまとめたいと思います。
特にsurfaceや構造体等のデータ渡しや、別ウィンドウの作成などまとめていく予定です。

【GMS2】C++拡張機能でデバッグウィンドウを作ってみた


はじめに

どうも、最近GameMakerのC++拡張機能に初めて触れました。
今回は自分の拡張機能作成の練習も兼ねて、
デバッグ画面を別ウィンドウで表示する機能を作ってみました。

こんな感じです

せっかく作ったので、公開してみました。

drive.google.com

今回の記事では、この拡張機能について紹介します。
C++拡張機能はこんなのが作れるんだ!というのが伝われば良いなと思います。

※2025/11/22追記
Youtubeにこのデバッグ機能を紹介している動画を投稿しました。
動作を確認したいのであれば動画を観た方がわかりやすいです。

https://youtu.be/ahT66lYF2cY?si=u6gze6iipNAMEEKY

今回作成した拡張機能Windows専用です

概要

ゲーム内の画面とは別デバッグ用のカメラを用意して、
そのカメラの映っている内容をゲームとは別のウィンドウに映すという内容です。

こんな感じ

メインのゲーム画面とは完全に独立しているので
ゲームプレイを阻害すること無くデバッグが行えます


導入方法

下記のリンクから「DebugWindowExtension.zip」ファイルをダウンロードして、展開してください。
drive.google.com

中に「DebugWindowExtension.yymps」が存在していればOKです。


次は導入したいGameMakerStudio2のプロジェクトを開き、
上のタブメニューから「ツール」->「ローカルパッケージのインポート」を選択します。

ここです

ファイル選択を促されるので、先程展開した「DebugWindowExtension.yymps」を選択します。

これ

選択すると、下記のようなインポート画面が開きます

この画面です

そしたら真ん中の「全て追加」をクリックした後、
左下の「インポート」をクリックします。

必ず真ん中のボタンからクリック

プロジェクトに「DebugWindowExtension」というフォルダーが追加されていればOKです。

これでOK!


セットアップ

DebugWindowExtensionフォルダーを開いた中にある「objDebugWindowExtension」を
最初に遷移するルームに配置してください

このオブジェクトを配置する
絶対最初のルームにしてください

これで起動時にデバッグウィンドウが表示されます。

注意!
今回はデバッグ機能ですので、デバッグビルドの時のみデバッグウィンドウが表示されます。
つまり、F6でのビルドのみに対応しています。(F5でのビルドだとデバッグウィンドウは表示されない)

一応リリースビルド時はデバッグウィンドウの処理を行わないようにしていますが、
リリースビルド時はプロジェクトから取り除いたほうが安全です。

機能紹介

ここからはデバッグウィンドウの機能を紹介します。
まだまだ機能は少ないですが、暇があったら追加しようと思います。


カメラ操作

このデバッグウィンドウは、ゲーム画面を映しているカメラとは別のカメラを用いているため、
ゲームプレイとは独立して操作ができます

マウスホイールをクリックしながらのドラッグで画面移動マウスホイールをスクロールすることで、カメラをズームイン/ズームアウト

が行えます。

通常カメラの外まで確認できる

ゲーム画面外までプレビューできるので、
画面外の内容まで確認できるのが強みです。


カメラ追従

カメラインデックスを指定することで、そのカメラに追従させることが出来ます
例えば、ゲーム内で使用しているメインのカメラのインデックスを指定すると
ゲームの画面に追従するように移動してくれます。

こんな感じに設定できます

デバッグウィンドウ内で右クリックをすると「特定のカメラを追従する」というメニューが出ます。
そこにカーソルを合わせるとサブメニューが出てくるので、そこから追従したいカメラインデックスを指定します。

このように表示されます

この機能は、例えばマップ用のカメラやイベント用のカメラなど、
別カメラが何を移しているのか知りたい時などに使えます。


追従中、再度右クリックをすることで「◯番のカメラ追従を辞める」というメニューが出るので、
そのメニューを選択することで追従をキャンセルできます。

今回は0番


オブジェクトデバッグメニュー

デバッグウィンドウで左クリックを行うと、マウス位置のオブジェクトを選択できます。

赤枠になれば選択済み

オブジェクト選択中に右クリックを押すと、オブジェクト専用メニューが表示されます。

選択中のオブジェクトのメニューが追加

それぞれのメニューについて解説します。


情報を見る

オブジェクトの情報ウィンドウを新たに表示します。

後半がごちゃごちゃ

オブジェクトの基本情報(座標やスプライト情報等)や、
オブジェクトのインスタンス変数の値を全て表示されます。


毎フレーム更新されているので値のチェックに活用できます。
複数のオブジェクトの情報ウィンドウを表示できるので、値の比較などにも活用できます。

変数の比較ができる!


この情報ウィンドウ上で左クリックすることで変数を選択することが出来ます。

クリックした変数はハイライトが変わる

右クリックをすることでメニューを出し、コピーやログに出力させることが出来ます。

右クリックメニュー


ログ出力を行った変数は毎フレームログに出力されます。
値をさらに詳しく調査したいときに有効です。

ログ出力の例

ログを止めたい時は、再度右クリックをすることで
ログ出力をオフにする」メニューが出るのでそれを選択してください。

ログ出力をオフにする

値を全てコピーするを選択すると、文字通りすべての情報をクリップボードにコピーします。


カメラに追従させる

カメラに追従させるメニューを選択すると、デバッグウィンドウがそのオブジェクトをカメラの中心に映して移動します。

リフトの追従をさせてみる

敵オブジェクトなど、特定のオブジェクトに注目したい時に役立ちます。
追従をキャンセルしたい時は、再度右クリックをして「カメラ追従を辞める」を選択します。

右クリックはどこでもOK


オブジェクトを破棄する

その名の通り、そのオブジェクトを instance_destroy() します。

確認画面がまだ未実装なので唐突に消えます。いずれ実装するのでお待ち下さい...。


複数選択

左クリックをした際、オブジェクトが重なっていた場合は複数選択されます。

複数選択しています

また、Ctrlキーを押しながらクリックすることで複数選択できます

Ctrlキーを押しています

複数選択後の右クリックのメニューでは、選択したオブジェクトが全て表示されます。

オブジェクト名とオブジェクトIDで表示されます

また、選択した全てのオブジェクトに対して動作を行うことも出来ます

複数の情報ウィンドウを同時に出す

カメラ追従は一つのオブジェクトに対してしか行えないので注意してください。


その他の機能

F1キーを押すことで画面上のデバッグ情報をオフに出来ます。

fpsが見切れてる

デバッグウィンドウの画面サイズを変更すると画面が拡大されます。

処理は重くなるので注意

F2キーを押すことで画面サイズとズーム倍率をリセットできます。

カメラ位置は維持されます


注意点

今回、デバッグ用のカメラのインデックスに7を指定しています。
なので、ゲーム内でインデックス7のカメラを使用している場合
この拡張機能は使えません


もし他のインデックスに変更したい場合は、
DebugWindowExtension/Define フォルダ内に存在する DebugWindowExtensionDefine スクリプトを開き、
中の「DEBUG_CAMERA_ID」のマクロの値を変更してください。

// デバッグ用に使用するカメラのID
// デフォルトは7を使用しています
// 7番のカメラをゲーム内で使用している場合は不具合が生じるため、
// 使用していないカメラIDを指定してください
// マクロの値を変更するだけで済みます
// 例: DEBUG_CAMERA_ID 3 // 7のカメラは使用するので、デバッグ用のカメラインデックスを3に変更する
#macro DEBUG_CAMERA_ID 7

まとめ

今回は私が作ったデバッグウィンドウ拡張機能について説明させていただきました。
ある程度便利に作りましたが、一週間くらいで作ったので機能不足ではあります。


どちらかと言うと、「C++拡張機能を使うとこんなのが作れるんだ」と言った機能紹介になれば良いなと思っています。


私も今回始めてC++拡張機能を使いましたが、色々苦戦したり学びがありました。
次回以降はC++拡張機能の作り方や実装方法についてまとめていきたいと思います。


また、今回の拡張機能に対して、実際に使ってみての感想や要望があったら
私のXやMisskeyの垢にご連絡ください。
よろしくお願いいたします。

X : https://x.com/hemuhununeOrang/

Misskey : misskey.io