続・最近の作業まとめ
例の液体の処理です。
今回もまたフォロワー以上で。
フォロワー以上限定無料
まずは無料プランで様子見を。 お気軽にフォローしてみて下さい。
無料
【 500円 】プラン以上限定 支援額:500円
このバックナンバーを購入すると、このプランの2021/02に投稿された限定特典を閲覧できます。 バックナンバーとは?
支援額:500円
クリエイター支援サイト Ci-en
フォローするにはユーザー登録が必要です。
Heliodor 2021/02/28 17:27
例の液体の処理です。
今回もまたフォロワー以上で。
まずは無料プランで様子見を。 お気軽にフォローしてみて下さい。
無料
このバックナンバーを購入すると、このプランの2021/02に投稿された限定特典を閲覧できます。 バックナンバーとは?
支援額:500円
Heliodor 2021/02/25 17:37
あやかし紅白戦のメモリー使用量は今のところ700MBぐらい。
ヴィータ大脱出は200MBを超えないように作っていたので、あやかし紅白戦は3~4倍ぐらい贅沢に使っています。
それでも近年のWindows10搭載PCは最小構成でも4GB(4096MB)ぐらいは搭載しているので、ヴィータ大脱出の頃とくらべるとかなり楽になりました。
とはいえ32BitOSでは、ひとつのアプリで2GBまでしか扱えないので、あまり油断もできません。
64bitOSなら小学生が考えたようなスゴイ数値になりますが、Windows10にも32bit版はあるし、物理的に搭載しているメモリーに限界もあるので無制限というわけにもいきません。
(※参考までに、プレステ3のメインメモリーは256MB。OSとか全然違うので一概には言えませんが、あやかし紅白戦どころかヴィータ大脱出もギリギリ動くかどうかというレベルです)
普通のゲームならそんなに気にする部分ではないのですが、当サークルのゲームはとにかくドット絵によるベタアニメが多いので(調子に乗って全画面アニメとかやってるし)メモリー使用量を気にせず作るとトンデモ無いことになります。
例えば主人公が立ち止まっている待機中の20枚のアニメーション。
どんな超効率圧縮されたデータでも画面に表示するときは1ピクセルずつメモリー上に展開するので、BMPファイルと同じサイズだけメモリーが必要になるわけです。
今のところ、こういう画像が5000枚ぐらいあって、そのまんま全部読み込むと1.5GBを超えます。
(まだ組み込んでいない画像や、今後描いて増える分も考えると最終的には3GB以上になると思われます)
画像だけでこれなんで、エフェクトとか効果音とかその他色々込みで考えるとエラいこっちゃですね。
あんまり低スペックPCのことは気にし過ぎても作るの大変ですが、全く気にしないと64BitOS必須 メモリー最低8GB以上というヘビー級の同人ゲームになってしまいます。見た目はただの2Dゲームなのに。
ではどうするか? 基本的な考え方は2つ。
1.必要なデータだけ読み込んで要らないデータはどんどん破棄する
2.データそのものの無駄を省く
1の方法ははゲーム機なんかで読み込みが入るのを思い出してもらうと分かりやすいですね。
ステージ1からステージ2に進んだら、ステージ1のデータを破棄して新しいデータを読み込むワケです。
逆にステージ1をプレイ中にステージ2のデータは必要ありません。
こうやってこまめにデータを入れ替えれば、少ないメモリーでも動作できるというわけです。
だかしかし
主人公のアニメーションデータはそうもいきません。
基本的にはいつでも全部メモリー上に持っている必要があります。
(極限まで切り詰めるならリアルタイムで圧縮データ展開とか方法が無いでもないですが)
そこで2の方法ですが、先程の待機モーションとか、画像の余白が結構ありますよね?
こんな風に16×16ピクセルで区切って、画像の入っていない部分をカットして一つのテクスチャーに入るだけ並べていくと無駄がかなり少なくなるというわけです。
みっちり詰められて1枚のテクスチャーとなった待機アニメ20枚分
そして実際にゲーム画面に表示するときに再配置し直すワケです。
節約した場合、実際の画面には16×16の画像がたくさん並んでいる状態ですね。
画期的な方法みたいに語ってますが、昔からよくある手法ですし、ヴィータ大脱出の時点でも採用しています。
他にもメモリー節約する方法は大小色々ありますし、今回紹介した方法ももっと細かくやって切り詰めることもできますが、プログラムが煩雑になりますし、その分処理も重くなります。
そんなワケで、今のところ手持ちで一番ショボイリーズナブルなPCで動作するラインを基準にやっています。ドン〇ノート大活躍。
それでも年々PCパワーは上がっているので楽ちんになりました。
ムーアの法則素晴らしい。
それを守ってるintelやAMDも素晴らしい。
Heliodor 2021/02/07 16:33
前回の話 の続きです。
デバッガー、コンソール、テキストファイルの3か所にそれぞれログを流すようにした、という話でした。
というわけで、しばらくこのやり方でやっていたのですが、ちょっとマズイことに気がつきました。ヴィータ大脱出を二つ同時に起動するとテキストファイルに出したログが破綻するんです。
同じログファイルを二つのEXEが使おうとするため後から起動した方のEXEがログをクリアしてしまったり、先に起動した方のEXEがファイルをロックしていて後から起動したEXEは一切ログの書き込みができなかったり……考えてみれば、そりゃそうですよね。
確かプロセス間で同期を取る仕組みがあったと思うので、それを使って互いに配慮しながらログを書き込もうか……と思ったこともありましたが、面倒くさかったのでもっと単純に、ヴィータ大脱出を複数起動出来ないようにしました。
もっとも、ヴィータ大脱出を複数起動してしまうとセーブファイルの書き込みで問題が発生したりするので、最終的には禁止するつもりではありましたが。
ちなみに今開発しているあやかし紅白戦では、複数起動しても問題なくログファイルが書き込めるようにしてあります。
複数のEXEが一つのログファイルを使えるようにしたため、誰が書き込んだのか区別できるようにプロセスIDを追記するようにしました。
また、ログの見た目を整えるためにインデントする機能なんかがあったのですが、それは廃止しました。インデントありの行とインデント無しの行が混在する可能性があるからです。
ところで、多重起動の他にもう一つ問題がありました。
起動時にログをいったんクリアしていたため、最後に起動したときのログしか残らないという点です。
つまり、強○終了が発生した後、一度でもゲームを起動したら強○終了時のログファイルが上書きされるって事なんですよね。
テスト配布した時のエラー報告なんかでこの事故が多発しました。
(「不具合が起きた!」というユーザーさんから何の問題も起きていないログファイルが送られてくるという……)
それと以前、強○終了時にその時のエラー内容をログに記録云々という話をしましたが、それはあくまでも強○終了したときに特別に作られるエラー報告用のファイルであって、今回の話のように普段から記録しているようなログファイルとは異なります。
そこで、ログファイル名に日時を入れて、毎回新しいログファイルに書き込むようにしたのですが……まあ、すぐにやめました。ファイルがどんどん増えてしまうんです。ストレージ(Cドライブ)が64GBしか無いド〇キノートでは(ry
じゃあ古いファイルを自動で消せばいいじゃないか?となったんですが、「ファイルを自動で消す」という動作が嫌な予感しかしなかったのでやめておきました。
ファイル名に日付が入るということは、あらかじめファイル名がビシッと決まってはないという事です。なので、消してもいいファイルなのかどうかをファイル名から自動判別させないといけません。
が、それだと関係無いファイルを消してしまうという事故がありそうで怖かったからです。
生成したログ名を別のファイルに記録しておいて、それを参照するという手もあります。
ただ、ユーザーが誤ってそのファイルを消してしまった場合にはログ名が分からなくなり、ログを削除できなくなってしまいます。まあ手動で消せばいいんですが……。
ログ専用のフォルダを作って、その中身は問答無用でログとみなすという方法もありますが、たかがエロ同人ゲームでログ専用のフォルダとか余計なもの作られるってのもねえ……ということで却下。
悩んだ挙句、ログファイルは一つだけにして、ログを開くときにファイルサイズを調べ、10MB以上のサイズだったら10MBになるように古い行を削る、という仕様にしてみました(ちなみに10MBという数字は割と適当に決めたものです)。
仕組みは単純で、いったんログを読み出しモードで開いてバッファに全テキストをロードして閉じ、テキストが目的のサイズになるように加工し、今度は書き込みモードで開いて書き込むというものです。
なんかもっとスマートに出来そうな気もするのですが、起動時の一回しかやらない処理なのであまり追求しないことにします。
やってることはリングバッファと同じですね。
これもしばらくはOKだったのですが、かなりの長時間起動していた場合に一回の起動でログが10MBを超えてしまう時があるんですね。
そうすると、起動して最初の方で記録した内容がいきなり消されてしまうんですよ。
結局、紆余曲折を経て最終的には過去3回分より古い部分を削除する、という仕様で落ち着きました。
まず、ログの書き込み開始するときに必ず区切り用のテキストを出力するようにします。
起動時にログテキストをロードし、区切りテキストを後ろから検索して、3つ目の区切りテキストが見つかったところで、それよりも先頭側のテキストを削除して再保存する、いうものです。
ちなみに区切りテキストは
"------------------------\n"
みたいに、いかにも区切ってますって感じのテキストにしておきます。
こうすればテキストエディタでログを見たときにも、どこが区切りなのかすぐに分かります。
これでファイルサイズとは無関係に、必ず過去3回分までのログを保存できるようになりました。
ところでログを見るときですが、テキストエディタでももちろん大丈夫ですが、テキストログ用のビュワーというものが探すといくつも見つかります("log text viewer とかで検索すると見つかります)。
こういうのを使うと、特定の単語を含む行(例えば "ERROR" とか)を色付きで表示してくれるだとか、ログのテキストファイルをずっと監視していて、ログファイルに何か書き込まれたらすぐに反映してくれるとか、そういう便利な機能があります。
もっとも、コンソールウィンドウに色付きでログテキストを流すようになってからはそういうツールを使わなくなり、あとでログファイルを見るときも普通のテキストエディタで済ませるようになりましたけど。
こうして改めて見ると行き当たりばったりと試行錯誤の連続ですね!
HAHAHA!
(笑い事ではない)
今回もおまけのどうでもいい話。
CPU+マザボ買い替えは見送るとして、再利用可能なグラボだけでも先に買っちゃおうかなぁとPCパーツ見てみましたが、なんでグラボこんな値上がりしてるの!?
今は1050ti使っているんで、せめて1650か1660あたりに……と思っていたんですが、こんなに高かったっけ?と目を疑いました。
つーか価格.comで売れ筋1位がRTX3080って嘘やろ!?(15万円ぐらいする)
どうせ高いの買うならハイエンド買っちゃえ!ということなんでしょうかね?
そーいやRyzenも5950Xとかもあの値段で売れまくって品薄なんですよね。
私の知らんところで景気良くなってるのか……?
ゲームの動作検証用に最近のRADEONも一つ欲しいなあなんて思っていたんですが、こっちも値上がりしていておいそれと手を出せそうもありません。
半導体値上がりかー。
個人レベルの同人ゲーム制作者には辛い時代ですねぇ。
冬場はマイニングPCが暖房器具代わりになるらしいんで、狙い目は夏頃……?
Heliodor 2021/02/03 22:26
プログラム話です。
今回はログについて。
ログを出力する方法はいくつかあると思いますが、ヴィータ大脱出を始めヘリオドール製ゲームは基本的に3つの方法で出力しています。
3つのうちのどれかに出力という意味ではなく、1つのログを3つの方法で同時に出力という意味です。
1つ目の方法はデバッガーに対するメッセージ出力で、これは開発中に最も役に立つものです。
デバッガーへのメッセージというのは、VisualStudioから実行した時に「出力」ウィンドウに表示される文字列です。
それをやるには OutputDebugString 関数を使います。
ちなみに、この時出力する文字列の先頭に"ファイル名(行番号)" という形式でファイル名と行番号を入れておけば、VisualStudioの出力ウィンドウ内でログメッセージをダブルクリックして該当箇所に飛ぶことができます。
void OutputDebugStringEx(const char *file, int line, const char *msg) {
char s[1024];
sprintf(s, "%s(%d): %s\n", file, line, msg);
OutputDebugString(s);
}
みたいな関数を作って _FILE_ と _LINE_ を引数に呼び出せばオッケーです。
この、ダブルクリックで該当箇所にジャンプと言うのは単純ながらすごく便利な機能なんですよね。
ちなみに OutputDebugString で出力したテキストというのは赤の他人でも見ることができるので、気になる場合は配布時に関数の呼び出しを切っておく必要があります。
2つ目の方法はコンソールウィンドウに対する出力です。
これはデバッガーがない環境でリアルタイムにログを見たい時なんかに便利です。
WinMain なプログラムでコンソールウィンドウを表示させるには AllocConsole 関数を使います。
ただ、Windows プログラムでは stdout がコンソールウィンドウに接続されておらず
printf で出力した文字列は闇の彼方に消えてしまうので
freopen("CON", "w", stdout);
を実行して自分で stdout と コンソールウィンドウと接続する必要があります。
これで普通に printf が使えるようになります。
ちなみにコンソールを閉じるには FreeConsole を使いますが、それよりも前に
freopen("NUL", "w", stdout);
を実行しておいて、コンソールをstdoutから切り離さないといけません。
そうしないと FreeConsole を呼んでもコンソールが閉じません。
まあ、アプリが終了すれはコンソールも勝手に閉じますけど。
コンソールウィンドウは文字の表示に特化してるだけあって、お手軽なんですよね。
ログをリアルタイムで表示するための専用ウィンドウとか自前で作らなくて良いし、工夫すればエラーメッセージは赤色で、警告メッセージは黄色で表示とかできますし。
コンソールに流れているログの色をかえるだけで、見やすさが完全に変わります。
これについては SetConsoleTextAttribute で検索してみてください。
製品版では AllocConsole を呼ばないようにすればコンソールウィンドウは表示されませんが、たとえ WinMain なプログラムであったとしてもコマンドプロンプトから EXE を起動した場合は普通に printf できてしまうため、見られたくないログがある場合は配布時に printf の呼び出しを切っておく必要があります。
3つ目の方法は、テキストファイルに出力するものです。
ここでは何も普通のテキスト形式で出す必要はなく、HTMLの書式で出せば文字の色を変えたり太字にしたり該当箇所にリンクを張ることもできます。
実際、最初の頃はヴィータ大脱出のログをHTML形式で出すようにしてました。
しかし途中でやめてしまいました。HTMLだとタグも出力しないといけないので全体の文字数が増え、ファイルの書き込みの負担がかかる&ログファイルがでかくなってしまうんです。
これ、製品版ではログ自体が少ないので問題ないのですが、開発中のバージョンだと細かいデバッグメッセージも全部記録しているのでわりとバカにならないんです。
ストレージ(Cドライブ)が64GBしか無いド〇キノートでは自殺行為。
それと、HTMLというのはタグを開くだけでなく閉じる必要もありますが、当たり前ですけど強○終了したときには終了タグが出せないんですよね。
いや、別に不完全なHTMLであってもブラウザで普通に表示できますが、なんか気になりまして。
そんなわけで、普通に fopen の書き込みモードで開いて、普通にテキスト書き込んで、ってやっていました。
この場合、fprintf の直後に fflush を置いて、バッファ内の文字列をすぐに吐き出させるように気をつけます。
そうしないと強○終了が発生したとき、内部バッファに文字列を溜め込んだまま逝ってしまうためにどの時点で強○終了したのかが分からなくなります。
ちなみにログファイルは毎回新規書き込みモードで開いています。
起動のたびに古いログが消え、まっさらなログファイルから始まるイメージです。
そんなわけで、しばらくは上記のやり方でやっていましたが、ちょっとマズイことに気がつきました。
次回に続きます。
おまけのすごくどうでもいい話。
なんで新しいCore i9、コア数減ってるの……。
さすがにPC重くて不安になってきたけどRyzen 5000番台は全然売ってないし、Ryzenの攻勢で値下がりするであろうCore i系行っちゃうか!?とか目論んでいたんですが、もうしばらく様子見かな……。やはりSandy Bridge最高。