オレンジブログ

オレンジブログ

GameMakerのちょっとしたTipsとか

【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や構造体等のデータ渡しや、別ウィンドウの作成などまとめていく予定です。