イベント駆動ではゲームが作りにくい時

Windowsは基本的にはイベント駆動型のプログラムのみで全てを作成します。しかし、ツール類

の場合はそれで良いのでしょうがゲームの場合はユーザーの入力が何も無くてもプログラムは

処理を行わなければならないことが良くあります。多くのWindowsプログラムの標準的な解説では

WM_TIMERイベントを使用する様な事が書いてありますが、WM_TIMERは実際には使い物になり

ません。WM_TIMERは、一定時毎に呼ばれるイベントという説明になっていますが、実際使用した

時には、大概不規則です。しかもWM_TIMERのイベント優先順位は非常に低い物なので、少しでも

他のイベントが重なりまくっていた時には全く呼び出されません。また、WM_TIMERはシステム全体

で使用出来る個数が決まっていて、いつでも使用できるという訳には行かない物です。とても

使う気にはなれません。で、Windows3.1の時から伝統的に使われている方法には、メッセージループ

をGetMessageでは無く、PeekMessageで回し、メッセージの来ていない時にはアイドル時処理を

入れる、と言う方法があります。これはWindowsがWindowProcedureへのメッセージの流れの管理も

アプリケーションに行わせている事を利用した物で、結構簡単な変更ですぐに導入出来ます。

ただ、この方法を使用する場合、1回のループが重い物だった場合はWindows3.1では、その重い

処理をしている間システムに制御が戻らずマウスもろくに動かない、という事になります。

Windows95でもこの方法で1回のループが重かった場合は、やはりシステム全体が目に見えて

重くなります(具体的にはマウスを動かしてもマウスカーソルの動きが追従しなくなったりする

場合があります)。

Win32独自の機能を使うともっと便利な方法もあります。

まず1つ目は、マルチメディアタイマーを利用し、タイマーのコールバック関数で処理を行う。

マルチメディアタイマー自体はWindows3.1にもあったのですが、Windows3.1時代はDLL内でしか

使えず、結構不便な面もありましたが、Win32になってその様な制限は無くなりました。これは

SetTimerよりも高精度の使い良いタイマーですが、余り短い間隔で呼ぶとイベントが重なって

しまい、俗に言う「処理落ち」が発生しやすくなります。

2つ目はメッセージループでアイドリングする代わりに、完全に一本道のスレッドを一つ作り、

ゲームの作業は全てそこで行い、画面の更新のみをメインスレッドで行う、という物です。

スレッドはWin32のCreateThreadで作っても良いのですが、それだとスレッド関数の中では

Cの関数で使用出来ない物が発生するなどの制限もあるので、VisualC++を使用している

場合は_beginthreadexでスレッドを作成しましょう。具体的には、スレッドを作成する時に

unsigned threadValue ;
HANDLE gameThreadHandle = (HANDLE)_beginthreadex(NULL, 0, gameThread, &Data, 0, &threadValue) ;

とします。gameThreadは

unsigned __stdcall gameThread(void *Data) ;

というプロトタイプを持つ関数で、*Dataは_beginthreadexの第4引数が渡されます。

_beginthreadexは内部でCreateThreadを呼び、新しいスレッド用にCのランタイム関数を初期化して

新しいスレッドを呼び出します。Dataはメインスレッドとサブスレッドのデータのやり取りをする時等

に使用しますが、作成するスレッドが1種類で、それを1回だけしか作成しないというのであれば、

ここで渡さなくてもグローバル変数などで連絡を取るのも簡単です。ちなみに、作成したスレッドは

明示的に終わらせなくてもスレッド関数が終了すれば勝手に後処理をやってくれるので、特にメイン

側から強制的に終了させたい、という事でも無い限りは考えなくてもよいでしょう。

尚、メイン側とスレッド側の同期を取りたい場合は幾つかの方法がありますが、タイミング的に重要な

同期を取る場合はグローバル変数をポーリングさせたりせずに、同期オブジェクト(特に同期の場合

はイベントを使う事が多い)で同期を取りましょう。但し、終了通知までそんな事をする必要は無く、

基本的にはメインの状態変数をグローバルに取って、それを全てのスレッドが参照するという形で

いいでしょう。一定タイミングをはかりたい場合はマルチメディアタイマーを使うのが良いのですが、

スレッド側でtimeGetTimeの戻り値をチェックして、この値と前回のtimeGetTimeの値との差が一定

以上になるまでtimeGetTimeを繰り返すと言うのも割と良く使われる手法の1つです。あるいは、

DirectDrawで排他モードを使用している場合はFlipで同期とするのも1つの手です。Flipは必ず

VSyncと同期する事になっているので、Flipすれば少なくともある一定タイミングの倍数のタイミング

にある事は確かです。

 

レジストリの内容いろいろ

システムレジストリにはプログラムを動かす際に、またインストールする際にお世話になる

キーが沢山有りますが、その内の一部を抜き出してみたいと思います。

まず、Windows95でアプリケーションのアンインストールをする時にコントロールパネルの

アプリケーションの追加と削除のメニューに登録する為のレジストリ。

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall

の下に、自分のアプリケーション用のキーを作り、そのキーの中に

DisplayName
UninstallString

という値を入れます。この2つの値は共に文字列型で、DisplayNameはアプリケーションの

追加と削除のリストに登録する名前、UninstallStringは、それが選択された時に実行される

コマンドライン文字列です。従って、DisplayNameには自分のアプリケーションの名前、

UninstallStringにはアンインストーラのフルパスと引数を書いておけば良いことになります。

次に、自分のWindows95環境内でのプログラム登録場所としてデフォルトに使用すべき

場所。DOS/Vの場合、多くはC:\Program Filesな訳ですが、これは

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion

内の

ProgramFilesDir

というキーに文字列として入っています。インストーラを自作する場合はデフォルトの

インストール先をこの下に作れば良いでしょう。尚、Microsoftはこの下に、

会社名\アプリケーション名

という形でディレクトリを作ることを推奨している様です。

特に私が知っているべき、と思ったレジストリの2点を紹介しました。他にもまた、

色々見つけてみたいと思います。

 

IMEを消したい時

ゲームの場合、大概はフル画面で作ると思いますが、MS-IME95等は常に画面に

出たままになっているのでゲーム画面の一部が隠れてしまい、うっとうしくなる事が

あります。そんな時は

WM_IME_NOTIFY

のメッセージがゲームのメインウィンドウに送られてきた時に、WPARAMの値をしらべ、

値が

IMN_OPENSTATUSWINDOW

だった時にはDefWindowProcへ処理を渡さずに0を返せばOKです。ゲームウィンドウが

アクティブになっている時はIMEの表示が消えます。

 

勉強について

どんな事でもそうですが、良い勉強法を見つけることが上達の早道となります。そこで、

ここでは私がやっている勉強法について紹介したいと思います。但し、これは飽く迄私の

方法であって、これで効率が上がる人もいれば下がってしまう人もいるかもしれません。

もしやりかたが自分に合わないと思ったら自分に合う勉強法を色々やってみて探す事を

お勧めします。

まず、私の場合、プログラミング言語は今自分が知っている言語の機能と照らし合わせて

覚えることが多いです。従って、新しい言語を習得する際には以前自分の知っている言語で

自分が作ったプログラムを新しい言語で書き直してみます。ただ、これはプログラムに

ある程度精通している人でないと出来ない方法なので、私がコンピュータを始めたころには

当然違う方法でした。私は一番最初はパソコンに付属していたBASICを勉強したのですが(

ちなみに機種はX1turboIIでした)、最初はまず簡単なデモプログラムを組みました。デモと

言っても、デモと呼べるほどの物でもなく、単に画面に線を引いたり音を鳴らしたりする非常に

単純な物でした。そしてそこから色々とプログラムの概念や方法論等を学びました。今は昔の

様に本体にBASICが付属していたりしないしDOSで画面に線を引こうとしても、コンパイラ言語

に付属のグラフィック制御ライブラリ等を用いる位しか簡単な方法が無いようで、昔の様に単純

には行かない様です。そこで、今プログラムを始める人はまずDOSのテキストを表示する事を

勉強することをお勧めします。つまり、C言語の世界では有名な、「Hello,World」ですね。Cでは

#include <stdio.h>
int main(int argc, char *argv[])
{
    printf("Hello,World\n") ;
}

といった物です。

このたった5行のプログラムの中にも様々な言語要素が含まれているので、まずはこの構造を

完全に把握できる様になる事をお勧めします。

次、新しいOS等について。これはまず、その対象OS、対象機種を触ってどんな物かを良く知る

事から始めましょう。そうしないと自分が今から何をしようとしているのかがわからなくなります。

そして、その対象についてある程度以上のことを知ったら、私の場合は本を読みます。それこそ

一日中って位本を読みます。別に覚える必要は有りません。ただ、どんな事をするのかのイメージ

を頭の中に作っておくのです。そして、イメージが大体出来る位まで本を読んだら後は言語の入門

と同じく、簡単なサンプルから入ります。私はやはり簡単なデモの作成から始めます。ここで表示の

仕方を覚えたら、後は今まで自分が培ってきたプログラミング技術を生かす方法を探します。

ちなみに、本にも良い本と悪い本が有ります。Windowsのプログラムを始める場合はいきなりMFCの

本を読むのは止めましょう。はっきり言って時間の無駄です。Windows95なら、プログラミングWindows95

の日本語版がお勧めです。プログラミングWindowsシリーズは私がWindowsプログラムの入門の為に

買ってみて、非常に役に立ちました。お勧めします。

後は一応Windows95APIBibleのシリーズも押さえておくと何かと役に立つでしょう。取りあえずそれだけ

あれば入門くらいは果たせると思います。入門さえしてしまえば後はどれだけ頑張るかが実力に

なるでしょう。頑張ってください。

 

 

プログラミングTipsに戻る