今回はスプライト処理をはじめとするテクニックを利用し、マップ、および主人公の描画をRPGライクに行います。

なお、今回からの解説は、First Seed Materialで公開されているRPG用素材を利用させていただきます。

今回作成するプログラムのサンプルファイルを置いておきますので、参考にしたい方はどうぞ☆ Fantasy.zip(プロジェクトに必要なファイルがすべて入っています)




このステップの要点は、スプライト処理です。ビットマップのマスク処理などといったほうが、親しみがあるかもしれません。具体的には、ビットマップに対して以下のような演算をさせ、画面に出力します。
=  AND  OR 

これを動かすとなると少し大変なので、まずは右上にあるような画面を作ってみることにしましょう。

ノーマル ウィンドウ ベースの新規プロジェクトを作成します(ここでは、プロジェクト名を "Fantasy" などと、いかにもそれらしくとしておきます(^^;)。

プロジェクトが保存されるディレクトリ内にキャラクタチップ "char.bmp"、マップチップ "road.bmp"を入れておいて下さい。尚、冒頭でも述べたとおり、これらのファイルはFirst Seed Materialさんで公開されているRPG用素材ですので、二次配布などはご遠慮下さい。

今回は、以下のプログラムを MainWnd.sbp に記述するだけです。Createイベント、Paintイベントのプロシージャは手書きでも、RADツールから生成してもOKです。

' ----------------------------------------------------------------------------
' イベント プロシージャ
' ----------------------------------------------------------------------------
' このファイルには、ウィンドウ [MainWnd] に関するイベントをコーディングします。
' ウィンドウ ハンドル: hMainWnd

' メモ - 以下の領域を、変数、構造体、定数、関数を宣言するための、
' グローバル領域として利用することができます。
' ----------------------------------ここから----------------------------------

'画面の大きさ X*Y
Const MAX_MAPX=25
Const MAX_MAPY=20

'マップチップ画像
Const FileName_Road="road.bmp"
Const MAPCHIP_PIXEL=16 'マップチップのピクセル数
Dim hRoadTipsDC As DWord
Dim hRoadTipsBmp As DWord

'キャラクタチップ画像
Const FileName_Char="char.bmp"
Const CHAR_PIXEL_X=24 'キャラクタチップの幅(単位はピクセル)
Const CHAR_PIXEL_Y=32 'キャラクタチップの高さ(単位はピクセル)
Dim hCharTipsDC As DWord
Dim hCharTipsBmp As DWord

'マップ画像
Dim hMapDC As DWord
Dim hMapBmp As DWord

'画面表示用の画像
Dim hMemDC As DWord
Dim hMemBmp As DWord

'描画位置
Dim CharPos As POINTAPI
CharPos.x=12 '初期位置X
CharPos.y=10 '初期位置Y



ちょっとキャラクタを表示するだけなのに、こんなにデータが必要なんですね。はじめは、こんなもんだと思って、コツコツと定義しておきましょう。後々、理解できるようになるので…(たぶん)。

Sub DrawMap()
   Dim x As Long, y As Long

   'マップを描画
   For y=0 To MAX_MAPY-1
       For x=0 To MAX_MAPX-1
           BitBlt(hMapDC,x*MAPCHIP_PIXEL,y*MAPCHIP_PIXEL,MAPCHIP_PIXEL,MAPCHIP_PIXEL,
               hRoadTipsDC,0,128,SRCCOPY)
       Next
   Next
End Sub



DrawMap関数は、マップチップ内のデータ(ここでは草原?のみ)を取り出し、それをhMapDCに敷き詰めます。hMapDCは、この下にあるDrawCharacter関数で利用されます。

Sub DrawCharacter()
   Dim hMaskDC As DWord
   Dim hMaskBmp As DWord
   Dim hTempDC As DWord
   Dim hTempBmp As DWord

   'hMemDCにhMapDCをコピー
   BitBlt(hMemDC,0,0,MAX_MAPX*MAPCHIP_PIXEL,MAX_MAPY*MAPCHIP_PIXEL,hMapDC,0,0,SRCCOPY)

   'マスク用画像を作成
   hMaskDC=CreateCompatibleDC(hMemDC)
   hMaskBmp=CreateBitmap(CHAR_PIXEL_X,CHAR_PIXEL_Y,1,1,0)
   SelectObject(hMaskDC,hMaskBmp)
   BitBlt(hMaskDC,0,0,CHAR_PIXEL_X,CHAR_PIXEL_Y,hCharTipsDC, 1*CHAR_PIXEL_X, 2*CHAR_PIXEL_Y, SRCCOPY)

   'スプライト用のキャラクタ画像を作成
   hTempDC=CreateCompatibleDC(hMemDC)
   hTempBmp=CreateCompatibleBitmap(hMemDC,CHAR_PIXEL_X,CHAR_PIXEL_Y)
   SelectObject(hTempDC,hTempBmp)
   BitBlt(hTempDC,0,0,CHAR_PIXEL_X,CHAR_PIXEL_Y,hMaskDC,0,0,NOTSRCCOPY)
   BitBlt(hTempDC,0,0,CHAR_PIXEL_X,CHAR_PIXEL_Y,hCharTipsDC, 1*CHAR_PIXEL_X, 2*CHAR_PIXEL_Y,SRCAND)

   'マスク画像をAND転送
   BitBlt(hMemDC, _
       CharPos.x*MAPCHIP_PIXEL-((CHAR_PIXEL_X-MAPCHIP_PIXEL)/2), _
       CharPos.y*MAPCHIP_PIXEL-((CHAR_PIXEL_Y-MAPCHIP_PIXEL)/2), _
       CHAR_PIXEL_X,CHAR_PIXEL_Y, _
       hMaskDC, 0, 0, SRCAND)

   'キャラクタ画像をOR転送
   BitBlt(hMemDC, _
       CharPos.x*MAPCHIP_PIXEL-((CHAR_PIXEL_X-MAPCHIP_PIXEL)/2), _
       CharPos.y*MAPCHIP_PIXEL-((CHAR_PIXEL_Y-MAPCHIP_PIXEL)/2), _
       CHAR_PIXEL_X,CHAR_PIXEL_Y, _
       hTempDC,0,0,SRCPAINT)

   'マスク用画像をメモリから解放
   DeleteDC(hMaskDC)
   DeleteObject(hMaskBmp)

   'キャラクタの一時画像をメモリから解放
   DeleteDC(hTempDC)
   DeleteObject(hTempBmp)
End Sub

' ----------------------------------ここまで----------------------------------



この、DrawCharacter関数で、スプライト処理が行われます。元のキャラクタ画像を加工し、マスク画像を作り出す機能も備わっています。

Sub MainWnd_Destroy()
   DeleteDC(hRoadTipsDC)
   DeleteObject(hRoadTipsBmp)
   DeleteDC(hCharTipsDC)
   DeleteObject(hCharTipsBmp)
   DeleteDC(hMapDC)
   DeleteObject(hMapBmp)
   DeleteDC(hMemDC)
   DeleteObject(hMemBmp)

   Fantasy_DestroyObjects()
   PostQuitMessage(0)
End Sub



Destroyイベントで、使用したデバイス コンテキスト、ビットマップ ハンドルを解放します。

Sub MainWnd_Create(ByRef CreateStruct As CREATESTRUCT)
   Dim hDC As DWord

   'マップチップ画像の読み込み
   hRoadTipsBmp=LoadImage(GetModuleHandle(0),
       FileName_Road,IMAGE_BITMAP,0,0,LR_LOADFROMFILE or LR_DEFAULTSIZE)
   If hRoadTipsBmp=0 Then
       MessageBox(hMainWnd,Ex"\qroad.bmp\qの読み込みに失敗","Error",MB_OK or MB_ICONEXCLAMATION)
       PostQuitMessage(0)
   End If

   'キャラクタチップ画像の読み込み
   hCharTipsBmp=LoadImage(GetModuleHandle(0),
       FileName_Char,IMAGE_BITMAP,0,0,LR_LOADFROMFILE or LR_DEFAULTSIZE)
   If hCharTipsBmp=0 Then
       MessageBox(hMainWnd,Ex"\qchar.bmp\qの読み込みに失敗","Error",MB_OK or MB_ICONEXCLAMATION)
       PostQuitMessage(0)
   End If

   hDC=GetDC(hMainWnd)

   '各画像のデバイスコンテキストを作成
   hRoadTipsDC=CreateCompatibleDC(hDC)
   hCharTipsDC=CreateCompatibleDC(hDC)
   hMapDC=CreateCompatibleDC(hDC)
   hMemDC=CreateCompatibleDC(hDC)

   '各デバイスコンテキストにビットマップを選択
   SelectObject(hRoadTipsDC,hRoadTipsBmp)
   SelectObject(hCharTipsDC,hCharTipsBmp)
   hMapBmp=CreateCompatibleBitmap(hDC,MAX_MAPX*MAPCHIP_PIXEL,MAX_MAPY*MAPCHIP_PIXEL)
   SelectObject(hMapDC,hMapBmp)
   hMemBmp=CreateCompatibleBitmap(hDC,MAX_MAPX*MAPCHIP_PIXEL,MAX_MAPY*MAPCHIP_PIXEL)
   SelectObject(hMemDC,hMemBmp)

   '透明色を設定
   SetBkColor(hCharTipsDC,RGB(0,117,117))

   ReleaseDC(hMainWnd,hDC)

   'hMapDCにマップ描画
   DrawMap()

   'hMemDCにキャラクタ描画
   DrawCharacter()
End Sub



Createイベントでは、各デバイス コンテキスト、ビットマップの生成を行い、マップ描画関数DrawMapとキャラクタ描画DrawCharacterを呼び出しています。

Sub MainWnd_Paint(hDC As Long)
   'ウィンドウにhMemDCの内容を描画
   BitBlt(hDC,0,0,MAX_MAPX*MAPCHIP_PIXEL,MAX_MAPY*MAPCHIP_PIXEL,hMemDC,0,0,SRCCOPY)
End Sub



Paintイベントでは、hMemDCの内容をウィンドウに再描画するプログラムを記述します。

さて、これで準備は完了です。コンパイル、実行を行うと、ウィンドウ内に草原と主人公キャラクタが出現します。
今回はただ単に表示するだけでしたが、次回はキー操作に反応し、マップ上を主人公キャラが歩くようなプログラムを作っていきます。トコトコと、本格的な歩き方をするので、是非挑戦してみて下さい!