(Visual C++)游戏开发笔记十六 讲解一个完整的回合制游戏demo

来源:岁月联盟 编辑:exp 时间:2012-07-02

上一篇:http://www.2cto.com/kf/201204/126625.html

首先,我们来了解这种行为型AI的设计方法。

游戏程序中计算机角色的思考与行为,实际上是对各种不同事件进行分析思考,然后根据不同的情况作出相应的反应。但如何对发生的条件进行判断,并作出相应的反应呢?


对,我们可以利用“if-else”条件句以及“switch-case”语句这类的判断式来完成。

通常情况下,设计此类AI,会涉及到连串的条件判断句,简单数学运算,及一些数据结构的知识。

下面我们就来具体讲解这个demo涉及到的一些知识点:


一、AI怪物攻击与思考方式设计

例如今天我们要展示的这个回合制游戏demo里的AI,就有如下几种行为:


(1)利爪攻击

(2)闪电链攻击

(3)致命一击

(4)使用梅肯斯姆回复生命值

(5)逃跑


那么我们可以根据以上设计的怪物行为,设计以下一段算法,用来模拟怪物对战时的思考与行为的方式


[cpp]
if(monster.nHp > 20)  //生命值大于20  

if(rand()%5!= 1)            
 //进行利爪攻击概率4/5  
else 
       //进行闪电链攻击概率1/5  

else     //生命值小于20  

switch(rand()%5) 

case 0:  //利爪攻击  
break; 
case 1:  //释放闪电链  
break; 
case 2:  //致命一击  
break; 
case 3:  //使用梅肯斯姆回复  ;  
break; 
case 4:  //逃跑  
if(1== rand()%3 )     //逃跑成功几率1/3  
//逃跑成功  
else 
//逃跑失败  
break; 


if(monster.nHp > 20)  //生命值大于20
{
if(rand()%5!= 1)          
 //进行利爪攻击概率4/5
else
       //进行闪电链攻击概率1/5
}
else  //生命值小于20
{
switch(rand()%5)
{
case 0:  //利爪攻击
break;
case 1:  //释放闪电链
break;
case 2:  //致命一击
break;
case 3:  //使用梅肯斯姆回复  ;
break;
case 4:  //逃跑
if(1== rand()%3 )     //逃跑成功几率1/3
//逃跑成功
else
//逃跑失败
break;
}
}

 

这段代码中,利用if-else判断式判断怪物生命值,然后怪物有4/5的几率释放普通的利爪攻击,有1/5的几率释放闪电链魔法攻击,当怪物重伤生命值小于20点时,也有一定的几率逃跑。


以上的利用“if-else”、“switch”语句,使计算机角色进行事件情况判断,然后写出相应的动作实现代码,这就是行为型游戏AI

设计的核心精神。

 

 

 

 

 

 


二,玩家角色攻击方式设计

 


然后我们再来设计一下玩家的攻击技能。

今天放出的这个demo里我给人物设定了两个技能,一个主动的普通攻击技能“无敌斩”,伤害计算公式为damage = rand()%10 + player.lv*player.w(player.lv为角色等级,player.w为攻击系数)。

而被动技能为可以有一定几率打出4倍暴击伤害的“恩赐解脱”,这个技能是Dota里面幻影刺客/的大招(呵呵,浅墨玩dota时可是超级幻刺控~~)。

其实暴击的实现方式很简单,就是用if条件句进行概率的判断(浅墨在这里利用4==rand( )%5来设定暴击概率为20%),如果判断成功就将“倍率x普通攻击”作为damage的值。

(哈哈,浅墨专门找到了Dota里面这两个技能的图标/以及/用到了这个demo里面,具体效果图在下面)

 

 

下面贴出实现人物技能的代码:

 


[cpp]
if (4==rand()%5)                   // 20%几率触发幻影刺客的大招,恩赐解脱,4倍暴击伤害  

damage = 4*(rand()%10 + player.lv*player.w); 
monster.nHp -= (int)damage; 
sprintf(str,"恩赐解脱触发,这下牛逼了,4倍暴击...对怪物照成了%d点伤害",damage); 
}  
else 

damage = rand()%10 + player.lv*player.w; 
monster.nHp -= (int)damage; 
sprintf(str,"玩家使用了无敌斩,伤害一般般...对怪物照成了%d点伤害",damage); 

if (4==rand()%5)                   // 20%几率触发幻影刺客的大招,恩赐解脱,4倍暴击伤害
{
damage = 4*(rand()%10 + player.lv*player.w);
monster.nHp -= (int)damage;
sprintf(str,"恩赐解脱触发,这下牛逼了,4倍暴击...对怪物照成了%d点伤害",damage);
}
else
{
damage = rand()%10 + player.lv*player.w;
monster.nHp -= (int)damage;
sprintf(str,"玩家使用了无敌斩,伤害一般般...对怪物照成了%d点伤害",damage);
}

 

 

 

 

 

三、完整的回合制游戏源代码

 

 

基础部分就讲解完了,下面就贴出注释详细的,完整的回合制游戏demo的代码吧:

 


[cpp] 
#include "stdafx.h"  
#include <stdio.h>  
 
//定义一个结构体  
struct chr 

    int     nHp; 
    int     fHp; 
    int     lv; 
    int     w; 
    int     kind; 
}; 
 
//全局变量声明  
HINSTANCE hInst; 
HBITMAP bg,sheep,girl,skill,skillult,slash,magic,recover,game; 
HDC     hdc,mdc,bufdc; 
HWND    hWnd; 
DWORD   tPre,tNow; 
int     pNum,f,txtNum; 
bool    attack,over; 
chr     player,monster; 
char    text[5][100]; 
 
 
 
 
//全局函数声明  
ATOM                MyRegisterClass(HINSTANCE hInstance); 
BOOL                InitInstance(HINSTANCE, int); 
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM); 
void                MyPaint(HDC hdc); 
void                MsgInsert(char*); 
void                CheckDie(int hp,bool player); 
 
//****WinMain函数,程序入口点函数**************************************  
int APIENTRY WinMain(HINSTANCE hInstance, 
                     HINSTANCE hPrevInstance, 
                     LPSTR     lpCmdLine, 
                     int       nCmdShow) 

    MSG msg; 
 
    MyRegisterClass(hInstance); 
 
    //初始化    
    if (!InitInstance (hInstance, nCmdShow))  
    { 
        return FALSE; 
    } 
 
    //消息循环  
    GetMessage(&msg,NULL,NULL,NULL);            //初始化msg      
    while( msg.message!=WM_QUIT ) 
    { 
        if( PeekMessage( &msg, NULL, 0,0 ,PM_REMOVE) ) 
        { 
            TranslateMessage( &msg ); 
            DispatchMessage( &msg ); 
        } 
        else 
        { 
            tNow = GetTickCount(); 
            if(tNow-tPre >= 40) 
                MyPaint(hdc); 
        } 
    } 
 
    return msg.wParam; 

 
//***设计一个窗口类,类似填空题,使用窗口结构体*************************  
ATOM MyRegisterClass(HINSTANCE hInstance) 

    WNDCLASSEX wcex; 
 
    wcex.cbSize = sizeof(WNDCLASSEX);  
    wcex.style          = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; 
    wcex.lpfnWndProc    = (WNDPROC)WndProc; 
    wcex.cbClsExtra     = 0; 
    wcex.cbWndExtra     = 0; 
    wcex.hInstance      = hInstance; 
    wcex.hIcon          = NULL; 
    wcex.hCursor        = NULL; 
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW); 
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1); 
    wcex.lpszMenuName   = NULL; 
    wcex.lpszClassName  = "canvas"; 
    wcex.hIconSm        = NULL; 
 
    return RegisterClassEx(&wcex); 

 
//****初始化函数************************************  
//加载位图并设定各种初始值   
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) 

    HBITMAP bmp; 
    hInst = hInstance; 
 
    hWnd = CreateWindow("canvas", "浅墨的绘图窗口" , WS_OVERLAPPEDWINDOW, 
        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); 
 
    if (!hWnd) 
    { 
        return FALSE; 
    } 
 
    MoveWindow(hWnd,10,10,640,510,true); 
    ShowWindow(hWnd, nCmdShow); 
    UpdateWindow(hWnd); 
 
    hdc = GetDC(hWnd); 
    mdc = CreateCompatibleDC(hdc); 
    bufdc = CreateCompatibleDC(hdc); 
 
    bmp = CreateCompatibleBitmap(hdc,640,510); 
    SelectObject(mdc,bmp); 
 
    bg = (HBITMAP)LoadImage(NULL,"bg.bmp",IMAGE_BITMAP,640,510,LR_LOADFROMFILE); 
    sheep = (HBITMAP)LoadImage(NULL,"sheep.bmp",IMAGE_BITMAP,133,220,LR_LOADFROMFILE); 
    girl = (HBITMAP)LoadImage(NULL,"girl.bmp",IMAGE_BITMAP,480,148,LR_LOADFROMFILE); 
    skill = (HBITMAP)LoadImage(NULL,"skill.bmp",IMAGE_BITMAP,50,50,LR_LOADFROMFILE); 
    skillult = (HBITMAP)LoadImage(NULL,"skillult.bmp",IMAGE_BITMAP,50,50,LR_LOADFROMFILE); 
    slash = (HBITMAP)LoadImage(NULL,"slash.bmp",IMAGE_BITMAP,196,162,LR_LOADFROMFILE); 
    magic = (HBITMAP)LoadImage(NULL,"magic.bmp",IMAGE_BITMAP,200,100,LR_LOADFROMFILE); 
    recover = (HBITMAP)LoadImage(NULL,"recover.bmp",IMAGE_BITMAP,300,150,LR_LOADFROMFILE); 
    game = (HBITMAP)LoadImage(NULL,"over.bmp",IMAGE_BITMAP,289,74,LR_LOADFROMFILE); 
 
    player.nHp = player.fHp = 50;   //设定玩家角色声明值及上限  
    player.lv = 2;                  //设定玩家角色等级  
    player.w  = 4;                  //设定攻击伤害加权值  
 
    monster.nHp = monster.fHp = 120;    //设定怪物角色生命值及上限  
    monster.lv = 1;                     //设定怪物角色等级  
    monster.w = 1;                      //设定攻击伤害加权值  
 
    txtNum = 0;     //显示消息数目  
 
    SetBkMode(mdc, TRANSPARENT);    //设置TextOut背景透明  
 
     
 
 
    MyPaint(hdc); 
 
    return TRUE; 

 
//****自定义绘图函数*********************************  
// 1.画面贴图与对战消息显示  
// 2.怪物行为判断及各项数据处理与计算  
void MyPaint(HDC hdc) 

    char str[100]; 
    int i,damage; 
 
    //贴上背景图  
    SelectObject(bufdc,bg); 
    BitBlt(mdc,0,0,640,510,bufdc,0,0,SRCCOPY); 
 
    //显示对战消息  
     
     
         
    for(i=0;i<txtNum;i++) 
        TextOut(mdc,0,360+i*18,text[i],strlen(text[i])); 
 
    //贴上怪物图  
    if(monster.nHp>0) 
    { 
        SelectObject(bufdc,sheep); 
        BitBlt(mdc,70,180,133,110,bufdc,0,110,SRCAND); 
        BitBlt(mdc,70,180,133,110,bufdc,0,0,SRCPAINT); 
        sprintf(str,"%d / %d",monster.nHp,monster.fHp); 
        TextOut(mdc,100,320,str,strlen(str)); 
    } 
 
    //贴上玩家图  
    if(player.nHp>0) 
    { 
        SelectObject(bufdc,girl); 
        BitBlt(mdc,500,200,60,74,bufdc,pNum*60,74,SRCAND); 
        BitBlt(mdc,500,200,60,74,bufdc,pNum*60,0,SRCPAINT); 
        sprintf(str,"%d / %d",player.nHp,player.fHp); 
        TextOut(mdc,510,320,str,strlen(str)); 
    } 
 
    if(over)                //贴上游戏结束图画  
    { 
        SelectObject(bufdc,game); 
        BitBlt(mdc,200,200,289,37,bufdc,0,37,SRCAND); 
        BitBlt(mdc,200,200,289,37,bufdc,0,0,SRCPAINT); 
    } 
    else if(!attack)        //贴上攻击命令图画  
    { 
        SelectObject(bufdc,skill); 
        BitBlt(mdc,500,350,50,50,bufdc,0,0,SRCCOPY); 
        SelectObject(bufdc,skillult); 
        BitBlt(mdc,430,350,50,50,bufdc,0,0,SRCCOPY); 
        //BitBlt(mdc,500,350,74,30,bufdc,0,30,SRCAND);  
        //BitBlt(mdc,500,350,74,30,bufdc,0,0,SRCPAINT);  
    }    
    else 
    { 
        f++; 
 
        //第5~10个画面时显示玩家攻击图标  
        if(f>=5 && f<=10) 
        { 
            SelectObject(bufdc,slash); 
             
            BitBlt(mdc,100,160,98,162,bufdc,98,0,SRCAND); 
            BitBlt(mdc,100,160,98,162,bufdc,0,0,SRCPAINT); 
 
            //第10个画面时计算怪物受伤害程度并加入显示消息  
            if(f == 10) 
            { 
 
                if (4==rand()%5)                   // 20%几率触发幻影刺客的大招,恩赐解脱,4倍暴击伤害  
                { 
                    damage = 4*(rand()%10 + player.lv*player.w); 
                    monster.nHp -= (int)damage; 
 
                    sprintf(str,"恩赐解脱触发,这下牛逼了,4倍暴击...对怪物照成了%d点伤害",damage); 
                }  
                else 
                { 
                    damage = rand()%10 + player.lv*player.w; 
                    monster.nHp -= (int)damage; 
 
                    sprintf(str,"玩家使用了无敌斩,伤害一般般...对怪物照成了%d点伤害",damage); 
                } 
 
 
                 
                MsgInsert(str); 
 
                CheckDie(monster.nHp,false); 
            } 
        } 
 
        srand(tPre); 
 
        //第15个画面时判断怪物进行哪项动作  
        if(f == 15) 
        { 
            if(monster.nHp > 20)             //生命值大于20  
            { 
                if(rand()%5 != 1)          //进行利爪攻击概率4/5  
                    monster.kind = 0; 
                else                       //进行闪电链攻击概率1/5  
                    monster.kind = 1;     
            } 
            else                                //生命值小于20  
            { 
                switch(rand()%5) 
                { 
                    case 0:                     //利爪攻击  
                        monster.kind = 0; 
                        break; 
                    case 1:                     //释放闪电链  
                        monster.kind = 1; 
                        break; 
                    case 2:                     //致命一击  
                        monster.kind = 2; 
                        break; 
                    case 3:                     //使用梅肯斯姆回复  
                        monster.kind = 3; 
                        break; 
                    case 4:                     //逃跑  
                        monster.kind = 4; 
                        break; 
                } 
            } 
        } 
 
        //第26~30个画面时显示玩家攻击图标  
        if(f>=26  && f<=30) 
        { 
            switch(monster.kind) 
            { 
                case 0:                         //利爪攻击  
                    SelectObject(bufdc,slash); 
                    BitBlt(mdc,480,150,98,162,bufdc,98,0,SRCAND); 
                    BitBlt(mdc,480,150,98,162,bufdc,0,0,SRCPAINT); 
 
                    //第30个画面时计算玩家受伤害程度并加入显示消息  
                    if(f == 30) 
                    { 
                        damage = rand()%10 + monster.lv*monster.w; 
                        player.nHp -= (int)damage; 
                         
                        sprintf(str,"怪物利爪攻击...对玩家照成 %d 点伤害",damage); 
                        MsgInsert(str); 
 
                        CheckDie(player.nHp,true); 
                    } 
                    break; 
                case 1:                         //释放闪电链  
                    SelectObject(bufdc,magic); 
                    BitBlt(mdc,480,190,100,100,bufdc,100,0,SRCAND); 
                    BitBlt(mdc,480,190,100,100,bufdc,0,0,SRCPAINT); 
 
                    //第30个画面时计算玩家受伤害程度并加入显示消息  
                    if(f == 30) 
                    { 
                        damage = rand()%10 + 3*monster.w; 
                        player.nHp -= (int)damage;   
                         
                        sprintf(str,"怪物释放闪电链...对玩家照成 %d 点伤害",damage); 
                        MsgInsert(str); 
 
                        CheckDie(player.nHp,true); 
                    } 
                    break; 
                case 2:                         //致命一击  
                    SelectObject(bufdc,slash); 
                    BitBlt(mdc,480,150,98,162,bufdc,98,0,SRCAND); 
                    BitBlt(mdc,480,150,98,162,bufdc,0,0,SRCPAINT); 
 
                    //第30个画面时计算玩家受伤害程度并加入显示消息  
                    if(f == 30) 
                    { 
                        damage = rand()%10 + monster.lv*monster.w*5; 
                        player.nHp -= (int)damage; 
                         
                        sprintf(str,"怪物致命一击...对玩家照成 %d 点伤害.",damage); 
                        MsgInsert(str); 
 
                        CheckDie(player.nHp,true); 
                    } 
                    break; 
                case 3:                         //使用梅肯斯姆补血  
                    SelectObject(bufdc,recover); 
                    BitBlt(mdc,60,160,150,150,bufdc,150,0,SRCAND); 
                    BitBlt(mdc,60,160,150,150,bufdc,0,0,SRCPAINT); 
 
                    //第30个画面时怪物回复生命值并加入显示消息  
                    if(f == 30) 
                    { 
                        monster.nHp += 30; 
                         
                        sprintf(str,"怪物使用梅肯斯姆...恢复了30点生命值",damage); 
                        MsgInsert(str); 
                    } 
                    break; 
                case 4: 
                    //在第30个画面时判断怪物是否逃跑成功  
                    if(f == 30) 
                    { 
                        if(1== rand()%3 )   //逃跑几率1/3  
                        { 
                            over = true; 
                            monster.nHp = 0; 
 
                            sprintf(str,"怪物逃跑中...逃跑成功"); 
                            MsgInsert(str); 
                        } 
                        else 
                        { 
                            sprintf(str,"怪物逃跑中...逃跑失败"); 
                            MsgInsert(str); 
                        } 
                    } 
                    break; 
            } 
        } 
 
        if(f == 30)         //回合结束  
        { 
            attack = false; 
            f = 0; 
        } 
    } 
 
    BitBlt(hdc,0,0,640,510,mdc,0,0,SRCCOPY); 
 
    tPre = GetTickCount(); 
 
    pNum++; 
    if(pNum == 8) 
        pNum = 0; 

 
//****新增的对战消息函数********************************  
void MsgInsert(char* str) 

    if(txtNum < 5) 
    { 
        sprintf(text[txtNum],str); 
        txtNum++; 
    } 
    else 
    { 
        for(int i=0;i<txtNum;i++) 
            sprintf(text[i],text[i+1]); 
 
        sprintf(text[4],str); 
    } 

 
//****生命值判断函数*************************  
void CheckDie(int hp,bool player) 

    char str[100]; 
 
    if(hp <= 0) 
    { 
        over = true; 
        if(player) 
        { 
            sprintf(str,"胜败乃兵家常事,大侠请重新来过......"); 
            MsgInsert(str); 
        } 
        else 
        { 
            sprintf(str,"少年,你赢了,有两下子啊~~~~~!!!!"); 
            MsgInsert(str); 
        } 
    } 

 
//****消息处理函数***********************************  
//   
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 

    int x,y; 
 
    switch (message) 
    { 
        case WM_KEYDOWN:                //键盘消息  
            if(wParam==VK_ESCAPE)       //按下Esc键  
                PostQuitMessage(0); 
            break; 
        case WM_LBUTTONDOWN:            //鼠标左键消息  
            if(!attack) 
            { 
                x = LOWORD(lParam);     //X坐标  
                y = HIWORD(lParam);     //Y坐标  
             
                if(x >= 500 && x <= 550 && y >= 350 && y <= 400) 
                    attack = true; 
            } 
            break; 
        case WM_DESTROY:                //窗口结束消息  
            DeleteDC(mdc); 
            DeleteDC(bufdc); 
            DeleteObject(bg); 
            DeleteObject(sheep); 
            DeleteObject(girl); 
            DeleteObject(skill); 
            DeleteObject(skillult); 
            DeleteObject(slash); 
            DeleteObject(magic); 
            DeleteObject(recover); 
            DeleteObject(game); 
 
            ReleaseDC(hWnd,hdc); 
 
            PostQuitMessage(0); 
            break; 
        default:                        //默认消息  
            return DefWindowProc(hWnd, message, wParam, lParam); 
   } 
   return 0; 

#include "stdafx.h"
#include <stdio.h>

//定义一个结构体
struct chr
{
 int  nHp;
 int  fHp;
 int  lv;
 int  w;
 int  kind;
};

//全局变量声明
HINSTANCE hInst;
HBITMAP bg,sheep,girl,skill,skillult,slash,magic,recover,game;
HDC  hdc,mdc,bufdc;
HWND hWnd;
DWORD tPre,tNow;
int  pNum,f,txtNum;
bool attack,over;
chr  player,monster;
char text[5][100];

 


//全局函数声明
ATOM    MyRegisterClass(HINSTANCE hInstance);
BOOL    InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void    MyPaint(HDC hdc);
void    MsgInsert(char*);
void    CheckDie(int hp,bool player);

//****WinMain函数,程序入口点函数**************************************
int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
 MSG msg;

 MyRegisterClass(hInstance);

 //初始化 
 if (!InitInstance (hInstance, nCmdShow))
 {
  return FALSE;
 }

 //消息循环
 GetMessage(&msg,NULL,NULL,NULL);            //初始化msg   
    while( msg.message!=WM_QUIT )
    {
        if( PeekMessage( &msg, NULL, 0,0 ,PM_REMOVE) )
        {
            TranslateMessage( &msg );
            DispatchMessage( &msg );
        }
  else
  {
   tNow = GetTickCount();
   if(tNow-tPre >= 40)
    MyPaint(hdc);
  }
    }

 return msg.wParam;
}

//***设计一个窗口类,类似填空题,使用窗口结构体*************************
ATOM MyRegisterClass(HINSTANCE hInstance)
{
 WNDCLASSEX wcex;

 wcex.cbSize = sizeof(WNDCLASSEX);
 wcex.style   = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
 wcex.lpfnWndProc = (WNDPROC)WndProc;
 wcex.cbClsExtra  = 0;
 wcex.cbWndExtra  = 0;
 wcex.hInstance  = hInstance;
 wcex.hIcon   = NULL;
 wcex.hCursor  = NULL;
 wcex.hCursor  = LoadCursor(NULL, IDC_ARROW);
 wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
 wcex.lpszMenuName = NULL;
 wcex.lpszClassName = "canvas";
 wcex.hIconSm  = NULL;

 return RegisterClassEx(&wcex);
}

//****初始化函数************************************
//加载位图并设定各种初始值
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
 HBITMAP bmp;
 hInst = hInstance;

 hWnd = CreateWindow("canvas", "浅墨的绘图窗口" , WS_OVERLAPPEDWINDOW,
  CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

 if (!hWnd)
 {
  return FALSE;
 }

 MoveWindow(hWnd,10,10,640,510,true);
 ShowWindow(hWnd, nCmdShow);
 UpdateWindow(hWnd);

 hdc = GetDC(hWnd);
 mdc = CreateCompatibleDC(hdc);
 bufdc = CreateCompatibleDC(hdc);

 bmp = CreateCompatibleBitmap(hdc,640,510);
 SelectObject(mdc,bmp);

 bg = (HBITMAP)LoadImage(NULL,"bg.bmp",IMAGE_BITMAP,640,510,LR_LOADFROMFILE);
 sheep = (HBITMAP)LoadImage(NULL,"sheep.bmp",IMAGE_BITMAP,133,220,LR_LOADFROMFILE);
 girl = (HBITMAP)LoadImage(NULL,"girl.bmp",IMAGE_BITMAP,480,148,LR_LOADFROMFILE);
 skill = (HBITMAP)LoadImage(NULL,"skill.bmp",IMAGE_BITMAP,50,50,LR_LOADFROMFILE);
 skillult = (HBITMAP)LoadImage(NULL,"skillult.bmp",IMAGE_BITMAP,50,50,LR_LOADFROMFILE);
 slash = (HBITMAP)LoadImage(NULL,"slash.bmp",IMAGE_BITMAP,196,162,LR_LOADFROMFILE);
 magic = (HBITMAP)LoadImage(NULL,"magic.bmp",IMAGE_BITMAP,200,100,LR_LOADFROMFILE);
 recover = (HBITMAP)LoadImage(NULL,"recover.bmp",IMAGE_BITMAP,300,150,LR_LOADFROMFILE);
 game = (HBITMAP)LoadImage(NULL,"over.bmp",IMAGE_BITMAP,289,74,LR_LOADFROMFILE);

 player.nHp = player.fHp = 50; //设定玩家角色声明值及上限
 player.lv = 2;     //设定玩家角色等级
 player.w  = 4;     //设定攻击伤害加权值

 monster.nHp = monster.fHp = 120; //设定怪物角色生命值及上限
 monster.lv = 1;      //设定怪物角色等级
 monster.w = 1;      //设定攻击伤害加权值

 txtNum = 0;  //显示消息数目

 SetBkMode(mdc, TRANSPARENT);    //设置TextOut背景透明

 


 MyPaint(hdc);

 return TRUE;
}

//****自定义绘图函数*********************************
// 1.画面贴图与对战消息显示
// 2.怪物行为判断及各项数据处理与计算
void MyPaint(HDC hdc)
{
 char str[100];
 int i,damage;

 //贴上背景图
 SelectObject(bufdc,bg);
 BitBlt(mdc,0,0,640,510,bufdc,0,0,SRCCOPY);

 //显示对战消息
 
 
  
 for(i=0;i<txtNum;i++)
  TextOut(mdc,0,360+i*18,text[i],strlen(text[i]));

 //贴上怪物图
 if(monster.nHp>0)
 {
  SelectObject(bufdc,sheep);
  BitBlt(mdc,70,180,133,110,bufdc,0,110,SRCAND);
  BitBlt(mdc,70,180,133,110,bufdc,0,0,SRCPAINT);
  sprintf(str,"%d / %d",monster.nHp,monster.fHp);
  TextOut(mdc,100,320,str,strlen(str));
 }

 //贴上玩家图
 if(player.nHp>0)
 {
  SelectObject(bufdc,girl);
  BitBlt(mdc,500,200,60,74,bufdc,pNum*60,74,SRCAND);
  BitBlt(mdc,500,200,60,74,bufdc,pNum*60,0,SRCPAINT);
  sprintf(str,"%d / %d",player.nHp,player.fHp);
  TextOut(mdc,510,320,str,strlen(str));
 }

 if(over)    //贴上游戏结束图画
 {
  SelectObject(bufdc,game);
  BitBlt(mdc,200,200,289,37,bufdc,0,37,SRCAND);
  BitBlt(mdc,200,200,289,37,bufdc,0,0,SRCPAINT);
 }
 else if(!attack)  //贴上攻击命令图画
 {
  SelectObject(bufdc,skill);
  BitBlt(mdc,500,350,50,50,bufdc,0,0,SRCCOPY);
  SelectObject(bufdc,skillult);
  BitBlt(mdc,430,350,50,50,bufdc,0,0,SRCCOPY);
  //BitBlt(mdc,500,350,74,30,bufdc,0,30,SRCAND);
  //BitBlt(mdc,500,350,74,30,bufdc,0,0,SRCPAINT);
 } 
 else
 {
  f++;

  //第5~10个画面时显示玩家攻击图标
  if(f>=5 && f<=10)
  {
   SelectObject(bufdc,slash);
   
   BitBlt(mdc,100,160,98,162,bufdc,98,0,SRCAND);
   BitBlt(mdc,100,160,98,162,bufdc,0,0,SRCPAINT);

   //第10个画面时计算怪物受伤害程度并加入显示消息
   if(f == 10)
   {

    if (4==rand()%5)                   // 20%几率触发幻影刺客的大招,恩赐解脱,4倍暴击伤害
    {
     damage = 4*(rand()%10 + player.lv*player.w);
     monster.nHp -= (int)damage;

     sprintf(str,"恩赐解脱触发,这下牛逼了,4倍暴击...对怪物照成了%d点伤害",damage);
    }
    else
    {
     damage = rand()%10 + player.lv*player.w;
     monster.nHp -= (int)damage;

     sprintf(str,"玩家使用了无敌斩,伤害一般般...对怪物照成了%d点伤害",damage);
    }


    
    MsgInsert(str);

    CheckDie(monster.nHp,false);
   }
  }

  srand(tPre);

  //第15个画面时判断怪物进行哪项动作
  if(f == 15)
  {
   if(monster.nHp > 20)    //生命值大于20
   {
    if(rand()%5 != 1)          //进行利爪攻击概率4/5
     monster.kind = 0;
    else                       //进行闪电链攻击概率1/5
     monster.kind = 1;   
   }
   else        //生命值小于20
   {
    switch(rand()%5)
    {
     case 0:      //利爪攻击
      monster.kind = 0;
      break;
     case 1:      //释放闪电链
      monster.kind = 1;
      break;
     case 2:      //致命一击
      monster.kind = 2;
      break;
     case 3:      //使用梅肯斯姆回复
      monster.kind = 3;
      break;
     case 4:      //逃跑
      monster.kind = 4;
      break;
    }
   }
  }

  //第26~30个画面时显示玩家攻击图标
  if(f>=26  && f<=30)
  {
   switch(monster.kind)
   {
    case 0:       //利爪攻击
     SelectObject(bufdc,slash);
     BitBlt(mdc,480,150,98,162,bufdc,98,0,SRCAND);
     BitBlt(mdc,480,150,98,162,bufdc,0,0,SRCPAINT);

     //第30个画面时计算玩家受伤害程度并加入显示消息
     if(f == 30)
     {
      damage = rand()%10 + monster.lv*monster.w;
      player.nHp -= (int)damage;
      
      sprintf(str,"怪物利爪攻击...对玩家照成 %d 点伤害",damage);
      MsgInsert(str);

      CheckDie(player.nHp,true);
     }
     break;
    case 1:       //释放闪电链
     SelectObject(bufdc,magic);
     BitBlt(mdc,480,190,100,100,bufdc,100,0,SRCAND);
     BitBlt(mdc,480,190,100,100,bufdc,0,0,SRCPAINT);

     //第30个画面时计算玩家受伤害程度并加入显示消息
     if(f == 30)
     {
      damage = rand()%10 + 3*monster.w;
      player.nHp -= (int)damage; 
      
      sprintf(str,"怪物释放闪电链...对玩家照成 %d 点伤害",damage);
      MsgInsert(str);

      CheckDie(player.nHp,true);
     }
     break;
    case 2:       //致命一击
     SelectObject(bufdc,slash);
     BitBlt(mdc,480,150,98,162,bufdc,98,0,SRCAND);
     BitBlt(mdc,480,150,98,162,bufdc,0,0,SRCPAINT);

     //第30个画面时计算玩家受伤害程度并加入显示消息
     if(f == 30)
     {
      damage = rand()%10 + monster.lv*monster.w*5;
      player.nHp -= (int)damage;
      
      sprintf(str,"怪物致命一击...对玩家照成 %d 点伤害.",damage);
      MsgInsert(str);

      CheckDie(player.nHp,true);
     }
     break;
    case 3:       //使用梅肯斯姆补血
     SelectObject(bufdc,recover);
     BitBlt(mdc,60,160,150,150,bufdc,150,0,SRCAND);
     BitBlt(mdc,60,160,150,150,bufdc,0,0,SRCPAINT);

     //第30个画面时怪物回复生命值并加入显示消息
     if(f == 30)
     {
      monster.nHp += 30;
      
      sprintf(str,"怪物使用梅肯斯姆...恢复了30点生命值",damage);
      MsgInsert(str);
     }
     break;
    case 4:
     //在第30个画面时判断怪物是否逃跑成功
     if(f == 30)
     {
      if(1== rand()%3 ) //逃跑几率1/3
      {
       over = true;
       monster.nHp = 0;

       sprintf(str,"怪物逃跑中...逃跑成功");
       MsgInsert(str);
      }
      else
      {
       sprintf(str,"怪物逃跑中...逃跑失败");
       MsgInsert(str);
      }
     }
     break;
   }
  }

  if(f == 30)   //回合结束
  {
   attack = false;
   f = 0;
  }
 }

 BitBlt(hdc,0,0,640,510,mdc,0,0,SRCCOPY);

 tPre = GetTickCount();

 pNum++;
 if(pNum == 8)
  pNum = 0;
}

//****新增的对战消息函数********************************
void MsgInsert(char* str)
{
 if(txtNum < 5)
 {
  sprintf(text[txtNum],str);
  txtNum++;
 }
 else
 {
  for(int i=0;i<txtNum;i++)
   sprintf(text[i],text[i+1]);

  sprintf(text[4],str);
 }
}

//****生命值判断函数*************************
void CheckDie(int hp,bool player)
{
 char str[100];

 if(hp <= 0)
 {
  over = true;
  if(player)
  {
   sprintf(str,"胜败乃兵家常事,大侠请重新来过......");
   MsgInsert(str);
  }
  else
  {
   sprintf(str,"少年,你赢了,有两下子啊~~~~~!!!!");
   MsgInsert(str);
  }
 }
}

//****消息处理函数***********************************
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
 int x,y;

 switch (message)
 {
  case WM_KEYDOWN:    //键盘消息
   if(wParam==VK_ESCAPE)  //按下Esc键
    PostQuitMessage(0);
   break;
  case WM_LBUTTONDOWN:   //鼠标左键消息
   if(!attack)
   {
    x = LOWORD(lParam);  //X坐标
    y = HIWORD(lParam);  //Y坐标
   
    if(x >= 500 && x <= 550 && y >= 350 && y <= 400)
     attack = true;
   }
   break;
  case WM_DESTROY:    //窗口结束消息
   DeleteDC(mdc);
   DeleteDC(bufdc);
   DeleteObject(bg);
   DeleteObject(sheep);
   DeleteObject(girl);
   DeleteObject(skill);
   DeleteObject(skillult);
   DeleteObject(slash);
   DeleteObject(magic);
   DeleteObject(recover);
   DeleteObject(game);

   ReleaseDC(hWnd,hdc);

   PostQuitMessage(0);
   break;
  default:      //默认消息
   return DefWindowProc(hWnd, message, wParam, lParam);
   }
   return 0;
}

 


每一回合开始的时候,我们点击画面上“无敌斩”的技能图标,就可以进行攻击,对怪物造成伤害,人品好的话,还可以触发强力被动技能“恩赐解脱”,对怪物造成4倍暴击伤害,这里我们设定的暴击概率为20%

 

浅墨在截图的时候,人品挺好的,恩赐解脱的暴击概率为20%,但是浅墨的4次攻击里,有3次都打出了“恩赐解脱”的暴击效果,直接果断地把这只小绵羊带走了,呵呵。

下面就是游戏运行的截图:


游戏开始/

 

第一刀就出暴击了,48点伤害

/

运气不错,又一刀暴击,68点伤害

 /
 

最后一刀又出了暴击,小绵羊被“秒杀”,游戏结束
/
 

我们还可以调节怪物等级,怪物攻击加权值,怪物血量上限以及玩家角色等级,玩家角色攻击加权值,玩家角色血量上限来让游戏更具挑战性。


当然,我们也可以增加更多的代码,来使怪物的思考与行动方式更具真实性和多样性,来使玩家的技能更加丰富。

 作者:zhmxy555