シーンを組み立てる

半透明(アルファ・ブレンディング) を使って、魔法のシーンや攻撃のシーンに挑戦します。
RPGゲームなどでは「魔法や攻撃のシーン」が売り物の一つになっています。
scene.txt で自由に Sprite の動きを設定して、アニメーションを楽しんで下さい。

テストに用いた画像は「背景画像」と次の三種類の Sprite 画像です。


前田稔(Maeda Minoru)の超初心者のプログラム入門

プログラムの説明

  1. 私有ライブラリを使うので my.h を組み込んで my.lib をリンクしています。
    HEAD はシーン情報を記録する構造体で、SPR は Sprite 情報を記録する構造体です。
    IMAGE は BMP, JPEG, GIF を表示できる総合的な画像のクラスで Sprite Class の機能も併せ持っています。
    構造体の変数の多くはシーンを構成する scene.txt から取得して設定します。
        #include    "My.h"
        #pragma     comment(lib,"my.lib")
        #define     ID_TIMER    (WM_APP + 0)
        typedef struct
        {   int     SP_N;           //Sprite の枚数
            int     FM_N;           //フレーム数
            int     SPEED;          //タイマ速度
            char    File[MAX_PATH]; //背景画像ファイル名
            long    Width,Height;   //画像の幅と高さ
            IMAGE   *Img;           //背景画像
        }   HEAD;
    
        typedef struct
        {   char    File[MAX_PATH]; //Sprite ファイル名
            int     WNum;           //横方向の SPrite 個数
            int     HNum;           //縦方向の SPrite 個数
            IMAGE   *Img;           //Sprite Object Class
            int     NO;             //Sprite の番号
            int     XP;             //X座標
            int     YP;             //Y座標
            float   SCL;            //倍率
            float   RATE;           //透明度
        }   SPR;
        
  2. CSV は scene.txt に記述されているカンマで区切られたデータを編集する Object Class です。
    TBL がアニメーションを制御するパラメータを格納する領域で、FRM_NO が現在描画中のフレームです。
    最大10枚の Sprite を 5000 フレームまで格納できる領域を定義しています。
    [5] は Sprite を制御するパラメータで「Sprite の番号,X座標,Y座標,倍率,透明度」を制御します。
    これらのデータは TEXT 形式のファイル(scene.txt)に記述されています。
    ゲームプログラムでは、シーンが完成するまでに何度もパラメータの試行錯誤が繰り返されるので、毎回プログラムを変更しなくてもテストできるようにします。
    *Surface は画像をブレンドするためのサーフェースの定義です。
    *Back は描画のチラツキを無くすための BackBuffer Class の定義です。
        HINSTANCE       g_hInst;
        HWND            g_hWnd;
        CSV             Csv;                    //CSV Object Class
        HEAD            head;                   //Header 構造体
        SPR             spr[10];                //Sprite 構造体
        float           TBL[10][5][5000];       //番号,X座標,Y座標,倍率,透明度
        int             FRM_NO=0;               //描画するフレーム番号
        SURFACE         *Surface;               //GIF 画像を転送して Sprite をブレンド
        BACKBUF         *Back= NULL;            //Back Buffer Object
        
  3. WinMain() は何時もと同じでウインドウを生成して表示するだけです。
  4. WM_CREATE: ではアプリケーションを初期化して、最初の描画を行うために WM_LBUTTONDOWN をポストします。
        {   case WM_CREATE:         //初期化
                AppInit(hWnd);
                PostMessage(hWnd,WM_LBUTTONDOWN,0,0);
                break;
        
  5. WM_LBUTTONDOWN: では FRM_NO をカウントアップしてシーンを描画します。
    WM_PAINT: から描画するとチラツクので、Render() 関数で直接描画しています。
    右ボタンがクリックされると、フレーム番号を先頭のフレームに戻します。
            case WM_LBUTTONDOWN:    //左ボタンをクリック
                KillTimer(hWnd,ID_TIMER);
                FRM_NO++;
                Render();
                break;
            case WM_RBUTTONDOWN:    //右ボタンをクリック
                KillTimer(hWnd,ID_TIMER);
                FRM_NO= 0;
                PostMessage(hWnd,WM_LBUTTONDOWN,0,0);
                break;
        
  6. キーがタイプされたときの処理です。
    Space または Enter キーが押されると、タイマ割り込みを使って連続してアニメーションを行います。
    Esc キーで連続処理を停止します。
    テンキーの操作(1,5)で、フレーム番号をアップします。
            case WM_KEYDOWN:        //KEY が押された
                switch (wp)
                {   case VK_SPACE:  //SPACE
                    case VK_RETURN: //RETURN
                        SetTimer(hWnd,ID_TIMER,head.SPEED,NULL);
                        break;
                    case VK_ESCAPE: //ESC KEY
                        KillTimer(hWnd,ID_TIMER);
                        break;
                    case VK_NUMPAD1://テンキーの 1
                        FRM_NO+= 100;
                        Render();
                        break;
                    case VK_NUMPAD5://テンキーの 5
                        FRM_NO+= 50;
                        Render();
                        break;
                }
                break;
        
  7. WM_TIMER: で連続してアニメーションを行います。
            case WM_TIMER:
                FRM_NO++;
                Render();
                if (FRM_NO>=head.FM_N-1)   KillTimer(hWnd,ID_TIMER);
                break;
        
  8. プログラムの終了前にリソースを開放して下さい。
            case WM_CLOSE:          //クローズ
                KillTimer(hWnd,ID_TIMER);
                SAFE_DELETE(head.Img);
                for(i=0; i<head.SP_N; i++)  SAFE_DELETE(spr[i].Img);
                SAFE_DELETE(Surface);
                SAFE_DELETE(Back);
                DestroyWindow(hWnd);
                break;
        
  9. アプリケーションの初期化を行う AppInit() 関数です。
    Scene.txt を入力してシーンの情報を入力します。
    ファイルの詳細は「scene.txt の形式」を参照して下さい。
        void  AppInit(HWND hwnd)
        {   char    *p;
            int     i,j;
    
            ZeroMemory(&head,sizeof(HEAD));
            ZeroMemory(spr,sizeof(SPR)*10);
            //Scene.txt を入力する
            Csv.ReadData("Scene.txt");
            p= Csv.SetInt(&head.SP_N,Csv.buf,1);
            p= Csv.SetInt(&head.FM_N,p,1);
            p= Csv.SetInt(&head.SPEED,p,1);
            p= Csv.NextRec(p);
            p= Csv.SetStr(head.File,p);
            //背景画像の入力とウインドウサイズの設定
            head.Img= new IMAGE(hwnd);
            head.Img->LoadFile(head.File);
            head.Width= head.Img->Width;
            head.Height=head.Img->Height;
            //Sprite 画像を入力
            for(i=0; i<head.SP_N; i++)
            {   p= Csv.NextRec(p);
                p= Csv.SetStr(spr[i].File,p);
                p= Csv.SetInt(&spr[i].WNum,p,1);
                p= Csv.SetInt(&spr[i].HNum,p,1);
                spr[i].Img= new IMAGE(hwnd);
                spr[i].Img->LoadFile(spr[i].File,spr[i].WNum,spr[i].HNum);
            }
            //Sprite の描画データを入力
            for(i=0; i<head.SP_N; i++)
            {   p= Csv.NextRec(p);
                for(j=0; j<5; j++)
                {   p= Csv.SetFloat(&TBL[i][j][0],p,head.FM_N);
                }
            }
            head.Img->Adjust(FALSE);
            // Surface を作成
            Surface= new SURFACE(hwnd,head.Img->Width,head.Img->Height);
            Back= new BACKBUF(hwnd);
        }
        
  10. TBL に設定された情報に基づいて描画を行う Render() 関数です。
    背景画像を描画した上から Sprite を重ねて描画します。
    描画パラメータの詳細は「scene.txt の形式」を参照して下さい。
        VOID  Render()
        {   int     i;
    
            head.Img->Show(Back->hBackDC,0,0);
            for(i=0; i<head.SP_N; i++)
            {   spr[i].NO= (int)TBL[i][0][FRM_NO];
                if (spr[i].NO<0)    continue;
                spr[i].XP= (int)TBL[i][1][FRM_NO];
                spr[i].YP= (int)TBL[i][2][FRM_NO];
                spr[i].SCL=  TBL[i][3][FRM_NO];
                spr[i].RATE= TBL[i][4][FRM_NO];
                //ブレンドレート/スケールの設定
                if (spr[i].RATE>0.0f || spr[i].SCL<1.1f || spr[i].SCL>0.9f)
                {   Surface->ClearW((HBRUSH)GetStockObject(BLACK_BRUSH));
                    spr[i].Img->ShowSize(Surface->hWBmpDC,spr[i].NO,0,0,spr[i].SCL);
                    Surface->Blend(Back->hBackDC,spr[i].XP,spr[i].YP,spr[i].RATE);
                }
                //通常の上書き描画
                else    spr[i].Img->Show(Back->hBackDC,spr[i].NO,spr[i].XP,spr[i].YP);
            }
            Back->Blt();
        }
        

scene.txt の形式

  1. シーンの情報を記録する screen.txt です。
     3, 2000, 100,          //Sprite の枚数, フレーム数, タイマ速度
    ダンジョン1.jpg,        //背景画像
    Sprite1.gif, 4,1,
    Sprite2.gif, 3,1,
    Sprite3.gif, 1,1,
    
    //※Sprite1 稲妻の4枚の画像
    //番号
    -1,-1,-1,
    0,0,0,1,1,1, (6)-1,
    0,0,0,1,1,1, -1,-1,-1, 
    0,0,0,1,1,1,2,2,2, (6)-1,
    0,0,0,1,1,1,2,2,2,3,3,3,
    (>2000)-1,
    //X座標
    (15)180,(9)420,(15)250,(12)280,
    (>2000)0,
    //Y座標
    //(33)100,(18)120,
    (15 +1)100,(9 +1)100,(15 +1)100,(12 +1)110,
    (>2000)0,
    //倍率
    (15)0.6, (9)0.7, (15)1.0, (12)1.5,
    (>2000)1.0,
    //透明度
    (15 -0.02)1.0, (9 -0.02)0.7, (15 -0.02)0.5,
    (>2000)0,
    
    //☆以下同様に Sprite2, Sprite3 のデータが続きます。
            :
    
  2. 先頭の行に「Sprite の枚数, フレーム数, タイマ速度」を記述します。
    二行目に背景画像のファイル名を記述します。
    ウインドウのサイズは背景画像の大きさに合わせて設定されます。
    続いて次の行から枚数分だけ Sprite の記述を行います。
    Sprite の記述は「ファイル名」「横の枚数」「縦の枚数」をカンマで区切って記述します。
  3. 次の行から Sprite を描画するパラメータの記述を行います。
    パラメータは「Sprite の番号,X座標,Y座標,倍率,透明度」の順に、フレーム数で指定した数だけ数値を並べます。
    Sprite 番号が -1 のときは Sprite は描画されません。
    透明度は 0.0 ~ 1.0 で、1.0 に近づくほど薄くなります。
  4. パラメータの記述方法は シップの動きを Text File で制御する を参照して下さい。

超初心者のプログラム入門(Win32API C++)