您的位置:首页 > 编程语言 > C语言/C++

Visual C++游戏编程基础之行为型AI游戏

2019-06-16 18:51 1706 查看

一、设计目标

1.建立一个回合制游戏,角色设定怪物与玩家,并在下方显示它们的生命值;

2.在背景图上贴上攻击标志,鼠标点击该标志时,玩家向怪物发动 攻击,并在左下角输出怪物的状态信息;

3.玩家回合结束后,轮到怪物,根据怪物的HP来决定要选择哪种攻击方式或者逃跑或重伤而死;

4.当某一方HP为0时,贴上游戏结束标志;

二、基本思路

1.初始化过程中,将背景图、怪物及其HP、玩家及其HP、攻击标志贴到mdc,最后在贴到hdc;

2.单击左键,WndProc函数处理该消息,获取光标位置判断是否在攻击标志范围内,有就将attack设为true;

3.接着回到MyPaint函数,玩家开始攻击,用f来表示贴图的次数,将攻击图像贴在怪物身上持续6个画面,然后f更新到第10个画

   面时,降低怪物的HP,并把伤害输出到左下角的文本框,并检查怪物的状态;

4.当f更新到第15个画面时,这次是怪物的回合,设定若怪物HP>20,有90%几率发动普通攻击,10%的几率发动魔法攻击,HP

   低于20就有5种情况:普攻、魔攻、全力攻、回血、逃跑,各有20%几率;

5.f更新到26个画面时,根据怪物的选择进行贴图,持续5个画面,当f更新到30时,减少玩家的HP并输出其状态到左下角,或者

   因血量太低逃跑或死亡;

6.一个回合结束,f重新设为0,等待下一回合,直到over=true,触发游戏结束标志;

三、效果

四、代码如下

[code]
#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,slash,magic,recover,game;//skill--攻击命令图;slash--普通攻击图;magic--怪物魔法攻击图;recover--怪物恢复魔法图;game--游戏结束图
HDC		hdc,mdc,bufdc;
HWND	hWnd;
DWORD	tPre,tNow;
int		pNum,f,txtNum;//pNum--玩家跑动图编号;f--每一回合攻击更新的画面数;TxtNum--记录目前所要显示消息的总数
bool	attack,over;//attack记录玩家是否点击攻击命令;over一方生命值为0时,设为true
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*);//****新增要显示的消息到‘text’缓冲区,当显示的消息数目达到显示的上限数目时,删除最先的消息*********************************

void				CheckDie(int hp,bool player);//每次攻击之后进行判断
void				MyPaint1(HDC hdc,LPARAM lParam);

int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR     lpCmdLine,
int       nCmdShow)
{
MSG msg;

MyRegisterClass(hInstance);

if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}

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,100,100,640,520,true);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);

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

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

bg = (HBITMAP)LoadImage(NULL,"bg.bmp",IMAGE_BITMAP,640,480,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,74,60,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 = 30;
monster.lv = 1;
monster.w = 1;

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

MyPaint(hdc);

return TRUE;
}

void MyPaint(HDC hdc)
{
char str[100];
int i,damage;

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

//显示对战消息
for(i=0;i<txtNum;i++)
TextOut(mdc,0,360+i*20,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,74,30,bufdc,0,30,SRCAND);
BitBlt(mdc,500,350,74,30,bufdc,0,0,SRCPAINT);
}
else
{
f++;  //递增变量,每回合开始重设为0;

//玩家按下攻击键时,玩家可看到6个画面(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)
{
damage = rand()%10 + player.lv*player.w;
monster.nHp -= (int)damage;

sprintf(str,"    怪物生命值减少%d HP    ",damage);
MsgInsert(str);//text里的内容是从这里传递过去的

CheckDie(monster.nHp,false);//检查怪物是否还活着;flase表示要判断的对象是monster
}
}

srand(tPre);//产生随机数,若int固定,那么在rand下,它是伪随机数,要产生真正的随机数必须是个时刻变化的量

//回合开始后的第15个画面,判断怪物要采取何种行动
if(f == 15)
{
if(monster.nHp > 20)
{
if(rand()%10 != 1)
monster.kind = 0;//90%的几率进行普通攻击
else
monster.kind = 1;//10%的几率进行魔法攻击
}
else
{
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个画面,对玩家HP减掉普通攻击的部分
if(f == 30)
{
damage = rand()%10 + monster.lv*monster.w;
player.nHp -= (int)damage;

sprintf(str,"    玩家生命值减少 %d HP    ",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个画面,对玩家HP减掉魔法攻击的部分
if(f == 30)
{
damage = rand()%10 + 3*monster.w;
player.nHp -= (int)damage;

sprintf(str,"    玩家的生命值减少%d HP    ",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个画面,对玩家HP减掉全力攻击的部分
if(f == 30)
{
damage = rand()%10 + monster.lv*monster.w*5;
player.nHp -= (int)damage;

sprintf(str,"    玩家的生命值减少 %d HP    ",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(rand()%3 == 1)	//2/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,480,mdc,0,0,SRCCOPY);

tPre = GetTickCount();

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

//****新增要显示的消息到‘text’缓冲区,当显示的消息数目达到显示的上限数目时,删除最先的消息*********************************
void MsgInsert(char* str)
{
if(txtNum < 5)
{
sprintf(text[txtNum],str);
txtNum++;
}
else
{
for(int i=0;i<txtNum;i++)//有5行的话,各行依次往上前进一行,最新的消息放在最后一行;
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)
PostQuitMessage(0);
break;
case WM_LBUTTONDOWN:			//检测攻击范围
if(!attack)
{
x = LOWORD(lParam);
y = HIWORD(lParam);

if(x >= 500 && x <= 574 && y >= 350 && y <= 380)
attack = true;
}
break;
case WM_DESTROY:
DeleteDC(mdc);
DeleteDC(bufdc);
DeleteObject(bg);
DeleteObject(sheep);
DeleteObject(girl);
DeleteObject(skill);
DeleteObject(slash);
DeleteObject(magic);
DeleteObject(recover);
DeleteObject(game);

ReleaseDC(hWnd,hdc);

PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: