VC6.0 MFC 单文档 坦克大战游戏 基础入门

坦克大战游戏

一、游戏简述

在顶部随机产生三种不同形状的敌机(用矩形、三角形、圆形表示),每秒产生一个,产生哪一个是随机的(大量用到随机数)。横坐标是随机的,纵坐标开始都是0。敌机大小、速度随机。
下面有一个坦克,坦克的移动通过上下左右键控制,通过按键盘空格键发射子弹。子弹运动靠OnTimer实现,坦克运动靠OnKeyDown实现。
首先我们要定义数组(敌机数组(矩形、三角形、圆形)、子弹数组),然后进行初始化。
大概需要的函数有:Draw(坦克、子弹);Create敌机是1秒产生一个,Create子弹是按空格键产生;Move(敌机、子弹)是0.1秒产生一个(设置两个时钟)。还要有Delete敌机、子弹,碰撞(包括子弹和敌机碰撞、坦克和敌机碰撞)等。

下面请看详细的实现步骤。

二、实现过程(前期)

1、新建一个MFC单文档应用程序,如下图所示。
在这里插入图片描述
在这里插入图片描述
2、新建一个TankWar类,在类里面先定义变量,并在构造函数里进行初始化,如下图所示。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
3、首先创建敌机,如下图所示。
在这里插入图片描述
代码如下:

//创建敌机
void CTankWar::CreateDiJi()
{
	int sjs;//随机数
	sjs = rand()%3;//产生0,1,2三个随机数
	switch(sjs)
	{
	case 0://矩形
		m_JX[m_nJX].x = rand()%600 + 400;//假设在400—1000范围内,1000-400=600,对600求余就是0—600,再加个400
		m_JX[m_nJX].y = 0;
		m_JX[m_nJX].v = rand()%80 + 20;//假设在20—100范围内,100-20=80,对80求余
		m_JX[m_nJX].Size = rand()%50 + 10;//假设在10—60范围内,60-10=50,对50求余
		m_JX[m_nJX].FS = m_JX[m_nJX].v + 300/m_JX[m_nJX].Size;//速度越大,尺寸越小,分数越高(300除10—60内的数,即5—30分)
		m_nJX++;//第n个矩形
		break;
		
	case 1://三角形
		m_SJX[m_nSJX].x = rand()%500 + 500;//500—1000
		m_SJX[m_nSJX].y = 0;
		m_SJX[m_nSJX].v = rand()%20 + 30;//30—50
		m_SJX[m_nSJX].Size = rand()%50 + 10;//10—60
		m_SJX[m_nSJX].FS = m_SJX[m_nSJX].v + 120/m_SJX[m_nSJX].Size;//2—12分
		m_nSJX++;
		break;
	case 2://圆形
		m_YX[m_nYX].x = rand()%500 + 450;//450—950
		m_YX[m_nYX].y = 0;
		m_YX[m_nYX].v = rand()%50 + 10;//10—60
		m_YX[m_nYX].Size = rand()%70 + 10;//10—80
		m_YX[m_nYX].FS = m_YX[m_nYX].v + 160/m_YX[m_nYX].Size;//2—16分
		m_nYX++;
		break;
	}
}

4、紧接着创建子弹,如下图所示。
在这里插入图片描述
代码如下:

//创建子弹
void CTankWar::CreateZiDan()
{
	m_ZD[m_nZD].x = m_x;
	m_ZD[m_nZD].Size = rand()%30 + 5;//5—35
	//用了“m_ZD[m_nZD].Size”,“.y”就一定要放在它的后面用,不然子弹出不来!!!!
	m_ZD[m_nZD].y = m_y - m_LPT - m_ZD[m_nZD].Size; 
	m_ZD[m_nZD].v = rand()%90 + 20;//10—100
	//m_ZD[m_nZD].Colr = RGB(c1,c2,c3);
	m_nZD++;
}

5、敌机和子弹已经创建好了,现在我们将敌机和子弹以及坦克给画出来,如下图所示。
(1)画敌机:
在这里插入图片描述
代码如下:

//画敌机
void CTankWar::DrawDiJi()
{
	int i;
	int x,y,r;
	for (i = 0; i < m_nJX; i++)//画矩形敌机
	{
		x = m_JX[i].x;
		y = m_JX[i].y;
		r = m_JX[i].Size/2;
		pDC->Rectangle(x - r,y - r,x + r,y + r);
	}
	
	for (i = 0; i < m_nYX; i++)//画圆形敌机
	{
		x = m_YX[i].x;
		y = m_YX[i].y;
		r = m_YX[i].Size/2;
		pDC->Ellipse(x - r,y - r,x + r,y + r);
	}
	
	for (i = 0; i < m_nSJX; i++)//画三角形敌机(当正三角形出处理)
	{
		x = m_SJX[i].x;
		y = m_SJX[i].y;
		r = m_SJX[i].Size/2;
		pDC->MoveTo(x,y);
		x -= r;
		y += r * 2 * cos(PI/6);
		pDC->LineTo(x,y);
		
		x += 2 * r;
		pDC->LineTo(x,y);
		
		x = m_SJX[i].x;
		y = m_SJX[i].y;
		pDC->LineTo(x,y);
	}
}

(2)画子弹:
在这里插入图片描述
代码如下:

void CTankWar::DrawZiDan()
{
	int i;
	int x,y,r;
	for (i = 0; i < m_nZD; i++)
	{
		x = m_ZD[i].x;
		y = m_ZD[i].y;
		r = m_r;//子弹大小固定,不要写成“r = m_ZD[i].Size”
		CBrush brush,*pOldBrush;
		brush.CreateSolidBrush(RGB(218,165,32));
		pOldBrush = pDC->SelectObject(&brush);
		pDC->Ellipse(x - r,y - r,x + r,y + r);//子弹按圆形处理
		brush.DeleteObject();
		pDC->SelectObject(pOldBrush);
	}
}

(3)画坦克:
在这里插入图片描述
代码如下:

void CTankWar::DrawTank()
{
	int x,y,r;
	int lx,ly,rx,ry;//代表矩形左上x,y,右下x,y
	//由外而内,如果先画中心圆,再画内围矩形,最后画外围矩形,则不显示中心圆和内围矩形
	lx = m_x - m_L1/2;//坦克外围矩形左上x
	ly = m_y - m_L1/2;//坦克外围矩形左上y
	rx = m_x + m_L1/2;//坦克外围矩形右下x
	ry = m_y + m_L1/2;//坦克外围矩形右下y
	pDC->Rectangle(lx,ly,rx,ry);//坦克外围矩形
	
	lx = m_x - m_L2/2;//坦克内围矩形左上x
	ly = m_y - m_L2/2;//坦克内围矩形左上y
	rx = m_x + m_L2/2;//坦克内围矩形右下x
	ry = m_y + m_L2/2;//坦克内围矩形右下y
	pDC->Rectangle(lx,ly,rx,ry);//坦克内围矩形
	
	lx = m_x - m_r;//坦克炮筒左上x
	ly = m_y - m_LPT;//坦克炮筒矩形左上y
	rx = m_x + m_r;//坦克炮筒矩形右下x
	ry = m_y;//坦克炮筒矩形右下y
	pDC->Rectangle(lx,ly,rx,ry);//坦克炮筒
	
	x = m_x;
	y = m_y;
	r = m_r;//坦克中心圆半径
	pDC->Ellipse(x - r,y - r,x + r,y + r);
}

6、敌机、子弹、坦克已经画出来了,那么我们就可以直接在Draw里面调用,如下图所示。
在这里插入图片描述
代码如下:

void CTankWar::Draw(CDC *p)
{
	pDC = p;
	
	DrawDiJi();
	DrawZiDan();
	DrawTank();
}

7、然后可以先去CYouXiView里面调用,如下图所示。
在这里插入图片描述
在这里插入图片描述
至此,我们的前期准备工作已经完全结束,现在来进一步的完善

三、实现过程(中期)

1、删除敌机,如下图所示。
在这里插入图片描述
代码如下:

//删除敌机
void CTankWar::DeleteDiJi(int xz, int n)//xz-形状,n-第n个敌机
{
	/*比如敌机有10个,最先到达下方的不一定是第9个,有可能是第5个或其他。假如第5个最先到达,
  可以让第5个等于第9个,从而删除第5个。拿下面代码举例,n = 5,m_nJX = 10。*/
	switch(xz)
	{
	case 0:
		m_JX[n] = m_JX[m_nJX - 1];
		m_nJX--;
		break;
	case 1:
		m_SJX[n] = m_SJX[m_nSJX - 1];
		m_nSJX--;
		break;
	case 2:
		m_YX[n] = m_YX[m_nYX - 1];
		m_nYX--;
		break;
	}
}

2、删除子弹,如下图所示。
在这里插入图片描述
代码如下:

//删除子弹
void CTankWar::DeleteZiDan(int n)
{
		m_ZD[n] = m_ZD[m_nZD - 1];
		m_nZD--;
}

3、移动敌机,如下图所示。
在这里插入图片描述
代码如下:

//移动敌机
void CTankWar::MoveDiJi()
{
	int i;
	for (i = 0; i < m_nJX; i++)
	{
		m_JX[i].y += m_JX[i].v * 0.2;
		if(m_JX[i].y > 700)
			DeleteDiJi(0,i);
	}
	
	for (i = 0; i < m_nSJX; i++)
	{
		m_SJX[i].y += m_SJX[i].v * 0.2;
		if(m_SJX[i].y > 700)
			DeleteDiJi(1,i);
	}
	
	for (i = 0; i < m_nYX; i++)
	{
		m_YX[i].y += m_YX[i].v * 0.2;
		if(m_YX[i].y > 700)
			DeleteDiJi(2,i);
	}
}

4、移动子弹,如下图所示。
在这里插入图片描述
代码如下:

//移动子弹
void CTankWar::MoveZiDan()
{
	int i;
	for(i = 0; i < m_nZD; i++)
	{
		m_ZD[i].y -= m_ZD[i].v * 0.2;
		if(m_ZD[i].y < 0)
			DeleteZiDan(i);
	}

}

5、移动坦克,如下图所示。
在这里插入图片描述
代码如下:

//移动坦克
void CTankWar::MoveTank(int n)
{
	switch(n)
	{
	case 37:
		m_x -= 4 * m_r;//左
		break;
	case 38:
		m_y -= 4 * m_r;//上
		break;
	case 39:
		m_x += 4 * m_r;//右
		break;
	case 40:
		m_y += 4 * m_r;//下
		break;
	}
}

6、再添加一个菜单,并建立类向导,添加命令消息,如下图所示。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
7、为了让坦克可以通过键盘上下左右键控制以及按空格键发射子弹,在CYouXiView里面添加WM_KEYDOWN,并在OnKeyDown里添加代码,如下图所示。
在这里插入图片描述
在这里插入图片描述
8、这时候我们可以编译运行看看它的效果,如下图所示。
在这里插入图片描述
四、实现过程(后期)

1、建立敌机和子弹碰撞函数。如果碰撞,让敌机、子弹消失,并且加上得分,如下图所示。
在这里插入图片描述
代码如下:

//碰撞敌机和子弹
void CTankWar::PengZhuangDiJiZiDan()
{
	int i,j;
	int d;//d表示子弹和敌机的距离
	for (i = 0; i < m_nZD; i++)
	{
		for (j = 0; j < m_nJX; j++)
		{
			d = sqrt( (m_ZD[i].x - m_JX[j].x) * (m_ZD[i].x - m_JX[j].x) + (m_ZD[i].y - m_JX[j].y) * (m_ZD[i].y - m_JX[j].y) );
			if(d < m_ZD[i].Size + m_JX[j].Size/2)
			{
				m_ZongFen += m_JX[j].FS;
				DeleteDiJi(0,j);//删除第j个矩形敌机
				DeleteZiDan(i);//删除第i个子弹
				break;
			}
		}
		if(j < m_nJX)
			continue;
		
		for (j = 0; j < m_nSJX; j++)
		{
			d = sqrt( (m_ZD[i].x - m_SJX[j].x) * (m_ZD[i].x - m_SJX[j].x) + (m_ZD[i].y - m_SJX[j].y) * (m_ZD[i].y - m_SJX[j].y) );
			if(d < m_ZD[i].Size + m_SJX[j].Size/2)
			{
				m_ZongFen += m_SJX[j].FS;
				DeleteDiJi(1,j);//删除第j个三角形敌机
				DeleteZiDan(i);
				break;
			}
		}
		if(j < m_nSJX)
		continue;
		
		for (j = 0; j < m_nYX; j++)
		{
			d = sqrt( (m_ZD[i].x - m_YX[j].x) * (m_ZD[i].x - m_YX[j].x) + (m_ZD[i].y - m_YX[j].y) * (m_ZD[i].y - m_YX[j].y) );
			if(d < m_ZD[i].Size+ m_YX[j].Size/2)
			{	
				m_ZongFen += m_YX[j].FS;
				DeleteDiJi(2,j);//删除第j个圆形敌机
				DeleteZiDan(i);
				break;
			}
		}
	}
}

2、建立敌机和坦克碰撞函数。如果碰撞,游戏结束,如下图所示。
在这里插入图片描述
代码如下:

//碰撞敌机和坦克
int CTankWar::PengZhuangDiJiTank()
{
	int i,d;//d表示坦克和敌机的距离
	
	for(i = 0; i < m_nJX; i++)
	{
		d = sqrt( (m_x -m_JX[i].x) * (m_x -m_JX[i].x) + (m_y - m_JX[i].y) * (m_y - m_JX[i].y) );
		if(d < m_JX[i].Size/2 + m_L1/2)
			return 1;//待会儿在OnTimer里判断
	}
	
	for(i = 0; i < m_nSJX; i++)
	{
        d = sqrt( (m_x - m_SJX[i].x) * (m_x - m_SJX[i].x) + (m_y - m_SJX[i].y) * (m_y - m_SJX[i].y) );
		if(d < m_SJX[i].Size/2 + m_L1/2)
			return 1;
	}
	
	for(i = 0; i < m_nYX; i++)
	{
		d = sqrt( (m_x - m_YX[i].x) * (m_x - m_YX[i].x) + (m_y - m_YX[i].y) * (m_y - m_YX[i].y) );
		if(d < m_YX[i].Size/2 + m_L1/2)
			return 1;
	}
	return 0;
}

3、游戏过程中有得分,我们可以通过屏幕显示,如下图所示。
在这里插入图片描述
代码如下:

void CTankWar::Draw(CDC *p)
{
	CString str;
	CFont ft; //设置输出字体
	pDC = p;
	
	DrawDiJi();
	DrawZiDan();
	DrawTank();

	str.Format("您已经获得了 %d 分!",m_ZongFen);
	ft.CreatePointFont(500,_T("隶书"),NULL); //输出字体大小、风格
	pDC->SelectObject(&ft);
	pDC->SetTextColor(RGB(255,0,0)); //字体颜色
	pDC->TextOut(1100,450,str);
}

4、在CYouXiView里面添加WM_TIMER,并在OnTimer里添加代码,如下图所示。
在这里插入图片描述
代码如下:

void CYouXiView::OnTimer(UINT nIDEvent) 
{
	// TODO: Add your message handler code here and/or call default
	if (nIDEvent == 1)
	{
		m_TankWar.CreateDiJi(); //调用敌机
		Invalidate(true);
	}
	if (nIDEvent == 2)
	{
		m_TankWar.MoveDiJi();
		m_TankWar.MoveZiDan();
		m_TankWar.PengZhuangDiJiZiDan();
		if(m_TankWar.PengZhuangDiJiTank() == 1)
		{
			KillTimer(1);
			KillTimer(2);
			AfxMessageBox("游戏结束!");
		}
		Invalidate(true);
	}
	CView::OnTimer(nIDEvent);
}

5、为了消除屏幕闪动,我们还可以利用双缓存机制做进一步处理,如下图所示。
在这里插入图片描述

BOOL CYouXiView::OnEraseBkgnd(CDC* pDC) 
{
	// TODO: Add your message handler code here and/or call default
	return true;
	return CView::OnEraseBkgnd(pDC);
}

代码如下:

void CYouXiView::OnDraw(CDC* pDC)
{
	CYouXiDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	// TODO: add draw code for native data here
//	m_TankWar.Draw(pDC);

	CDC MemDC; //定义内存DC
	int width,height; //定义屏幕宽度、高度
	CRect rect; //建立rect对象
	CBitmap MemBitmap; //缓冲的内存位图
	GetWindowRect(&rect); //获取当前视图的大小
	width = rect.Width();
	height = rect.Height(); //记录当前屏幕大小
	MemDC.CreateCompatibleDC(NULL); //建立兼容内存DC(设备上下文),NULL为系统默认模式
	MemBitmap.CreateCompatibleBitmap(pDC,width,height);
	CBitmap *pOldBit = MemDC.SelectObject(&MemBitmap); //保存之前的内存位图
	MemDC.FillSolidRect(0,0,width,height,RGB(212,242,231)); //设置背景颜色
	MemDC.SetBkMode(TRANSPARENT); //设置缓冲DC参数,为双缓存机制做准备	
//=====================================================================
	m_TankWar.Draw(&MemDC);//在缓冲DC中画图
	pDC->BitBlt(0,0,width,height,&MemDC,0,0,SRCCOPY);
	MemBitmap.DeleteObject();
	MemDC.DeleteDC();
}

五、运行结果
在这里插入图片描述
在这里插入图片描述

  • 6
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值