スプライトの描画速度(DIBSection)

これまで、DIBDIBSectionベースでソフトウエアスプライトなどゲームを意識した画像処理をいろいろ試して来ました。実験では、処理自体はうまく行ったものの実際にゲームとなると多数のキャラクタを透過表示したり大量の描画が発生するはず。そこで、今回はソフトスプライトやDIB間のメモリ転送が実際にどの程度の性能を発揮できるか、テストしてみましょう。

今回のテストでは、ゲームのマップ表示処理を意識して描画画面を1パーツ32×32ピクセル、15×15パーツで構成される480×480ピクセルにします。このピクセルにビットマップのパーツを描いてその上に32×32ピクセルのキャラクタを透過処理をしながら描いていきましょう。描画方法は、まず480×480ピクセルのマップ用DIBにパーツを描いてそれをバックバッファのDIBSectionBltし、その後バックバッファにキャラクタのピクセル列を転送します。今回は、スクロールしないのでマップはバックバッファに直接描いても良いのですが、実際のゲームのマップ表示処理を意識して一度バックバッファに転送する事にしました。使用するビットマップは以下のようになります。

バックバッファ

 BITMAPINFOlpbiBack
 ピクセル列lpBackPix

マップ

 BITMAPINFOlpbiMap
 ピクセル列lpMapPix

マップパーツ

 BITMAPINFOなし
 ピクセル列lpParts[4]

キャラクタ

 BITMAPINFOなし
 ピクセル列lpChr

 これらのメモリは、CreateDIBSection()で確保するlpBackPix以外はlpBufにまとめて確保し、ポインタを分配します。

  lpBuf=(LPBYTE)GlobalAlloc(GPTR,
    (sizeof(BITMAPINFO)+255*sizeof(RGBQUAD))*2+32*32*5+480*480);

  lpbiBack=(LPBITMAPINFO)lpBuf;
  lpbiMap=(LPBITMAPINFO)(lpBuf+sizeof(BITMAPINFO)+255*sizeof(RGBQUAD));
  lpChr=lpBuf+(sizeof(BITMAPINFO)+255*sizeof(RGBQUAD))*2;
  for (i=0;i<4;i++)
      lpParts[i]=lpChr+1024*(i+1);
  lpMapPix=lpParts[3]+1024;

バッファを確保したら、マップとバックバッファのBITMAPINFOを設定し、バックバッファのDIBSectioを作ります。

  /* BITMAPINFOとウインドウのDCからDIBSectionを作成 */
  hBack=CreateDIBSection
    (GetDC(hwMain),lpbiBack,DIB_RGB_COLORS,(void **)&lpBackPix,NULL,0);
  hdcBack=CreateCompatibleDC(GetDC(hwMain)); /* メモリDC を作成 */
  SelectObject(hdcBack,hBack); /* メモリDC にビットマップを選択 */

これでビットマップの用意が出来たので、後はマップパーツとキャラクタのグラフィックを適当に作って準備完了ですね。

フレームレート測定

マップの描画は、マップ配列byMapを参照しマップ用ビットマップの(x,y)の位置にbyMap[x][y]のパーツを描いていきます。

  for (i=0;i<15;i++)
      for (j=0;j<15;j++)
          for (k=0;k<32;k++)
              CopyMemory(lpMapPix+j*32+(i*32+k)*480,lpParts[byMap[j][i]]+k*32,32);

マップ配列は最初に乱数で設定し、以降描画の度に以下のようにしていくつかのパーツを「上下左右のパーツと入れ替える」処理をするようにしました。

  for (i=0;i<16;i++) {

      x1=rand() % 13+1;
      y1=rand() % 13+1;
      x2=x1+(rand() % 3)-1;
      y2=y1+(rand() % 3)-1;

      byW=byMap[x1][y1];
      byMap[x1][y1]=byMap[x2][y2];
      byMap[x2][y2]=byW;

  }

マップが出来たら、そのマップをバックバッファに描画します。

  StretchDIBits(hdcBack,0,0,480,480,0,0,480,480,
    lpMapPix,lpbiMap,DIB_RGB_COLORS,SRCCOPY);

続いて、バックバッファにキャラクタを描く処理。これは、乱数で適当な位置にキャラクタlpChrを0を抜き色にして描画する事にします。

  for (i=0;i<128;i++) {

      /* キャラクタの表示位置決定 */
      x1=rand() % 15;
      y1=rand() % 15;

      /* キャラクタを描画 */
      for (j=0;j<32;j++)
          for (k=0;k<32;k++)
              if (lpChr[k+j*32]!=0)
                  lpBackPix[x1*32+k+(y1*32+j)*480]=lpChr[k+j*32];

   }

 このマップとキャラクタの描画を関数test()内で行い、処理が終わったら前回の処理からの経過時間を調べ、その経過時間を元にフレームレートを計算します。

  /* 前回の処理からの経過時間取得 */
  dwTime2=timeGetTime()-dwTime;

  /* 今回の処理終了時間を記録 */
  dwTime=timeGetTime();

  /* フレームレート計算 */
  dwFrate=1000/dwTime2;

  /* フレームレート表示用文字列作成 */
  wsprintf(str,"Frame Rate=%dFPS",dwFrate);

  /* バックバッファにフレームバッファを表示 */
  DrawText(hdcBack,str,-1,&rec,DT_CENTER);

  /* ウインドウを再描画してバックバッファを反映 */
  InvalidateRgn(hwMain,NULL,FALSE);
  UpdateWindow (hwMain);

プログラム

このプログラムはtimeGetTime()を使用しているので、winmm.libをリンクしビルドしてください(VC+++5では、プロジェクトメニューの設定ーリンクでwinmm.libを追加する)。実行すると、背景にマップが描かれキャラクタが表示されます。

実行結果を見ると、128個のキャラクタを表示してもフレームレートは60を超えるみたいですね。意外に速い。なお、このプログラムではキャラクタが激しく点滅して表示されるのであまり画面を見つめないほうが良いかもしれません。

プログラムソース表示


ゲーム制作研究室 > プログラミング資料庫