第11章 豆腐を並べる(1)
2006年9月27日
さて、クライアント領域への描画(ビットマップ限定)にかろうじて成功したところで、今度はユーザーからの入力インターフェイスにアプローチしてみようと思う。
ゲームを作るなどと大口を叩いた上では、避けて通ることの出来ぬ命題である。
また、グラフィックの描画にしても、非常に奥が深く更なる苦難が待ち受けていることは想像に難くない。
今章の目標も、素人(担当者限定とも云う)にとっては、巧妙に設置された地雷原を避けつつ進む難行軍のようであった。
いや、探る手立ても無しに駆け抜けようとして、ことごとく踏みつけているとも云えなくも無いが・・・
ともかく今回は、本連載初の動きあるアプリケーションの作成である。
緊張と興奮をもってチャレンジしてみることにする。(大袈裟)
毎度の事ながら実行画面をキャプチャしてみたので、ご参照あれ。
とは云え、これだけでは何のことなのか判らないので、使い方(?)を説明すると・・・
クライアント領域内にマウスカーソルをもっていき、任意の位置で左クリックすると、そこに白四角を描画するアプリケーションである。
因みに、左のキャプチャ画像は既に幾度か上記の作業を行い、多数の白四角を描画させてみた状態である。
ユーザーが任意に描画位置を決めることが出来る為、左図とまったく同じ配置になることはまず無いと云えよう。
と云うか、そんな説明は要らないと思うぞ。
さて、ではまず失敗作から。(をいをい)
このソースをコンパイルして実行してみると、左上角に白四角が現れるものの、他の領域を左クリックしても、白四角が描画されることは無い。
では、問題点を暴露する前に、ソースの何処で何を行っているのかを説明しよう。
まずは、左クリックの検出。
これは、WM_LBUTTONDOWNメッセージを処理することで実装することにした。
このメッセージはネーミングから察することが出来ると思うが、マウスの左ボタンが押し下ろされた時にOSから送られてくる。
因みに、これのお仲間として、WM_RBUTTONDOWN、WM_LBUTTONUP、WM_RBUTTONUP等があるが、それぞれ名前の通りのものなので深くは語るまい。
そこで、まずこのアプリケーションのクライアント領域内で左クリックが行われると、変数position_xとposition_yに変数lParamの値が加工されて代入されている。
これは、OSがWM_LBUTTONDOWNメッセージを送ってくれる時に、その座標を変数lParamに代入してくれるので、それを利用しているのである。
そして、このcaseをbreakでくくるのを止めることで、次のWM_PAINTメッセージの描画処理を行ってしまおう、とこう考えたのである。
そう、理屈でいけばマウスを左クリックすると描画処理まで行われるので、画面の任意の場所に白四角が描画されるはずだったのである。
・・・されるはずなのに、なぜ描画されないのか。
そもそも、本当に描画されていないのか?
そこで、WM_PAINTメッセージを無理やり(?)このアプリケーションに送ることでどのような反応を示すか実験してみた。
WM_PAINTメッセージを送りつける方法は、他の窓(今回はエクスプローラ窓)でこのアプリケーションのクライアント領域をこする(?)ことで実現してみる。
結果は左図のとおり。
元の白四角が消えかけて、新たに左クリックした地点に現れた。
どうやらWM_LBUTTONDOWNメッセージの処理と、WM_PAINTメッセージの処理は機能しているらしい。
しかして、この失敗作、ひとつだけ私に教えてくれたことがある。
それは、アプリケーション起動時にWM_PAINTメッセージが一度、OSから送られてくると云う事実であった。
さて暫く試行錯誤を行った結果、この問題の解決方法が二つ存在する事が判明した。 そこでまず、一つ目の方法を試してみることにする。
今回の場合はこちらが正攻法ではないか、と思われるメッセージ周りの修正である。
手を加えたのはWM_LBUTTONDOWNメッセージの処理。
追加した関数InvalidateRect()は、WM_PAINTメッセージを任意のWindowに対して送り出す働きをする。
今回はこの関数を使って、自身にWM_PAINTメッセージを送り、改めて描画処理を行うことにしたわけだ。
コンパイルして実行してみると、なるほど、マウスの左クリックに合わせて、カーソル位置に四角が出現していく。
では、もう一つの方法はと云うと、描画周りの変更による修正である。
こちらはあまり正攻法とは思えないので、あくまで実験としての修正としてご紹介しよう。
ここではWM_PAINTメッセージ内、つまり描画処理において使っていた関数BeginPaint()の代わりに関数GetDC()を用いて、クライアント領域のデバイスコンテキストハンドルを取得するよう変更している。
あわせて関数EndPaint()を関数ReleaseDC()に変更して、デバイスコンテキストを開放するようにしなければならない。
この2つの関数の違いについてはともかく(?)、一つはっきりしていることは関数BeginPaint()が、WM_PAINTメッセージ処理としてしか機能しない、と云うこと。
手元の資料をみてみると、そもそも関数BeginPaint()とは再描画処理に用いるもの、とある。
どうやら、もう少し突っ込んで実験してやる必要があるようだ。
とにかく、こちらの実行形式にコンパイルしたものも、左クリックで四角が並ぶ。
良かった良かった。
とまとめている場合ではなく、実は今回のサンプル、まだまだバグが潜んでいるのである。
従って、次回でもまだまだバグ取り作業を行っていかなければならない。(しくしく)
|