DIBSectionによる4方向スクロールテスト

 これまで様々なスクロールを試してきましたが、今回はゲームなどに応用しやすいDIBSectionによる4方向スクロールの実験です。この方式のスクロールはかなり高速で、例えば今回のプログラムをPentuim2-400MHz, G200という環境で動かすとスクロール量から推定した描画回数は毎秒100回を軽く超えています。もっとも、実際のゲームでいろいろな処理を入れて行けば速度も落ちるでしょうし、環境によっても変わってくるのですが。

DIBSectionによるスクロール

今回のスクロール画面には、32×32のマップパーツを15×15表示(全体で480×480)します。スクロールの原理は、RPGの時と同じですが、今回はスクロール用ビットマップをダブルバッファのビットマップと兼用にしてその一部を直接画面に描画するようにしました。
 実際のゲームでは、キャラクタを描き込む必要があるので別にバッファを1枚用意して、そこにマップ・キャラクタを描いてから画面に表示する事になるでしょう。

DIBSectionのビットマップは、画面より一回り大きい17×17パーツ分・544×544ドットのスクロールビットマップ用ビットマップを1枚、パーツ用ビットマップを4枚作成します。

  hwMain=CreateWindow ("CWindow",NULL,
    WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,600,560,
    NULL,NULL,hInstance,NULL);

  lpScDIB=(LPBITMAPINFO)GlobalAlloc /* DIBヘッダ用メモリ確保 */
    (GPTR,sizeof(BITMAPINFO)+255*sizeof(RGBQUAD));
  lpChDIB=(LPBITMAPINFO)GlobalAlloc
    (GPTR,sizeof(BITMAPINFO)+255*sizeof(RGBQUAD));

  /* スクロールビットマップ用BITMAPINFO */
  lpScDIB->bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
  lpScDIB->bmiHeader.biWidth=544;
  lpScDIB->bmiHeader.biHeight=544;
  lpScDIB->bmiHeader.biPlanes=1;
  lpScDIB->bmiHeader.biBitCount=8;
  lpScDIB->bmiHeader.biCompression=BI_RGB;

  /* マップパーツ用BITMAPINFO */
  lpChDIB->bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
  lpChDIB->bmiHeader.biWidth=32;
  lpChDIB->bmiHeader.biHeight=32;
  lpChDIB->bmiHeader.biPlanes=1;
  lpChDIB->bmiHeader.biBitCount=8;
  lpChDIB->bmiHeader.biCompression=BI_RGB;

  for (i=0;i<6;i++) /* カラーテーブル作成 */
      for (j=0;j<6;j++)
          for (k=0;k<6;k++) {

              RGBs[i*36+j*6+k].rgbRed=i*51;
              RGBs[i*36+j*6+k].rgbGreen=j*51;
              RGBs[i*36+j*6+k].rgbBlue=k*51;

          }

  /* 作成したカラーテーブルをビットマップのカラーテーブルに設定 */
  CopyMemory(lpScDIB->bmiColors,RGBs,256*sizeof(RGBQUAD));
  CopyMemory(lpChDIB->bmiColors,RGBs,256*sizeof(RGBQUAD));

  /* DIBヘッダとウインドウのDC からDIBSectionを作成 */
  hScBMP=CreateDIBSection
    (GetDC(hwMain),lpScDIB,DIB_RGB_COLORS,(void **)&lpScreen,NULL,0);
  hdcScMem=CreateCompatibleDC(GetDC(hwMain)); /* メモリDC を作成 */
  SelectObject(hdcScMem,hScBMP); /* メモリDC にビットマップを選択 */

  for (i=0;i<4;i++) { /* マップパーツ用ビットマップ作成 */

      hPtBMP[i]=CreateDIBSection(GetDC(hwMain),lpChDIB,DIB_RGB_COLORS,
        (void **)&lpParts[i],NULL,0);

      hdcPtMem[i]=CreateCompatibleDC(GetDC(hwMain)); /* メモリDC を作成 */
      SelectObject(hdcPtMem[i],hPtBMP[i]); /* メモリDC にビットマップを選択 */

  }

  FillMemory(lpParts[0],32*32,5);
  for (i=0;i<48;i++)
      *(lpParts[0]+rand() % 1024)=173;

  FillMemory(lpParts[1],32*32,61);
  for (i=0;i<48;i++)
      *(lpParts[1]+rand() % 1024)=84;

  FillMemory(lpParts[2],32*32,172);
  for (i=0;i<32;i++)
      *(lpParts[2]+rand() % 1024)=215;

  FillMemory(lpParts[3],32*32,180);
  for (i=0;i<32;i++)
      *(lpParts[3]+rand() % 1024)=182;

 ビットマップとマップを作成したら、後はメインループに入りそこでキー入力の判定を行ってカーソルキーが押されていたらscroll()を呼び出して押された方向にスクロールします。スクロールの原理は、スクロールする方向の端一列にキャラクタを描いて、少しずつずらしながら表示していく、というものです。

  int scroll(void) { /* スクロール処理 */

      int iLeft,iTop;
      MSG msg;

      while(iDx!=iScd && iDy!=iScd && iDx!=64-iScd && iDy!=64-iScd) {

          if (GetTickCount()>dwTime+1) {

              dwTime=GetTickCount();

              /* 表示開始位置をずらしてスクロール */
              iDx+=iDdx;
              iDy+=iDdy;

              /* ウインドウを再描画 */
              InvalidateRect(hwMain,&rec,FALSE);

              if (PeekMessage (&msg,NULL,0,0,PM_NOREMOVE)) {

                  if (!GetMessage (&msg,NULL,0,0)) /* メッセージ処理 */
                      return msg.wParam ;

                  TranslateMessage(&msg);
                  DispatchMessage(&msg);

              }

          }
      }

      /* 表示中マップの中央とスクロール用ビットマップの中央を合わせる */
      if (iDdy<0) { /* 上スクロール時 */

          iLeft=32;
          iTop=0;

      } else if (iDdx>0) { /* 右スクロール時 */

          iLeft=64;
          iTop=32;

      } else if (iDdy>0) { /* 下スクロール時 */

          iLeft=32;
          iTop=64;

      } else if (iDdx<0) { /* 左スクロール時 */

          iLeft=0;
          iTop=32;

      }

      /* 表示中マップをスクロール用ビットマップ中央に描きなおす */
      BitBlt(hdcScMem,32,32,480,480,hdcScMem,iLeft,iTop,SRCCOPY);

      iDx=32;
      iDy=32;
      InvalidateRect(hwMain,&rec,FALSE);

  }

 DirectDrawの時には、バックバッファに画面を描いてフリップして表示していましたが、DIBSectionはGDIで描画するのでスクロール用ビットマップを画面に表示する時にはウインドウを更新してWM_PAINTメッセージの処理で描画しています。

  case WM_PAINT:

      hdc=BeginPaint(hwnd,&ps);

      /* マップビットマップを描画 */
      BitBlt(hdc,8,8,480,480,hdcScMem,iDx,iDy,SRCCOPY);

      break;

プログラム

 カーソルキーで上下左右にスクロールしてください。スクロールの速度を変えるには、最初の方にあるiScdの値を変更します。大きくするほどスクロール量が増えますが、その分滑らかさが失われるので通常は2、高性能なビデオカードを使用しているなら1にすると良いでしょう。

プログラムソース表示

 今回の実験で、DIBSectionベースの画面描画でもゲームを作る上で十分に実用になる事が確認できたと思います。DIBSectionベースなら、通常のWindowsアプリとしてゲームを作れますし、プログラムの開発も楽ですよね。ちょっとしたRPGやアドベンチャーを創ってみるなら、無理にDirectDrawにしないでも良いのかもしれません。


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