怎样做小游戏挖金子(VC,源码2)

先别急着看源码,首先要解决的问题是程序框架。

VC只是一个开发环境,支持的是C++。Windows下的程序,当然是用Windows API(Application Programming Interface)写成的。 VC提供了一个类库MFC,如同课本中介绍的一样,“点几下鼠标即可生成一个程序”。控件靠拖动即可生成。很方便,但也是最大的缺陷。隐藏了太多东西。我的电脑中VC的一个链接库有点问题,无法运行MFC程序,API是可以运行的。我用API来做。

既然是API,默认生成的结构是:主函数WinMain,注册类MyRegisterClass,初始化InitInstance,消息处理WndProc。

在InitInstance中处理变量的初始设置。

在WndProc中实现游戏的流程控制。

以前做《俄罗斯方块》,包括博客中贴的《扫雷》,都是用一个变量表示当前的状态。这是一种状态机的转换,很简单,一个状态迁移到另一个状态。不同状态下,有不同的消息处理。我用全局变量iGameState来记录。

 

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, 

LPARAM lParam)
{
    
switch  (message) 
    {
    
case  消息ID:
        
switch (当前状态)
        {
        
case  游戏开始菜单:
            
break ;
        
case  游戏进行中:
            
break ;
        }
        
break ;
    
case  消息ID:
        
break ;
    }
}

这个游戏用了多少种状态?

#define GAME_STATE_MENU 0
#define GAME_STATE_MAIN 1

#define GAME_STATE_END 2
#define GAME_STATE_END_NEXT 3

先看游戏的第一个状态GAME_STATE_MENU:用户点击“开始”,进入游戏。

很简单,建一个按钮,处理它的单击消息,修改当前游戏状态。

创建按钮可以用CreateWindow,我没这样做。

画一个图片(我所有的图片资源都是BMP位图),包含两幅图像,一个是按钮正常情况下显示,一个是鼠标移动到上面后显示,并列排放。

怎样显示图片呢?每个图片都由一个位图句柄表示。先把图片文件加载到工程资源resources中,在加载到一个位图句柄。屏幕作为一个显示设备,需要获取他的Device Contexts的句柄。MFC中的CDC就是对设备句柄HDC的封装。Device Contexts,有时候翻译成“设备上下文”,中英文的意思还是有差别,直接叫DC。显示的时候,先画到一个临时的DC,再画到屏幕上。

这个初始化、显示的功能,放到一个类中。这样的小游戏,如果不用类来组织,也能实现功能,但是有大缺点,代码不易维护。一个基本的思想就是:尽量模块化。

void MYBITMAP::Init(HINSTANCE hInstance,int iResource,int row,int col)
{
 BITMAP bm;
 inum=row;
 jnum=col;

 hBm=LoadBitmap(hInstance,MAKEINTRESOURCE(iResource));
 GetObject(hBm,sizeof(BITMAP),&bm);

 width=bm.bmWidth/inum;
 height=bm.bmHeight/jnum;
}

简单说明一下:hInstance程序实例句柄,iResource位图资源ID,row,col,把图像分割成几行几列。函数记录了这张图像的图像句柄,有几行几列,分割后单幅图的宽高。

这是图像的信息记录完成,以下要设置“屏幕DC”,“临时DC”。

void MYBITMAP::SetDC(HDC hdest,HDC hsrc)
{
 hdcdest=hdest;
 hdcsrc=hsrc;
}

从临时DC显示到屏幕上,参数为坐标、第几幅图像:

void MYBITMAP::Show(int x,int y,int iFrame)
{
 xpos=x;
 ypos=y;
 SelectObject(hdcsrc,hBm);
 BitBlt(hdcdest,xpos,ypos,width,height,hdcsrc,iFrame*width,0,SRCCOPY); 
}

说明:显示的时候记录了它的坐标,SelectObject将句柄装入临时DC,再BitBlt到屏幕上。

图像的显示问题解决了。下面要判断鼠标坐标,当鼠标移到其上时,改变图像,下面这两个函数入参为鼠标坐标,判断是否在图片范围内,设置framenow。然后,重绘屏幕,根据framenow显示指定图像即可。

int MYBITMAP::MouseOut(int x,int y)
{
 if(x<xpos || x>(xpos+width) || y<ypos || y>(ypos+height))
  framenow=0;

 return 1;
}
int MYBITMAP::MouseOver(int x,int y)
{
 if(x<xpos || x>(xpos+width) || y<ypos || y>(ypos+height))
  return 0;

 framenow=1;
 return 1;
}

看一下这个状态下的消息处理:重绘消息,单击消息,鼠标移动消息

重绘消息:画一个背景,画一个“开始”按钮。

         case  WM_PAINT:
            hdcwindow 
=  BeginPaint(hWnd,  & ps);
            
switch (iGameState)
            {
            
case  GAME_STATE_MENU:
                SelectObject(hdcscreen,hback);
                bBack.ShowCenter(
0 );
                bBegin.ShowCenter(
200 );
                
break ;

单击消息:如果鼠标单击了开始按钮,迁移状态。

         case  WM_LBUTTONUP:
            
switch (iGameState)
            {
            
case  GAME_STATE_MENU:
                
if (bBegin.DetectMouseUp(LOWORD(lParam),HIWORD(lParam)))
                {
                    iGameState
= GAME_STATE_MAIN;
                    
// 加载地图
                    gmap.LoadFirstMap();
                    gamecatch.Init();
                    myclock.Begin(hWnd,
100 ,TIME_MATCH);
                    
// 清除当前金钱数量
                    gmap.ClearMoney();
                }
                
break ;

鼠标移动消息:检测鼠标坐标,改变图片当前帧(第几幅):

         case  WM_MOUSEMOVE:
            
switch (iGameState)
            {
            
case  GAME_STATE_MENU:
                bBegin.DetectMouseMove(LOWORD(lParam),HIWORD(lParam));
                
break ;

这样,游戏的第一个状态就完成了。且听下回分解。

附:游戏主文件源码

 

//  gamegold.cpp : Defines the entry point for the application.
//

#include 
" stdafx.h "
#include 
" resource.h "

#include 
" bitmaptool.h "
#include 
" filereport.h "
#include 
" gamemap.h "
#include 
" catch.h "
#include 
" myclock.h "
#include 
" mybutton.h "
#include 
" myequip.h "

#define  MAX_LOADSTRING 100


#define  GAME_STATE_MAIN    1
#define  GAME_STATE_MENU    0
#define  GAME_STATE_END    2
#define  GAME_STATE_END_NEXT    3

// FILEREPORT f1("main.txt");

//  Global Variables:
HINSTANCE hInst;                                 //  current instance
TCHAR szTitle[MAX_LOADSTRING];                                 //  The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];                                 //  The title bar text

MYBITMAP bBegin(GAMEWIDTH,GAMEHEIGHT);
MYBITMAP bMap(GAMEWIDTH,GAMEHEIGHT);
MYBITMAP bEarth(GAMEWIDTH,GAMEHEIGHT);
MYBITMAP bObject(GAMEWIDTH,GAMEHEIGHT);
MYBITMAP bBack(GAMEWIDTH,GAMEHEIGHT);
MYBITMAP bFire(GAMEWIDTH,GAMEHEIGHT);
MYBITMAP bBombAni(GAMEWIDTH,GAMEHEIGHT);

MYEQUIP bEquip;

struct  EQUIP
{
    
int  iPrice;
    
char  info[ 100 ];
};
struct  EQUIP equip[] = {
    {
100 , " 炮:能炸掉抓到的物品 100元 " },
    {
200 , " 体力:加快拉动物品的速度 200元 " }};

MYBUTTON btNext;

HDC hdcwindow,hdcscreen,hdcmem;
HBITMAP hback;

HFONT hf[
5 ];

// 游戏状态
int  iGameState = GAME_STATE_MENU;


GAMEMAP gmap;
GAMECATCH gamecatch;
MYCLOCK myclock;

//
void  InitFont()
{
    
char  temp[ 10 ] = { 0 };
    
int  i;

    
for (i = 0 ;i <= sizeof (hf) / sizeof (HFONT);i ++ )
    {
        sprintf(temp,
" black%d " ,i);
        hf[i]
= CreateFont( 10 * (i + 2 ), 0 , 0 , 0 ,FW_HEAVY, 0 , 0 , 0 ,GB2312_CHARSET,OUT_DEFAULT_PRECIS,
                CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH
| FF_DONTCARE,temp);
    }
}

//  Foward declarations of functions included in this code module:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, 
int );
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int  APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     
int        nCmdShow)
{
     
//  TODO: Place code here.
    MSG msg;
    HACCEL hAccelTable;

    
//  Initialize global strings
    LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadString(hInstance, IDC_GAMEGOLD, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    
//  Perform application initialization:
     if  ( ! InitInstance (hInstance, nCmdShow)) 
    {
        
return  FALSE;
    }

    hAccelTable 
=  LoadAccelerators(hInstance, (LPCTSTR)IDC_GAMEGOLD);

    
//  Main message loop:
     while  (GetMessage( & msg, NULL,  0 0 )) 
    {
        
if  ( ! TranslateAccelerator(msg.hwnd, hAccelTable,  & msg)) 
        {
            TranslateMessage(
& msg);
            DispatchMessage(
& msg);
        }
    }

    
return  msg.wParam;
}



//
//   FUNCTION: MyRegisterClass()
//
//   PURPOSE: Registers the window class.
//
//   COMMENTS:
//
//     This function and its usage is only necessary if you want this code
//     to be compatible with Win32 systems prior to the 'RegisterClassEx'
//     function that was added to Windows 95. It is important to call this function
//     so that the application will get 'well formed' small icons associated
//     with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;

    wcex.cbSize 
=   sizeof (WNDCLASSEX); 

    wcex.style            
=  CS_HREDRAW  |  CS_VREDRAW;
    wcex.lpfnWndProc    
=  (WNDPROC)WndProc;
    wcex.cbClsExtra        
=   0 ;
    wcex.cbWndExtra        
=   0 ;
    wcex.hInstance        
=  hInstance;
    wcex.hIcon            
=  LoadIcon(hInstance, (LPCTSTR)IDI_GAMEGOLD);
    wcex.hCursor        
=  LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground    
=  (HBRUSH)(COLOR_WINDOW + 1 );
//     wcex.lpszMenuName    = (LPCSTR)IDC_GAMEGOLD;
    wcex.lpszMenuName     =  NULL;
    wcex.lpszClassName    
=  szWindowClass;
    wcex.hIconSm        
=  LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);

    
return  RegisterClassEx( & wcex);
}

//
//    FUNCTION: InitInstance(HANDLE, int)
//
//    PURPOSE: Saves instance handle and creates main window
//
//    COMMENTS:
//
//         In this function, we save the instance handle in a global variable and
//         create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance,  int  nCmdShow)
{
    HWND hWnd;
    
int  x,y;
    
    hInst 
=  hInstance;  //  Store instance handle in our global variable
    
    hWnd 
=  CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 
0 , CW_USEDEFAULT,  0 , NULL, NULL, hInstance, NULL);
    
    
if  ( ! hWnd)
    {
        
return  FALSE;
    }
    
    
// 窗口居中
    x = GetSystemMetrics(SM_CXSCREEN);
    y
= GetSystemMetrics(SM_CYSCREEN);
    MoveWindow(hWnd,(x
- GAMEWIDTH) / 2 ,(y - GAMEHEIGHT) / 2 ,GAMEWIDTH,GAMEHEIGHT, false );
    
    hdcwindow
= GetDC(hWnd);
    hdcmem
= CreateCompatibleDC(hdcwindow);
    hdcscreen
= CreateCompatibleDC(hdcwindow);
    
    gmap.hdc
= hdcscreen;
    
    myclock.SetDC(hdcscreen);
    
    ReleaseDC(hWnd,hdcwindow);
    
    
// 设置字体
    InitFont();
    gmap.SetFont(hf,
sizeof (hf));

    
// 加载图像
    hback = LoadBitmap(hInstance,MAKEINTRESOURCE(IDB_BITMAP_NULL));

    bBack.Init(hInst,IDB_BITMAP_NULL,
1 , 1 );
    bBack.SetDC(hdcscreen,hdcmem);
    bBegin.Init(hInst,IDB_BITMAP_BEGIN,
2 , 1 );
    bBegin.SetDC(hdcscreen,hdcmem);
    bMap.Init(hInst,IDB_BITMAP_GROUND,
1 , 1 );
    bMap.SetDC(hdcscreen,hdcmem);
    bEarth.Init(hInst,IDB_BITMAP_EARTH,
3 , 1 );
    bEarth.SetDC(hdcscreen,hdcmem);
    bObject.Init(hInst,IDB_BITMAP_OBJECT,
5 , 1 );
    bObject.SetDC(hdcscreen,hdcmem);
    bEquip.Init(hInst,IDB_BITMAP_EQUIP,
3 , 1 );
    bEquip.SetDC(hdcscreen,hdcmem);
    bFire.Init(hInst,IDB_BITMAP_FIRE,
1 , 1 );
    bFire.SetDC(hdcscreen,hdcmem);
    bBombAni.Init(hInst,IDB_BITMAP_BOMB,
4 , 1 );
    bBombAni.SetDC(hdcscreen,hdcmem);
    
    
// 按钮
    btNext.Init( " 下一关 " ,RGB( 0 , 0 , 255 ),RGB( 0 , 128 , 255 ),
        RGB(
255 , 255 , 0 ),RGB( 255 , 255 , 128 ),hf[ 3 ]);
    btNext.SetDC(hdcscreen,hdcmem);
    
    
// 得到总关数
    gmap.GetMatchNum();
    
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
        
    
return  TRUE;
}

//
//   FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//
//   PURPOSE:  Processes messages for the main window.
//
//   WM_COMMAND    - process the application menu
//   WM_PAINT    - Paint the main window
//   WM_DESTROY    - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    
int  wmId, wmEvent;
    
int  i;

    PAINTSTRUCT ps;

    TCHAR szHello[MAX_LOADSTRING];
    LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);

    
switch  (message) 
    {
        
case  WM_COMMAND:
            wmId    
=  LOWORD(wParam); 
            wmEvent 
=  HIWORD(wParam); 
            
//  Parse the menu selections:
             switch  (wmId)
            {
                
case  IDM_ABOUT:
                   DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
                   
break ;
                
case  IDM_EXIT:
                   DestroyWindow(hWnd);
                   
break ;
                
default :
                   
return  DefWindowProc(hWnd, message, wParam, lParam);
            }
            
break ;
        
case  WM_PAINT:
            hdcwindow 
=  BeginPaint(hWnd,  & ps);
            
switch (iGameState)
            {
            
case  GAME_STATE_MENU:
                SelectObject(hdcscreen,hback);
                bBack.ShowCenter(
0 );
                bBegin.ShowCenter(
200 );
                
break ;
            
case  GAME_STATE_MAIN:
                bMap.ShowCenter(
0 );
                bEarth.ShowLoop(
0 , 80 ,GAMEWIDTH,GAMEHEIGHT,gmap.GetMatchNow());
                
                
for (i = 0 ;i < gmap.iObjectNum;i ++ )
                {
                    
if (gmap.objectarray[i].id <= 0 )
                    {
                        
continue ;
                    }
                    bObject.ShowNoBack(gmap.objectarray[i].x,
                                gmap.objectarray[i].y,
                                gmap.objectarray[i].id
- 1 );
                
                }
                gamecatch.DrawAngle(hdcscreen);
                gmap.ShowMoneyNow();
                bFire.ShowNoBackLoop(
389 , 46 , 0 ,gmap.iFireNum);
                bBombAni.ShowAni();
                myclock.Show();
                
break ;
            
case  GAME_STATE_END:
                gmap.GameEnd();
                
break ;
            
case  GAME_STATE_END_NEXT:
                gmap.GameNext();
                bEquip.ShowFramesNoBack(
80 , 150 , 2 );    
                bEquip.ShowFrameLast();
                
if (bEquip.gridnow >= 0   &&  bEquip.gridnow <= 2 )
                {
                    TextOut(hdcscreen,
80 , 220 ,equip[bEquip.gridnow].info,
                                strlen(equip[bEquip.gridnow].info));
                }
                btNext.Show(
350 , 50 );
                
break ;
            }
               BitBlt(hdcwindow,
0 , 0 ,GAMEWIDTH,GAMEHEIGHT,hdcscreen, 0 , 0 ,SRCCOPY);
            EndPaint(hWnd, 
& ps);
            
break ;
        
case  WM_MOUSEMOVE:
            
switch (iGameState)
            {
            
case  GAME_STATE_MENU:
                bBegin.DetectMouseMove(LOWORD(lParam),HIWORD(lParam));
                
break ;
             
case  GAME_STATE_END_NEXT:
                btNext.DetectMouseMove(LOWORD(lParam),HIWORD(lParam));
                bEquip.DetectMouseOverGrid(LOWORD(lParam),HIWORD(lParam));
                
break ;
            }
            InvalidateRect(hWnd,NULL,
false );
            
break ;
        
case  WM_LBUTTONUP:
            
switch (iGameState)
            {
            
case  GAME_STATE_MENU:
                
if (bBegin.DetectMouseUp(LOWORD(lParam),HIWORD(lParam)))
                {
                    iGameState
= GAME_STATE_MAIN;
                    
// 加载地图
                    gmap.LoadFirstMap();
                    gamecatch.Init();
                    myclock.Begin(hWnd,
100 ,TIME_MATCH);
                    
// 清除当前金钱数量
                    gmap.ClearMoney();
                }
                
break ;
             
case  GAME_STATE_END_NEXT:
                
if (btNext.DetectMouseUp(LOWORD(lParam),HIWORD(lParam)))
                {
                    iGameState
= GAME_STATE_MAIN;
                    gmap.LoadMap();
                    gamecatch.Init();
                    myclock.Begin(hWnd,
100 ,TIME_MATCH);                    
                }
                
if (bEquip.DetectMouseOverGrid(LOWORD(lParam),HIWORD(lParam)))
                {
                    bEquip.UseEquip();
                    
// 使用了道具
                    gmap.UseEquip(equip[bEquip.gridnow].iPrice,bEquip.gridnow,gamecatch);
                }
                
break ;
            }                    
            InvalidateRect(hWnd,NULL,
false );
            
break ;
        
case  WM_KEYDOWN:
            
switch (iGameState)
            {
            
case  GAME_STATE_MAIN:
                gamecatch.KeyProc(wParam);
                gmap.KeyProc(wParam);
                InvalidateRect(hWnd,NULL,
false );
                
break ;            
            }
            
break ;
        
case  WM_TIMER:
            
switch (iGameState)
            {
            
case  GAME_STATE_MAIN:
                
if (myclock.Dec())
                {
                    
// 时间到
                    iGameState = GAME_STATE_END;
                    myclock.Begin(hWnd,
100 , 3 );
                    InvalidateRect(hWnd,NULL,
false );
                    
break ;
                }
                gamecatch.TurnAngle();
                
if ( 1 == gamecatch.iState)
                {
                    
// 在叉子伸出的过程中检测是否抓到物品
                     if (gmap.DetectCatch(gamecatch.xpos,gamecatch.ypos,gamecatch.iAngle))
                    {
                        
// 如果抓到物品
                        gamecatch.SetState( 2 ); // 开始收回
                        gmap.SetSpeedBack(gamecatch);                    
                    }
                }
                
// 移动物品
                gmap.MoveObject(gamecatch.xpos,gamecatch.ypos,gamecatch.iAngle);
                InvalidateRect(hWnd,NULL,
false );
                
break ;
            
case  GAME_STATE_END:
                
if (myclock.Dec())
                {
                    
// 动画结束
                    gmap.GameEndAniOver();
                    
// 点击进入下一关
                     if (gmap.CanNextMatch())
                    {
                        bEquip.ClearUseFlag();
                        iGameState
= GAME_STATE_END_NEXT;
                    }
                    
else
                    {
                        iGameState
= GAME_STATE_MENU;
                    }
                }
                InvalidateRect(hWnd,NULL,
false );
                
break ;
            }
            
break ;
        
case  WM_DESTROY:
            DeleteDC(hdcmem);
            DeleteDC(hdcscreen);
            PostQuitMessage(
0 );
            
break ;
        
default :
            
return  DefWindowProc(hWnd, message, wParam, lParam);
   }
   
return   0 ;
}

//  Mesage handler for about box.
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    
switch  (message)
    {
        
case  WM_INITDIALOG:
                
return  TRUE;

        
case  WM_COMMAND:
            
if  (LOWORD(wParam)  ==  IDOK  ||  LOWORD(wParam)  ==  IDCANCEL) 
            {
                EndDialog(hDlg, LOWORD(wParam));
                
return  TRUE;
            }
            
break ;
    }
    
return  FALSE;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值