Insight into Nie:

ゲーム終了時(Alt+F4含む)の自動セーブ

この記事では、ゲームデータの自動保存のうち、ゲーム終了時の自動セーブについて解説します。

動機

Nieでは、特定のタイミングで、ゲームデータが自動的に保存されます。これを一時データと呼びます。ゲーム起動時やソフトリセット時(F12キー)には、この一時データを読み込んで、ゲームを再開します。

この自動セーブは、ゲーム終了時にも実行されます。これにより、プレイヤーは、次にゲームを起動するとき、前に遊んでいた続きのように、ゲームプレイを再開することができます。

メニューを開いたときや、特定のイベントを実行した際に、自動セーブを実行することは難しくありません。そのタイミングで自動セーブを実行するコードを呼び出せばよいからです。

しかしゲーム終了時に自動セーブを行うには、特殊な処理が必要になります。RGSS3のメインループは、ウィンドウを閉じたり、Alt+F4を行うことなどによる、アプリケーションの終了を検出する機能を提供していないからです。

アプリケーション終了の検出

WindowsのGUIアプリケーションは、Windows APIを介して生成したGUIコンポーネントに対する操作などを、イベントとして受け取ります。受け取ったイベントに応じた処理を実装することで、アプリケーションはGUIへの操作に対応する機能を提供することができます。

たとえばウィンドウを閉じる際には、WM_CLOSEというイベントがOSからアプリケーションに送られてきます。

それらのイベントを受け取るために、アプリケーションはイベント発生時に呼び出されるコールバック関数を登録することができます。このようなイベントを処理するための関数を(一般的にはイベントハンドラといいますが、WindowsのGUIアプリケーションではウィンドウに紐づけるものを特に)ウィンドウプロシージャと呼びます。

ここまでをまとめると、ゲーム起動時に生成されるウィンドウに対してウィンドウプロシージャを登録し、WM_CLOSEイベントを受け取ることで、アプリケーションの終了を検出することができる、ということになります。

Rubyスクリプトからのイベント検出

RGSS3ではゲームロジックをRubyスクリプトで記述しますが、Rubyからは前項で説明したウィンドウプロシージャを登録することができません。

そこでNieではitefu_hook.dllという専用のDLLを使っています。これはNieで使用しているItefuというライブラリの機能の一部として用意されているシンプルなDLLで、C++で書かれています。特定のウィンドウにウィンドウプロシージャを登録し、指定したイベントを検出したり、デフォルトの処理を中断させることができます。

RGSS3に組み込まれているRubyには、DLLを利用するための拡張ライブラリが付属しています(名称は〝Win32API〟ですが、自前のDLLも呼ぶことができます)。この機能を使ってitefu_hook.dllの関数を呼び出すことで、Ruby側からWM_CLOSEを検出することができるようになります。

Nieでの実例

まずWM_CLOSEを定義します。これはWindows APIで定義されている値をそのまま指定します。

1
WM_CLOSE = 0x10

次にitefu_hook.dllを利用して、WM_CLOSEをフックします。Itefuには、itefu_hook.dllを簡単に利用できるよう、Itefu::Win32::Hookというモジュールが用意さているので、それを使用します。

1
2
3
4
5
6
7
8
# ゲームウィンドウのウィンドウハンドルを取得
hwnd = Itefu::Win32::getWindowHandle

# itefu_hook.dllを有効化する
Itefu::Win32::Hook.enable(hwnd)

# WM_CLOSEをフックする。デフォルトの処理は無効化する。
Itefu::Win32::Hook.hook_window_message(WM_CLOSE, true)

これによって、WM_CLOSEが送られて来たかどうかを検出できるようになります。

1
2
3
4
5
# WM_CLOSEが発生したかどうかを判定する
if Itefu::Win32::Hook.window_message_sent?(WM_CLOSE)
  # WM_CLOSEが発生した
  # 自動セーブなど、終了処理を行って、メインループを抜けるようにする
end

WM_CLOSEをフックする際に、Itefu::Win32::Hook.hook_window_message(WM_CLOSE, true)と、第2引数でtrueを指定していますが、これによってウィンドウプロシージャはデフォルトの処理(WM_CLOSEならウィンドウを閉じてアプリケーションを終了するなど)を行わなくなります。

ウィンドウプロシージャのデフォルトの処理でウィンドウを閉じられると、終了処理を行う前にアプリケーションが終了してしまうので、自動セーブができません。従って、このケースでは、WM_CLOSEを検出した後、ゲーム中でゲーム終了を行ったときと同じような処理を行ってから、アプリケーションを終了するようにする必要があります。

以上で、RGSS3のゲームを、ウィンドウの閉じるボタンやAlt+F4で終了した場合でも、通常のゲーム終了時と同じ終了処理を行えるようになりました。そこで自動セーブを行うようにすれば、ゲーム終了時の自動セーブを実装できます。<終>