圧縮ファイルからの更新にも対応した。
圧縮形式に何を使おうか悩んだが、ZIPやらLZHやらはライセンスの問題やライブラリ引用するのが面倒くさいので、一番手っ取り早そうな LZCopy 系 *1 を使うことにした。
圧縮率に関しては可もなく不可もなくという感じだが、アップデートソフトに追加でDLLが必要、というのもやりたくなかったので決定。サーバ上に圧縮されたファイルを置いておけばダウンロードしたときに自動的に解凍されるようにした。
とりあえず形にはなったのでバグとりも兼ねてなにかのゲームで実際に使っていきたいところ。
つまりネタ募集。
イマドキなオンラインゲーム等はゲームの修正が入ったときに、自動的にダウンロード&インストールする仕組みを持っている。
自分の作ったゲームがこの機能を持っていたらカッコいいと思ったので作ってみた。
仕様上、別EXEになってしまうのだが、ゲームからこいつを呼び出すと更新ファイルがあるかをサーバーに確認しに行き、あれば既存ファイルと置き換える形でダウンロードを行う。
サーバ側では、更新するファイルのリストを準備しておく必要がある。(すべてのファイルを確認してたら重そうなのでしかたない)リストにあるファイルのタイムスタンプを確認し、サーバーにあるものが新しかったら更新という筋書きである。
とりあえず作ってみただけなので、ファイルは素のままダウンロードするし(圧縮すら無し)ファイルの破損確認などを今後追加していこうと思う。
てことで一般公開はまだNGです。
SocketのプログラムをWindowsで書く場合、winsock.hまたはwinsock2.hをインクルードすることになる。
前者はVersion1のWinSockで、後者はVersion2となっていて、TCP、UDP以外のプロトコルを使いたい場合や、高速な通信を行いたい場合は多機能な新バージョンの WinSock Ver2 を用いるわけだが、ここには罠が存在している。
winsock.h と winsock2.h は互いに排他的で、同時に使用することはできない。(関数や構造体が同名異義となっているものがある)そこでwinsock2.hだけをインクルードしようとするわけだが、実はwindows.h内部でwinsock.hがインクルードされているのだ。
これを回避するためには、windows.hをインクルードする前に _WINSOCKAPI_ を define する。
こうするとwinsock.hがインクルードされても、中身をパスしてくれるのでwinsock2.hをインクルードしても定義の重複は発生しない。
ライブラリなどを組んでいるとあちこちのソースファイルで windows.h をインクルードしているので、結構面倒くさいものである。よって#defineでコード中に記述するより、プロジェクトオプションで定義したほうがよいだろう。しかし、DirectXのライブラリ(d3d9.hとか)でも普通にwindows.hインクルードしてあるので、この場合、手が出せない。この場合ワーニングが出まくるのを我慢するか #pragma warning ( disable : 4005 ) としてワーニングを消してしまう他方法はないものと思われる。
後から追加されたライブラリであることから、しかたないともいえるかもしれんがこの実装はどうかと思うな。
足掛け4年・・・。ぶっちゃけ、やっつけ仕事で公開。
全プログラム書き直すこと3回。仕様変更数知れず。
没になったグラフィック、BGMおなかいっぱい。
モチベーションの維持が大事だとつくづく思わされたゲームです。
次回はバカゲーでも作りたいと考えています。
Win32アプリのデフォルトアイコンを設定するときは、リソースに IDR_MAINFRAME という名の*.icoファイルを追加する。32*32と16*16分けるときはどうすればいいんかいな。
久々に C でハマってしまった事例があるので、紹介。
もちろんハマるの意味は熱中するほうではなく、どつぼにはまる
*1
状態を指しています。
データをファイルに出力するとき、よくあるケースとして構造体をそのまま書き込むことがあると思います。
// ビットマップヘッダを書き込む
WriteFile(hF, (BITMAPFILEHEADER*)&bmFH, sizeof(BITMAPFILEHEADER), &dwResult, NULL);
この例だと、BITMAPFILEHEADER構造体をそのままメモリからファイルへ出力しています。
通常、この構造体は PlatformSDK に含まれる WinGDI.h に定義されていて、実際には Windows.h をインクルードした時点で参照されています。
これを使う分には問題は発生しないのですが、先日ある理由により上記ヘッダファイルをインクルードせずに自分でBITMAPFILEHEADER構造体を定義して使っていたのです。具体的にはこんな感じ。
typedef struct tagBITMAPFILEHEADER {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;
(MSDN に記載されているそのままです)
さて、このとき何が起こるのか予想できますか?
実はこの方法でBMPファイルを作成したところ、ペイント等でファイルを開こうとしてもエラーが出るだけです。不思議に思ってファイルをダンプしてみたところ、先頭部分(この構造体がまるまる入っている)がこんな感じになっていました。
42 4D 00 00 38 10 0E 00 00 00 00 00 38 00 00 00
(本当は ↓ みたいになるはず)
42 4D 36 10 0E 00 00 00 00 00 36 00 00 00 28 00
BITMAPFILEHEADER構造体の使い方を知らない方のために説明しておくと、通常のBMPでは bfType には常に 0x4d42 が入れてあり、bfReserved1 と bfReserved2 には 0 が入ってなくてはなりません。
しかし実際には上記のとおり、bfReserved1 に 0x000E が入ってしまっていました。
何度プログラムを見直しても、単に構造体をぶち込んでいるだけなのでミスは見当たりません。
よーく考えてみると、そもそも 3バイト目と4バイト目が0になっているのもおかしい。
はい。結論をいいますと、構造体では WORD bfType; となっているのに、実際のメモリ上にはWORD(2バイト)+パディング(2バイト)が入っていたのです。
通常のコンパイラだとデフォルト設定で構造体のアライメントが4バイトになっているのをすっかり忘れておりました。
これは 通常通りにWindows.hをインクルードして使っている分には問題は発生しません。期待通りに構造体がそのまま出力されます。WinGDI.hを よくよく見てみると
#include <pshpack2.h>
typedef struct tagBITMAPFILEHEADER {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;
#include <poppack.h>
このように定義されているじゃありませんか。pshpack2の中にアライメントを2バイトに設定する指定が入っていて、poppackで再度設定を戻すようになっています。
原因がわかったので自前の構造体定義にも 同様の設定になるようにします。
#pragma pack(push, 2)
typedef struct tagBITMAPFILEHEADER {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;
#pragma pack(pop)
これが正解。アライメント指定の記述方法がちと違いますが、これもアリです。
これによって構造体が2バイトアライメントされるようになり、構造体メンバはすべて2バイトと4バイトのものしかありませんので、ファイルに出力した場合でも余計なパディングはできません。
あーまた変なとこで時間くっちゃったい。
引き続き 画面関連のお話。
画像をディスプレイに表示する場合、なんらかのテクスチャ(サーフェス)を使用するわけだが、そのフォーマットは様々である。一般的には RGBの色情報を持つビットマップが使われるが、キャプチャデバイスによっては画像データをYUV形式で返すものがあるので、これに対応しなければならない。
YUV とは、輝度信号(Y)と、輝度信号と赤色成分の差(U)、輝度信号と青色成分の差(V)の3つの情報で色を表す形式(IT用語辞典) とある。基点となる情報の差分でデータが構成されているのでRGBよりも圧縮がしやすい。かつての白黒テレビと互換性をもつために考案されたものらしい。 *1
YUVと似たようなものに、YIQというのもあるが、基本的な考え方は同じでよい。前者はPAL方式、後者はNTSCで使用されている。
さて、YUVとRGBを相互に変換するのはそんなに難しくない。RGBの各要素にある係数を掛けて足せばYUVの要素が得られる。とあるページでは以下のように記されていた。
Y = 0.299R + 0.587G + 0.114B
U = -0.169R - 0.331G + 0.500B
V = 0.500R - 0.419G - 0.081B
また、別のページでは以下のような式もあった。
Y = 0.299R + 0.587G + 0.114B
U = -0.147R - 0.289G + 0.436B
V = 0.615R - 0.515G - 0.100B
似てるけど、なんか係数が違っている。なぜだろう?
これは 各要素の上限と下限を考慮するかしないかの差である。
具体的に言うと、RGBを0~255と扱うか、16~235と扱うかで係数が変わるのだ。
前者はYCC形式と呼ばれ、UとVがそれぞれCb,Crと呼びかえて使われる。
この差は意外と重要で、後者は色の範囲がせまいので、真っ黒に近い黒、真っ白にちかい白など、極に近い色がつぶれてしまう恐れがあるので注意しなければならない。
また、一口にYUVといっても、またこれがさらに細かく枝分かれして存在する。泣ける。
UYVY、YUY2、YVYU、IYU1、IYU2 など。これらの違いはビット長である。
各要素を何ビットで取り扱うかが異なるので、当然データ量と画質に違いがでる。
DirectXで主に使用されるのは D3DFMT_UYVY と D3DFMT_YUY2 である。
*2
この2つのフォーマットは、Yを8Bit、UとVを2ピクセルでそれぞれ8bitで構成するという特殊な形をしている。つまり1ピクセルあたりY:8+U:4+V:4=16bitとなる。(UとVが2ピクセルで共有されてるってこと)
DirectShowのヘルプを追っていくと、このあたりはレンダラがいろいろとやってくれるらしいが、自前で色空間の変換を行う場合、様々な注意点があることを認識しておこう。
なんだか、まとまりのない文章になってしまった。1エントリにまとめるには情報が多すぎる。
アナログなテレビ放送には 大きく分けて2種類ある。
日本で使用されているのはNTSCという方式。アメリカやアジア諸国もNTSCである。正確には日本はNTSC-JとアメリカではNTSC-Mという差があり、何が違うのかというと輝度の幅(0IRE=黒)が違う。
ハード的な仕様としては、走査線本数が 525本、秒間 30フレーム、 60フィールドのインタレース描画を行う、となっている。
これに対し、欧州で普及しているのがPALという方式。ヨーロッパで動作するコンシューマゲームを作る際にはこいつを念頭におかなければならない。さて、何が問題になるのか。それはハード仕様を見るとすぐわかる。PAL・・・走査線本数625本、秒間 25フレーム、50フィールドのインタレース描画。
そう。要するに秒間60フレームで描画することを前提にゲームを作った場合、PALのテレビだと50フレームでしか描画できないのである。このことから、フレーム単位でモーションやアニメーションを計算しているものは スロー再生 *1 になってしまう。
また、最近のPAL方式テレビでも60フレーム描画可能な方式のものが出てきており、実質ヨーロッパでゲームを作成する場合は50フレームでも60フレームでも動作するものを作らなければならないのである。
一番よい方法は、両方のテレビで再生できるようにデータを2つ持っておくことだが、実際には難しい。基本となるモーションデータなどを60フレーム用につくっておき、環境によって間引いて表示させる処理などが必要となる。ポリゴンのモデルを表示する分にはこれで問題ないが、ムービーデータなどは手間だろう。50フレームのテレビで60フレームのデータを再生した場合にはティアリングは避けられない。
全世界で規格が完全に統一されるには、当分かかりそうだし、気をつけよう。
| 日 | 月 | 火 | 水 | 木 | 金 | 土 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 | 31 |