時乃工房-Windowsとアマグラマーな関係-

アクセスアップにオートリンクネット リンクが自動で増殖オートリンクの登録はこちら 節約内職情報検索局
ページランク向上リンク集
役立つリンク集 Web Links
SEO対策ディレクトリ型検索エンジン Su-Jine
メニュー
トップ
アマグラミング
C++編 目次
第18章
第20章
ソフトウェア開発製品
相互リンク

<勘違いだらけのアマグラミングな日々(C++編)>

第19章 タコと背景の重ね合わせ

2006年12月6日

皆さんは”スプライト”と呼ばれる機能をご存知であろうか?
これを一言で云ってしまえば、「親画面上に他の画面を重ねてディスプレイに表示する機能」である。
一昔前のゲーム用ハードウェアではかなり重宝されており、なにより専用のハードウェアを実装することによって、CPUの処理能力が低い場合でもあまり実行速度に影響を与えることなく、気軽(?)にキャラクターを動かすことが出来る、と云った便利な機能であった。
特にセ○製のサタ○ンと呼ばれるコンシューマゲーム機のスプライト機能は素晴らしく、3D描画にまで利用されていた(サタ○ンはスプライト機能ポリゴン機能を同一チップに統合し、実装していた)ことは、もはや、伝説と云ってもいい(断言)。
・・・しかし、それも今は昔の物語。
ハイパワーなCPU強力なGPUが当たり前となった現代では、過去においてリアルタイムな処理が不可能とされていた複雑な描画処理(2Dにおけるアンチエイリアシング3DにおけるZバッファ、など)をVRAM上で直接(しかもごくあっさりと)行ってしまうため、わざわざ専用のハードウェアを載せてまでスプライトを利用する意味がなくなってしまっている。
いやしかし、メモリ上ではなく走査信号に直接スーパーインポーズ(これも死語だね)されて出来上がる映像は、ピクセルを超えたスムーズ感による独特の味があって、今見るとなかなか感慨深いものがあるなぁ、とは思う。

さて、前章までの実験によって、キャラクターがあたかもクライアントエリアを移動しているかのような状況は出来上がった。
そこで、次のステップとして背景とキャラクターの重ね合わせ処理をおこなって、より実用的(?)なキャラクター表示を行ってみようと思う。
以下に重ね合わせ処理実装前(左図)の画像と、重ね合わせ処理実装後(右図)の画像を並べてみた。

では、これを実現する為には何をどうすればよいか。
これまで、キャラクタの表示に用いてきた関数BitBlt()(←Win32apiの標準関数ね)を、改めて観察してみよう。
その定義について、手元の資料を読み解くと


BOOL BitBlt(    HDC     hdcDest,//コピー先のデバイスコンテキストハンドル
                int     nXDest, //コピー先の横座標
                int     nYDest, //コピー先の縦座標
                int     nWidth, //コピー先とコピー元の横幅
                int     nHeight,//コピー先とコピー元の縦幅
                HDC     hdcSrc, //コピー元のデバイスコンテキストハンドル
                int     nXSrc,  //コピー元の横座標
                int     nYSrc,  //コピー元の縦座標
                DWORD   dwRop); //ラスタオペレーションコード



と、こんな感じ。
さて、これら引数の中に、ラスタオペレーションコードなどと云う(個人的に)聞きなれない引数がある。
ちなみに、これまでの白四角からタコ助、背景画像の描画までを行ってきた関数SetCharacter()内の関数BitBlt()におけるラスタオペレーションコード”SRCCOPY”
こいつはもちろん、”Windows.h”マクロ定義されたDWARD値で、この値の意味は・・・”コピー元をそのままコピーする”と云うものだ。
つまり、ラスタオペレーションコードと云うのは、関数BitBlt()機能を指示するものである。
この資料を読んで、その昔、PC−88○1でゲーム作りを試みていた頃、論理演算によるキャラクターの重ね合わせを学んだことを思い出した。
その方法とは、マスクと呼ばれるドット絵VRAM上のデータOR処理し、その後でキャラクターのドット絵AND処理して描画する方法である。
・・・なんか書いててもピンとこない表現だが、ビット演算をご存知の方なら想像は難しくないだろう。
では早速、この方法で重ね合わせ処理を試みることにしよう。
まず、キャラクターの描画用ビットマップ(左図)とマスク用ビットマップ(右図)を用意してみる。

     

まぁ、前章で作ったタコ助のビットマップを、キャラクタ背景に分けたものと思ってもらえば良いと思う。
但し、キャラクター用ビットマップは何色で作っても良いが、マスクは白と黒で塗り分ける必要がある。
と、云うか、今回黒くした部分が黒で無い場合は実行結果が面白いことになるのだが、まぁ、それはまたの機会に試すこととしよう。
では、まず描画用のビットマップを使った実験から。

List.002-019a
main_window.h
main_window.cpp
main_callback.h
main_callback.cpp
main_process.h
main_process.cpp
graphics_control.h
graphics_control.cpp
bitmap.rc
main_menu.h
main_menu.cpp
main_menu.rc
<ソースのダウンロード>

変更した点は2つ。
1つは、描画対象となるビットマップファイル前章で用いたものから、上記の重ね合わせ用のものに変更したこと、そしてもう1つは、関数SetCharacter()内の関数BitBlt()ラスタオペレーションコード”SRCPAINT”に変更したことである。

この、”SRCPAINT”という指示の意味は、「コピー元とコピー先を論理OR演算子で結合する」とのことだ。
実行結果は、左図のとおり。
透けたタコ助が描画されているのがわかる。
因みに、これまでのように動かしてみると、半透明の赤い線になってしまって、何が描画されているのか判らなくなってしまう。

左下図がキャラクターを動かしてみた時の様子。
あれ?背景を毎回上書きしているのに、軌跡が残ったままになってる。
と、思い当たっていただけると非常にありがたい。

そう、この関数SetCharacter()背景の描画にも利用されているので、背景の描画にも”SRCPAINT”が適用されてしまっているのだ。
では、背景が正常に表示されている部分、半透明のタコチューが表示されていない部分はなぜ正常に描画されているのか。
これは、アプリケーション起動直後に何も描画されていなかった部分、つまり最初に黒かった部分である。
もっとも、黒かったのはほんとに起動直後のほんの一瞬のことなので、その後は背景が既に描かれた上から”SRCPAINT”が適用されて書き込まれているのが現状のはずである。









さて、ここまでを踏まえた上で、次の実験に移ろう。
次はマスク用ビットマップを使った実験である。

List.002-019b
main_window.h
main_window.cpp
main_callback.h
main_callback.cpp
main_process.h
main_process.cpp
graphics_control.h
graphics_control.cpp
bitmap.rc
main_menu.h
main_menu.cpp
main_menu.rc
<ソースのダウンロード>

今度は追加したマスク用ビットマップを、ラスタオペレーションコード”SRCAND”を指示して描画してみた。

ただし、今回はマスク表示用の関数SetMask()を用意している。
なぜそんなことをしているのかと云うと、真っ黒なところに”SRCAND”を適用して描画しても真っ黒なままで、したがって背景の描画に”SRCAND”を使っていては、いつまでたってもクライアントエリアが真っ黒なままだからである。

あっとそうそう、ラスタオペレーションコード”SRCAND”とは「コピー元とコピー先のビットマップを論理AND演算子で結合する」ことを指示するのだそうだ。











以上、2つの実験を持って重ね合わせの理屈をまとめると、
@”SRCPAINT”を適用した画像は、黒いところで正常に描画される
A”SRCAND”を適用した画像の、黒いところは黒く描画され、白いところは元のままである。
・・・ああ、なんか小学生の頃書いた感想文みたい。

えー、では早速、以上の2点を踏まえて、重ね合わせ処理を実装してみよう。

List.002-020
main_window.h
main_window.cpp
main_callback.h
main_callback.cpp
main_process.h
main_process.cpp
graphics_control.h
graphics_control.cpp
bitmap.rc
main_menu.h
main_menu.cpp
main_menu.rc
<ソースのダウンロード>

因みに、”List002−019a””List002−019b”は実験用に作ったものなのでいったん破棄して、この”List002−020””List002−019”を元に手を加えたものである。

まず手をつけたのは、”bit_map.rc”
ここに、マスク用のビットマップファイルを追加する。
そして、関数SetupProcess()内でこのビットマップを読み込んで登録。
あ、グローバル変数enemy_mask_handleの宣言を追加することを忘れずに。

そして、いよいよ重ね合わせ処理の実装部分である。
さて、今回はここでちょっと洒落た(?)実装方法に挑戦してみた。
C++言語は関数の多重定義をサポートしている。
多重定義のルールとして、引数の内容を変えてやれば同名複数の関数を定義し、利用することが出来るとのこと。
こうして、”graphics_control.cpp”内の関数SetCharacter()2つ用意してみた。

追加したほうの関数SetCharacter()には、従来の引数に加え、マスク用ビットマップのハンドルの指示を追加してやった。
こうすることによって、背景の描画処理部分に手を加えることなく、また、関数MainTransaction()の変更も最低限で済む。
そしてなにより、新しい関数名を考えなくて済むわけだ。
これは便利。

こうして出来上がったアプリケーションの実行状況は左図のとおり。
タコ助は綺麗に背景と合成され、表示方法としてはひとまず完成。

しかし、こう云うコードを書いてみると、改めて描画関連関数、しいてはWin32APIの用意周到さには感心してしまう。

いや、まぁ、この程度の機能くらい用意しておいてくれなきゃ困る、と云うのも事実ではあるが、この機能が実装されたのは少なくとも1995年以前なわけだから、実に10年以上前に作られたライブラリの機能を使わせて貰ったと云うことで、ここは素直に感心しておくことにしよう。

ライブラリの汎用性とは、まさにこの様(?)な設計思想をもって語られるべきものなのだ、と判ったフリをしつつ次回へと続く。

<前章> <目次>





<時乃工房>
Net Office Nakai
メビウスリング投稿掲示板には小説日記ゲームアニメコミック小学生中学生などの掲示板過去ログがあります。相互リンクも募集中。