Map Editor の Main Program

Map Editor の Main Program の説明です。

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

プログラムの説明

  1. 次のファイルをプロジェクトに追加して MAPEDIT Object Class を組み込んで下さい。
    ファイル名説明
    Mapedit.h MAPEDIT Object Class のヘッダファイル
    Mapedit.cppMAPEDIT Object Class のプログラムファイル
  2. Main Program の #include と定義です。
    MAPEDIT Object Class のヘッダファイルを組み込みます。
    HEAD 構造体はファイルに保存するときのヘッダ領域の定義です。
        /***************************************/
        /*★ Mapeditor を作成する    前田 稔 ★*/
        /***************************************/
        #define     NAME    "Mapedit"
        #define     TITLE   "Map Editor  Maeda Minoru"
        #include    <windows.h>
        #include    <windowsx.h>
        #include    "Mapedit.h"
        #include    "resource.h"
        #define     SAFE_DELETE(p)  { if (p) { delete (p);  (p)=NULL; } }
        //セーブファイルに記録する File Header
        typedef struct
        {   char    id[8];          //"Mapedit"
            char    ver[8];         //"Ver 1.0"
            char    file[MAX_PATH]; //Mapchip ファイル名(Map->szFile)
            long    Xnum,Ynum;      //Map に並べる幅と縦のセル数(Map->Xnum,Map->Ynum)
            long    SWidth,SHeight; //Sprite の幅と高(Map->SWidth,Map->SHeight)
            long    SnumX,SnumY;    //Sprite の枚数(Map->SnumY,Map->SnumY)
            int     Buton[24];      //ラジオボタンの情報
        }   HEAD;
        
  3. g_hWnd, g_hMap, g_hDlg は Mapedit の三枚のウインドウのハンドルです。
    *Map は MAPEDIT Object Class の定義です。
    TBL[8][500][500]; は8枚のレイヤの定義です。
    SEL にはマウスで選択された矩形領域を設定します。
    SIZ には選択されたセルのサイズを格納します。
    RCT には MainWindow 上にセルを貼り付けるときの矩形領域を設定します。
    LAY は現在作業中のレイヤです。
    ID[24] はラジオボタンのIDの定義です。
    hFile; からはファイル入出力関係の領域の定義です。
    SCROLLINFO 以降は MainWindow と Mapchip のスクロールバーの定義です。
        HEAD        H;
        HINSTANCE   g_hInst;
        HWND        g_hWnd;
        HWND        g_hMap;
        HWND        g_hDlg;
        MAPEDIT     *Map= NULL;
    
        int         TBL[8][500][500];
        RECT        SEL;                //選択されたセル範囲
        SIZE        SIZ;                //選択されたセルのサイズ
        RECT        RCT;                //貼り付ける座標
        int         LAY= 0;             //作業中のレイヤ
        HBRUSH      hBrush;
        HPEN        hPen,hOldPen;
        int         ID[24]=
        { IDC_RADIO1,IDC_RADIO2,IDC_RADIO3,IDC_RADIO4,IDC_RADIO5,IDC_RADIO6,IDC_RADIO7,IDC_RADIO8,
          IDC_RADIO9,IDC_RADIO10,IDC_RADIO11,IDC_RADIO12,IDC_RADIO13,IDC_RADIO14,IDC_RADIO15,IDC_RADIO16,
          IDC_RADIO17,IDC_RADIO18,IDC_RADIO19,IDC_RADIO20,IDC_RADIO21,IDC_RADIO22,IDC_RADIO23,IDC_RADIO24
        };
    
        HANDLE      hFile;
        char        szFile[MAX_PATH];   //ファイル名(パス付き)
        DWORD       dwBytes;            //入力(出力)長
        char        buf[5000];          //入力(出力)領域
        //スクロール制御領域
        SCROLLINFO  w_sv;
        SCROLLINFO  w_sh;
        POINT       w_sp= { 0,0 };      //Wnd Scroll Position
        SCROLLINFO  m_sv;
        SCROLLINFO  m_sh;
        POINT       m_sp= { 0,0 };      //Scroll Position
        
  4. Windows Main 関数です。
    MainWindow と MapchipWindow を生成してメッセージループに入ります。
        int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR, int)
        {   MSG     msg;
    
            g_hInst= hInstance;
            if (!InitApp(NAME,WndProc,IDR_MENU1))   return FALSE;
            g_hWnd= InitInstance(NAME,0,0,700,600);
            if (g_hWnd==NULL)       return FALSE;
    
            if (!InitApp("MapWin",MapProc,0))   return FALSE;
            g_hMap= InitInstance("MapWin",600,200,300,300);
            if (g_hMap==NULL)       return FALSE;
    
            SetFocus(g_hMap);
            while (GetMessage(&msg,NULL,0,0))
            {   TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
            return msg.wParam;
        }
        
  5. Windows Initialize 関数です。
        ATOM  InitApp(LPSTR name,LRESULT (CALLBACK *Proc)(HWND,UINT,WPARAM,LPARAM),int ID)
        {
            WNDCLASSEX wc;
            wc.cbSize       = sizeof(WNDCLASSEX);
            wc.style        = CS_HREDRAW | CS_VREDRAW;
            wc.lpfnWndProc  = Proc;         //プロシージャ名
            wc.cbClsExtra   = 0;
            wc.cbWndExtra   = 0;
            wc.hInstance    = g_hInst;      //インスタンス
            wc.hIcon        = LoadIcon(g_hInst,MAKEINTRESOURCE(IDI_ICON1));
            wc.hCursor      = LoadCursor(NULL, IDC_ARROW);
            wc.hbrBackground= (HBRUSH)GetStockObject(WHITE_BRUSH);
            if (ID>0)   wc.lpszMenuName = MAKEINTRESOURCE(ID);
            else        wc.lpszMenuName = NULL;
            wc.lpszClassName= name;
            wc.hIconSm      = LoadIcon(g_hInst,MAKEINTRESOURCE(IDI_ICON1));
            return (RegisterClassEx(&wc));
        }
        
  6. InitInstance 関数です。
        HWND  InitInstance(LPSTR name, long xp, long yp, long w, long h)
        {   HWND    hWnd;
            hWnd= CreateWindow(name,TITLE,WS_OVERLAPPEDWINDOW,
                            xp,yp,w,h,NULL,NULL,g_hInst,NULL);
            if (!hWnd)      return NULL;
            ShowWindow(hWnd,SW_SHOW);
            UpdateWindow(hWnd);
            return hWnd;
        }
        
  7. Main Window の CALLBACK 関数です。
    WM_CREATE: でアプリケーションの初期化を行います。
    WM_PAINT: から Render() 関数で描画します。
        LRESULT  CALLBACK  WndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
        {   PAINTSTRUCT ps;
                :
                :
            switch(msg)
            {   case WM_CREATE:
                    App_Init(hWnd);
                    break;
                case WM_PAINT:
                    hdc= BeginPaint(hWnd,&ps);
                    Render(hWnd);
                    EndPaint(hWnd, &ps);
                    break;
        
  8. WM_COMMAND: です。
            case WM_COMMAND:
                switch(LOWORD(wParam))
                {   case IDM_OPEN:          //Open File
                        OpenFile();
                        break;
                    case IDM_SAVE:          //Save File
                        SaveFile();
                        break;
                    case IDM_LOAD:          //Mapchip Load
                        Map->OpenMapFile();
                        InvalidateRect(g_hWnd,NULL,TRUE);
                        InvalidateRect(g_hMap,NULL,TRUE);
                        break;
                    case IDM_CSV:           //CSV Save
                        SaveCSV();
                        break;
                    case IDM_SIZE:          //Map Size
                        DialogBox(g_hInst,MAKEINTRESOURCE(IDD_DIALOG1),NULL,(DLGPROC)DlgProc);
                        break;
                    case IDM_CLR:           //MAP のクリア
                        ZeroMemory(TBL,sizeof(int)*8*500*500);
                        ZeroMemory(H.Buton,sizeof(int)*24); 
                        H.Buton[0]=H.Buton[8]=H.Buton[16]= 1;
                        ButtonSet(g_hDlg);
                        InvalidateRect(g_hWnd,NULL,TRUE);
                        break;
                    case IDM_PAINT:         //Layer 全面にセルを表示
                        for(yw=0; yw<Map->Ynum; yw++)
                            for(xw=0; xw<Map->Xnum; xw++)
                                TBL[LAY][yw][xw]= SEL.top*Map->SnumX+SEL.left;
                        InvalidateRect(g_hWnd,NULL,TRUE);
                        break;
                    case IDM_CHIP:          //Mapchip Window を表示
                        ShowWindow(g_hMap,SW_SHOW);
                        break;
                    case IDM_LAY:           //Layer Window を表示
                        ShowWindow(g_hDlg,SW_SHOW);
                        UpdateWindow(g_hDlg);
                        break;
                    case IDM_VER:           //バージョン情報を表示
                        MessageBox(NULL,"Map Editor  Ver 1.0  by 前田 稔","Version", MB_OK);
                        break;
                    case IDM_END:
                        DestroyWindow(hWnd);
                        break;
                }
                break;
        
  9. MainWindow でマウスが操作されたときの処理です。
    左ボタンでセルの貼り付けを、右ボタンでセルの選択をします。
            case WM_LBUTTONDOWN:
                RCT.left= (LOWORD(lParam)/Map->SWidth)+w_sp.x;
                RCT.top = (HIWORD(lParam)/Map->SHeight)+w_sp.y;
                SetCapture(hWnd);
                break;
            case WM_RBUTTONDOWN:
                SEL.left=SEL.right= LOWORD(lParam)/Map->SWidth+w_sp.x;
                SEL.top=SEL.bottom= HIWORD(lParam)/Map->SHeight+w_sp.y;
                SetCapture(hWnd);
                break;
            case WM_MOUSEMOVE:
                if (wParam&MK_LBUTTON)
                {   RCT.right  = (LOWORD(lParam)/Map->SWidth)+w_sp.x;
                    RCT.bottom = (HIWORD(lParam)/Map->SHeight)+w_sp.y;
                    if (RCT.left==RCT.right && RCT.top==RCT.bottom) break;
                    RCT.left= RCT.right;
                    RCT.top= RCT.bottom;
                    RCT.right= RCT.left+SIZ.cx;
                    RCT.bottom= RCT.top+SIZ.cy;
                    if (SIZ.cx<2)   TBL[LAY][RCT.top][RCT.left]= TBL[7][0][0];
                    Render(hWnd);
                    break;
                }
                if (wParam&MK_RBUTTON)
                {   xp= LOWORD(lParam)/Map->SWidth+w_sp.x;
                    yp= HIWORD(lParam)/Map->SHeight+w_sp.y;
                    if (SEL.right!=xp || SEL.bottom!=yp)
                    {   SEL.right= xp;
                        SEL.bottom= yp;
                        RenderSel(hWnd);
                    }
                }
                break;
            case WM_LBUTTONUP:
                ReleaseCapture();
                RCT.right= RCT.left+SIZ.cx;
                RCT.bottom= RCT.top+SIZ.cy;
                Paste(RCT.left,RCT.top);
                Render(hWnd);
                return 0L;
            case WM_RBUTTONUP:
                ReleaseCapture();
                //選択されたセルを7番レイヤにセーブ
                SaveSel(&SEL,&SIZ,TRUE);
                return 0L;
        
  10. スクロールバーの操作です。
            case WM_VSCROLL:
                switch (LOWORD(wParam))
                {   case SB_LINEUP:     yw = -1;    break;
                    case SB_LINEDOWN:   yw =  1;    break;
                    case SB_PAGEUP:     yw = -1*w_sv.nPage;             break;
                    case SB_PAGEDOWN:   yw = w_sv.nPage;                break;
                    case SB_THUMBTRACK: yw = HIWORD(wParam)-w_sv.nPos;  break;
                    default:            yw =  0;    break;
                }
                yw= max(-1*w_sv.nPos, min(yw,w_sv.nMax-w_sv.nPos));
                if (yw!=0)
                {   w_sv.nPos += yw;
                    SetScrollInfo(hWnd,SB_VERT,&w_sv,TRUE);
                    w_sp.y= w_sv.nPos;
                    InvalidateRect(hWnd,NULL,TRUE);
                    UpdateWindow(hWnd);
                }
                break;
            case WM_HSCROLL:
                switch (LOWORD(wParam))
                {   case SB_LINEUP:     xw = -1;    break;
                    case SB_LINEDOWN:   xw =  1;    break;
                    case SB_PAGEUP:     xw = -1*w_sh.nPage;             break;
                    case SB_PAGEDOWN:   xw = w_sh.nPage;                break;
                    case SB_THUMBTRACK: xw = HIWORD(wParam)-w_sh.nPos;  break;
                    default:            xw =  0;    break;
                }
                xw= max(-1*w_sh.nPos, min(xw,w_sh.nMax-w_sh.nPos));
                if (xw!=0)
                {   w_sh.nPos += xw;
                    SetScrollInfo(hWnd,SB_HORZ,&w_sh,TRUE);
                    w_sp.x= w_sh.nPos;
                    InvalidateRect(hWnd,NULL,TRUE);
                    UpdateWindow(hWnd);
                }
                break;
        
  11. Mapchip の CALLBACK 関数も MainWindow の CALLBACK と同じ要領で作成します。
  12. 初期化を行う App_Init() 関数です。
    MAPEDIT Class を生成して領域を初期設定します。
    レイヤ制御の DialogBox 生成します。
    スクロールバーを設定します。
        void  App_Init(HWND hwnd)
        {   Map= new MAPEDIT(hwnd);
            SEL.left=SEL.top= 0;
            SEL.right=SEL.bottom= 1;
            SIZ.cx=SIZ.cy= 1;
            hBrush= CreateHatchBrush(HS_CROSS,RGB(230,230,230));
            hPen= CreatePen(PS_SOLID,2,RGB(255,0,0));
            g_hDlg= CreateDialog(g_hInst,MAKEINTRESOURCE(IDD_DIALOG2),hwnd,(DLGPROC)DlgLayer);
            // 縦スクロールの設定
            w_sv.cbSize = sizeof(SCROLLINFO);
            w_sv.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
            w_sv.nMin = 0;
            w_sv.nMax = 480;
            w_sv.nPage = 16;
            w_sv.nPos = 0;
            // 横スクロールの設定
            w_sh= w_sv;
            SetScrollInfo(hwnd, SB_VERT, &w_sv, TRUE);
            SetScrollInfo(hwnd, SB_HORZ, &w_sh, TRUE);
        }
        
  13. WM_PAINT: から呼ばれる Render() 関数です。
    BackBuffer を hBrush でクリアして、可視設定されているレイヤを描画します。
    複数のセルが選択されているときは、RCT の矩形範囲を赤色の線で表示します。
    Blt() 関数で BackBuffer から FrontBuffer に一挙に転送します。
        VOID  Render(HWND hwnd)
        {   int     i;
            RECT    rc;
            Map->Clear(hwnd,hBrush);
            Map->Line(Map->hBackDC,Map->Xnum-w_sp.x,Map->Ynum-w_sp.y);
            //可視設定されている MAP を描画
            for(i=0; i<8; i++)
                if (H.Buton[i]) Map->View(Map->hBackDC,TBL[i],w_sp.x,w_sp.y);
            if (SIZ.cx>1)
            {   rc= RCT;
                rc.left-= w_sp.x;
                rc.right-= w_sp.x;
                rc.top-= w_sp.y;
                rc.bottom-= w_sp.y;
                Map->BoxLine(Map->hBackDC,&rc,hPen);
            }
            Map->Blt(hwnd);
        }
        
  14. 選択されたセル(矩形範囲の領域)を7番レイヤに保存する SaveSel() 関数です。
        void  SaveSel(RECT *sel,SIZE *siz,BOOL sw)
        {   int     xp,yp,zp,dt;
    
            if (sel->right<sel->left)
            {   xp= sel->left; sel->left= sel->right; sel->right= xp;
                yp= sel->top;  sel->top= sel->bottom; sel->bottom= yp;
            }
            siz->cx= sel->right-sel->left;
            siz->cy= sel->bottom-sel->top;
            if (siz->cx<1)          siz->cx= 1;
            if (siz->cy<1)          siz->cy= 1;
            if (siz->cx>Map->SnumX) siz->cx= Map->SnumX;
            if (siz->cy>Map->SnumY) siz->cy= Map->SnumY;
            sel->right= sel->left+siz->cx;
            sel->bottom= sel->top+siz->cy;
            if (sw==FALSE)
            {   for(yp=0; yp<siz->cy; yp++)
                    for(xp=0; xp<siz->cx; xp++)
                        TBL[7][yp][xp]= (sel->top+yp)*Map->SnumX+sel->left+xp;
            }
            else
            {   for(yp=0; yp<siz->cy; yp++)
                    for(xp=0; xp<siz->cx; xp++)
                    {   dt= 0;
                        for(zp=6; zp>=0; zp--)
                        {   if (H.Buton[zp]==0)  continue;   //見えていないレイヤ
                            if (TBL[zp][sel->top+yp][sel->left+xp])
                            {   dt= TBL[zp][sel->top+yp][sel->left+xp];
                                break;
                            }
                        }
                        TBL[7][yp][xp]= dt;
                    }
            }
            RenderMap(g_hMap);
            Render(g_hWnd);
        }
        
  15. 選択されたセルを作業中のレイヤに貼り付ける Paste() 関数です。
        void  Paste(int xp, int yp)
        {   int     x,y;
    
            for(y=0; y<SIZ.cy; y++)
                for(x=0; x<SIZ.cx; x++)
                    TBL[LAY][yp+y][xp+x]= TBL[7][y][x];
        }
        
  16. システムを保存する SaveFile() 関数です。
    HEAD 構造体に保存情報を収集してファイルに書き出します。
    続いて現在使われている全レイヤのデータを書き出します。
        VOID  SaveFile()
        {   int     z,y;
            DWORD   len;
            BOOL    rc;
    
            strcpy(H.id,"Mapedit");
            strcpy(H.ver,"Ver 1.0");
            strcpy(H.file,Map->szFile);
            H.Xnum= Map->Xnum;
            H.Ynum= Map->Ynum;
            H.SWidth= Map->SWidth;
            H.SHeight= Map->SHeight;
            H.SnumX= Map->SnumY;
            H.SnumY= Map->SnumY;
            rc= Map->OpenWriteFile(szFile,"map(*.map)\0*.map\0All files(*.*)\0*.*\0\0","map");
            if (rc==FALSE)  return;
            hFile= CreateFile(szFile,GENERIC_WRITE,0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
            if (hFile==INVALID_HANDLE_VALUE)
            {   MessageBox(NULL,szFile,"File Open Error", MB_OK);
                return;
            }
            WriteFile(hFile,H.id,sizeof(HEAD),&dwBytes,NULL);
            len= sizeof(int)*H.Xnum;
            for(z=0; z<8; z++)
            {   if (H.Buton[z+16])  //保存するレイヤ
                {   for(y=0; y<H.Ynum; y++)
                        WriteFile(hFile,&TBL[z][y][0],len,&dwBytes,NULL);
                }
            }
            CloseHandle(hFile);
        }
        
  17. 保存したファイルを入力する OpenFile() 関数です。
    HEAD 構造体にヘッダ情報を入力して、Object に設定します。
    続いて保存されている全レイヤのデータを格納します。
        VOID  OpenFile()
        {   int     z,y;
            DWORD   len;
            BOOL    rc;
    
            rc= Map->OpenReadFile(szFile,"map(*.map)\0*.map\0All files(*.*)\0*.*\0\0","map");
            if (rc==FALSE)  return;
            hFile= CreateFile(szFile,GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
            if (hFile==INVALID_HANDLE_VALUE)
            {   MessageBox(NULL,szFile,"File Open Error", MB_OK);
                return;
            }
            SetFilePointer(hFile,0,0,FILE_BEGIN);
            ReadFile(hFile,H.id,sizeof(HEAD),&dwBytes,NULL);
            if (strcmp(H.id,"Mapedit")!=0)
            {   MessageBox(NULL,szFile,"File Format Error", MB_OK);
                return;
            }
            len= sizeof(int)*H.Xnum;
            for(z=0; z<8; z++)
            {   if (H.Buton[z+16])  //入力するレイヤ
                {   for(y=0; y<H.Ynum; y++)
                        ReadFile(hFile,&TBL[z][y][0],len,&dwBytes,NULL);
                }
            }
            CloseHandle(hFile);
    
            Map->Xnum= H.Xnum;
            Map->Ynum= H.Ynum;
            Map->SWidth= H.SWidth;
            Map->SHeight= H.SHeight;
            Map->LoadFile(H.file);
            ButtonSet(g_hDlg);
            InvalidateRect(g_hWnd,NULL,TRUE);
            InvalidateRect(g_hMap,NULL,TRUE);
            UpdateWindow(g_hWnd);
            UpdateWindow(g_hMap);
        }
        
  18. MAP データを CSV ファイルに書き出す SaveCSV() 関数です。
    ファイルの先頭に Mapchip 画像の名前やサイズ情報を記録してゲームプログラムで扱いやすいようにします。
    保存されるデータは「現在見えている」一番上のレイヤの画像です。
        void  SaveCSV()
        {   int     x,y,z,v;
            char    w[12];
            BOOL    rc;
    
            rc= Map->OpenWriteFile(szFile,"csv(*.csv)\0*.csv\0All files(*.*)\0*.*\0\0","csv");
            if (rc==FALSE)  return;
            hFile= CreateFile(szFile,GENERIC_WRITE,0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
            if (hFile==INVALID_HANDLE_VALUE)
            {   MessageBox(NULL,szFile,"File Open Error", MB_OK);
                return;
            }
            wsprintf(buf,"//Map  Ver1.0  前田 稔\r\n//%s\r\n",Map->szFile);
            WriteFile(hFile,buf,strlen(buf),&dwBytes,NULL);
            wsprintf(buf,"//%4d, %4d, %4d, %4d,  Map の幅と高さ, Mapchip の幅と高さ\r\n",
                     Map->Xnum,Map->Ynum,Map->SWidth,Map->SHeight);
            WriteFile(hFile,buf,strlen(buf),&dwBytes,NULL);
            for(y=0; y<Map->Ynum; y++)
            {   buf[0]= '\0';
                for(x=0; x<Map->Xnum; x++)
                {   v= 0;
                    for(z=7; z>=0; z--)
                    {   if (H.Buton[z]==0)  continue;   //見えていないレイヤ
                        if (TBL[z][y][x])
                        {   v= TBL[z][y][x];
                            break;
                        }
                    }
                    wsprintf(w,"%d, ",v);
                    strcat(buf,w);
                }
                strcat(buf,"\r\n");
                WriteFile(hFile,buf,strlen(buf),&dwBytes,NULL);
            }
            CloseHandle(hFile);
        }
        

[Previous Chapter ↑] Map Editor の Object Class

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