Heliodorさんをフォローして、最新情報をチェックしよう!

マイページへ

Ci-enはクリエイターに対して、金銭的な支援を送ることができるサービスです。

投稿記事

2018年 09月の記事(4)

たまにはプログラムの話2

前回、ヴィータ大脱出での不正終了イベントログの取得について書きましたが、あれって結構不格好な実装だったんですよ。なんたって、不正終了した時にそのイベントを取得できるのは次回起動時でしたからね。不正落ちしてそのまま電源を切って、翌日起動とかされると、15分制限に引っかかってイベント取得できなかったわけです。

かといってイベントの検索範囲を24時間とかにするとイベントの量が多くなり過ぎて検索に時間が掛かる=起動に時間が掛かりますし、どうせなら不正終了したその場でイベントを取得してテキストファイルに書き出してダイアログ表示したいじゃないですか。

そこで、もう一工夫して一度はボツにした SetUnhandledExceptionFilter を使ってみることにしました。
SetUnhandledExceptionFilter を使った時の問題点は、例外検出しても、その時点ではまだエラーイベントが作成されていないため、詳しいエラー情報を取得できないというものでした。ならば、アプリが完全に終了するまで待ってやればよいではないかと。


というわけで。次のように処理内容を変更しました。

  • ゲーム起動時に SetUnhandledExceptionFilter で例外フックを仕掛けておく
  • 例外フックが発動したら、自分のプロセスIDをコマンドラインパラメータにしてプログラムBを起動する
  • そのまま終了させる

  • プログラムBは起動時にコマンドライン引数を調べ、例外発生側のプロセスIDが指定されていれば、そのプロセスが終了するまで待機

  • 終了を確認したらWindowsのイベントログを調べる
  • 該当エラーイベントが見つかったら内容をテキストファイルに書き出してダイアログを出す

ここで、プログラムBとは誰ぞや?ということですが、これはゲームプログラム自身です。
ただのゲームとして起動されたのか、それとも例外報告用として起動されたのかはコマンドラインパラメータで識別します。

全体の流れは以下の通りです

WinMain(HINSTANCE hCurrInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
	if (例外報告モードで起動された?(lpCmdLine)) {
	
		DWORD pid = コマンドラインからプロセスIDを取得(lpCmdLine);
		
		// そのプロセスが終わるまで待つ
		HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
		WaitForSingleObject(hProcess, INFINITE);
		CloseHandle(hProcess);
	
		// 例外を出したプロセスが終了した。
		// Windowsイベントログに反映されるまでの時間を考慮して、少し待機する
		Sleep(200);
		
		std::string event_text = イベントログを検索();
		
		テキストファイルに保存(event_text);
		
		ダイアログメッセージ(event_text);
		
		// このまま何もしないで終了
	
	} else {
		
		// 普通のゲームモードで起動された
		SetUnhandledExceptionFilter(onExceptionOccurred); // 例外フックを登録
		
		...
		// 普通のゲーム処理
		...
	
	}
	return 0;
}

で、肝心の例外フックはこんな感じ

LONG WINAPI onExceptionOccurred(struct _EXCEPTION_POINTERS *e) {

	char cmdline[256];
	コマンドライン引数作成(cmdline, GetCurrentProcessId()); // <---自分のプロセスIDも含めておく

	// 自分自身の実行ファイル名
	char self_path[MAX_PATH] = {0};
	GetModuleFileName(NULL, self_path, MAX_PATH);

	// 例外チェックモードで自分の複製を起動する
	ShellExecute(NULL, "OPEN", self_path, cmdline, NULL, SW_SHOWNORMAL);

	return EXCEPTION_CONTINUE_SEARCH; // EXCEPTION_CONTINUE_SEARCH にしないとアプリ終了時にwindowsエラーログに残らない
}

これで実験してみると、見事に不正落ちしたその場でエラーの後始末が始まります。
ようやくスマートに後始末ができるようになりました。めでたしめでたし。


サンプルを置いておきますので、興味がありましたらどうぞ。

ErrorReport_sample.zip (7.74kB)

ダウンロード
\いいねで応援!/

らくがき

ほぼラフ線画のらくがきです。
恥ずかしいので今回もフォロワー以上限定で

フォロワー以上限定無料

フォロワーが増えるとヘリオドールの製作速度が精神的に向上します。

無料
【 白銀ベリル 】プラン以上限定 月額:500円

先月以前に投稿された記事のため、この限定特典を閲覧するには[ バックナンバー購入 ]する必要があります。

月額:500円
購入する
\いいねで応援!/

イラスト練習中2

例によってエロ絵なのでフォロワー以上限定です。

フォロワー以上限定無料

フォロワーが増えるとヘリオドールの製作速度が精神的に向上します。

無料
【 白銀ベリル 】プラン以上限定 月額:500円

先月以前に投稿された記事のため、この限定特典を閲覧するには[ バックナンバー購入 ]する必要があります。

月額:500円
購入する
\いいねで応援!/

カラーパレットの話

昔のハードウェアにはカラーパレット切り替え機能というものがあり、RPGの色違いの敵キャラや、格闘ゲームの2Pカラーは大体この機能で表現されていました。
プログラム側で色を切り替えるので、グラフィッカーがいちいち画像を用意する手間も掛かりませんし、画像データもひとつ分で済むので容量の節約にもなります。

今はパレット機能自体が廃れたのと、データ容量の制限もあまり無いので、全ての色違いグラフィックを予め用意してあるパターンの方が多いと思われます。


ヴィータ大脱出でも、いかにも2Pカラーなユニゾンモード(「ヴィータのひみつ」で解放される白いヴィータ)を作ったのですが、これはパレット機能ではなく予め色違いグラフィックを用意してあるパターンです。

ではどうやって色違いグラフィックを用意したのか?と言うと……全部の絵を手作業でユニゾンカラーに塗り直しました。
塗り直すだけだし、そんな大変じゃないだろうと軽く考えてたのですが、なんと言うかもう、めちゃくちゃ大変でした。

ニュートラルポーズだけでもこの枚数に各脱げ段階と帽子ハンマーの切り替え(これは後にレイヤー化しましたが)、これを全部塗り直すというのだから、少し考えてみれば大変なのは当たり前なのですが……。

途中で「専用の画像変換プログラムを作っちゃうか?」とも考えたのですが、一括では処理できない部分があるし、その頃既に1/3ぐらい塗り直していたので、もはや後には引けずに結局全部手作業でやってしまいました。


後半はもう「色指定で範囲を選択して塗り潰し……色指定で範囲を選択して塗り潰し……」とブツブツ言いながら単純作業を繰り返すだけの人生みたいな状態でした。
そしてこの作業が終わった後に、ドット絵ではないイラストもあったことを思い出し、自分は何故こんなことをしているのだろうと自問自答したりしました。

おそらくヴィータちゃんの絵をたくさん描いた選手権があったら結構な上位に入れるだろうと自負しておりますが、それをユニゾンヴィータに塗り直した選手権があれば間違いなく優勝だろうと確信しております。アホですね。



まあとにかく、色違いキャラを自力で塗り直すのはもう二度とやるまいと誓いました。
(どちらかと言うと段階的に服が脱げる方が元凶な気もしますがそれはさておき)

やはりパレット切り替えはプログラムでやるしかない!と考え、それなりの苦労があって何とか実現しました。
こうして「ヴィータ大脱出」の次に作った「滅びの国の王女」ではそのパレット切り替えプログラムを組み込み、大量のカラー切り替えが可能になりました。(宣伝)

余談ですがこのカラー、左からFC紫髪、SFC金髪、ローレシア、サマルトリア、ロンダルキア、4勇者、アリーナ、モンバーバラ姉妹、5主人公、ビアンカ、フローラ、デボラをイメージして作りました。
他に12カラーありますが元ネタがあるのはここまでで、あとは結構適当に作りました。どうでもいですね。

\いいねで応援!/

記事を検索