弄了控制台的贪吃蛇等游戏后,学了一些MFC,即马上去弄MFC的贪吃蛇。写的时候由于学的MFC视频和实际操作还是有很大区别的,很多东西还是要自己去查MSDN或百度才知道的,在这里次MFC贪吃蛇中学到了很多知识!做出来后发现界面有闪烁的显现,后有前辈说可以采取双缓冲去避免闪烁,即百度查了不少这方面的资料,自己编写后还是闪烁,后问前辈是否写错,前辈说未写错,我就继续找度娘,找了很久没弄出来,最后就去csdn论坛去发了个帖子。呵,果然给力,一下解决了问题。即只要添加一个消息函数OnEraseBkgnd(CDC* pDC) 在加上之前的双缓冲即可避免不闪烁了!很是高兴啊!
关于双缓冲 和 OnEraseBkgnd(CDC* pDC) 的资料请看我博客的文章。
我是用VC6建立一个.exe
大家可以交流下,如有不好之处,请见谅!
// mfc_snackeView.cpp : implementation of the CMfc_snackeView class
//
#include "stdafx.h"
#include "mfc_snacke.h"
#include "mfc_snackeDoc.h"
#include "mfc_snackeView.h"
#include <cstdlib>
#include <ctime>
#include <cmath>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/
// CMfc_snackeView
IMPLEMENT_DYNCREATE(CMfc_snackeView, CView)
BEGIN_MESSAGE_MAP(CMfc_snackeView, CView)
//{{AFX_MSG_MAP(CMfc_snackeView)
ON_WM_TIMER()
ON_WM_KEYDOWN()
ON_COMMAND(IDS_EASY, OnEasy)
ON_COMMAND(IDS_MEDIUM, OnMedium)
ON_COMMAND(IDS_HARD, OnHard)
ON_WM_ERASEBKGND()
//}}AFX_MSG_MAP
// Standard printing commands
ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
END_MESSAGE_MAP()
/
// CMfc_snackeView construction/destruction
CMfc_snackeView::CMfc_snackeView()
{
// TODO: add construction code here
m_Count = m_UseTime = 0;
m_start = 0; //记录玩家开始第几次玩
m_speed = 500; //初始化速度为0.5s
H = 35; //地图的high 和 length,实际x = (40,580), y = (40, 380)
L = 55; //这样主要是为了rand的方便
srand(time(0)); //随机种子
CMfc_snackeView::Initial_Game(); //初始化游戏的界面
}
CMfc_snackeView::~CMfc_snackeView()
{
}
BOOL CMfc_snackeView::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
return CView::PreCreateWindow(cs);
}
/
// CMfc_snackeView drawing
void CMfc_snackeView::OnDraw(CDC* pDC) //画界面
{
CMfc_snackeDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
//
CDC MemDC; //首先定义一个显示设备对象
CBitmap MemBitmap; //定义一个位图对象
//随后建立与屏幕显示兼容的内存显示设备
MemDC.CreateCompatibleDC(pDC);
//这时还不能绘图,因为没有地方画
//下面建立一个于屏幕显示兼容的位图,至于位图的大小嘛
//可以用窗口的大小
CRect rect;
GetClientRect(&rect);
MemBitmap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
//将位图选入到内存显示设备中
//只要有选入了位图的内存显示设备才有地方地方绘图,画到指定的位图上
CBitmap *pOldBit = MemDC.SelectObject(&MemBitmap);
//先用背景色将位图清除干净,这里我用的是白色作为背景
//你也可以用自己应该用的颜色
MemDC.FillSolidRect(0, 0, rect.Width(), rect.Height(), RGB(255, 255, 255));
//绘图//
//draw border of game
MemDC.Rectangle(CRect(39, 39, 601, 401));
//Records cstring show
CString s;
s.Format("当前用时: %d", m_UseTime/(1000/m_speed));
MemDC.TextOut(620, 60, s);
s.Format("当前得分: %d", m_Count);
MemDC.TextOut(620, 100, s);
s.Format("版权所有: [菜菜]咸鱼");
MemDC.TextOut(620, 140, s);
s.Format("时间: 2011-10-22");
MemDC.TextOut(620, 180, s);
//draw snake
//draw head of snake
CPoint h;
h = m_body.GetAt(0);
MemDC.SelectStockObject(DKGRAY_BRUSH);
switch(m_NowDir)
{
case 4: MemDC.Pie(CRect(h.x, h.y, h.x+20, h.y+20), CPoint(h.x+5, h.y), CPoint(h.x+15, h.y));
MemDC.SelectStockObject(WHITE_BRUSH ); //蛇的眼睛
MemDC.Ellipse(h.x+13, h.y+5, h.x+18, h.y+10);
break;
case 3: MemDC.Pie(CRect(h.x, h.y, h.x+20, h.y+20), CPoint(h.x+20, h.y+5), CPoint(h.x+20, h.y+15));
MemDC.SelectStockObject(WHITE_BRUSH );
MemDC.Ellipse(h.x+10, h.y+2, h.x+15, h.y+7);
break;
case 2: MemDC.Pie(CRect(h.x, h.y, h.x+20, h.y+20), CPoint(h.x+15, h.y+20), CPoint(h.x+5, h.y+20));
MemDC.SelectStockObject(WHITE_BRUSH );
MemDC.Ellipse(h.x+13, h.y+10, h.x+18, h.y+15);
break;
case 1: MemDC.Pie(CRect(h.x, h.y, h.x+20, h.y+20), CPoint(h.x, h.y+15), CPoint(h.x, h.y+5));
MemDC.SelectStockObject(WHITE_BRUSH );
MemDC.Ellipse(h.x+5, h.y+2, h.x+10, h.y+7);
break;
}
//draw body of snake
CPoint b;
MemDC.SelectStockObject(GRAY_BRUSH);
for(int i = 1; i <= m_body.GetUpperBound(); i++)
{
b = m_body.GetAt(i);
MemDC.Rectangle(CRect(b.x, b.y, b.x+20, b.y+20));
}
//draw a food
CBrush br;
br.CreateSolidBrush(RGB(0, 255, 0));
MemDC.SelectObject(br);
MemDC.FillRect(CRect(m_food.x, m_food.y, m_food.x+20, m_food.y+20), &br);
MemDC.Rectangle(CRect(m_food.x, m_food.y, m_food.x+20, m_food.y+20));
//将内存中图拷贝到屏幕上进行显示
pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &MemDC, 0, 0, SRCCOPY);
//绘图完成后的清理
MemBitmap.DeleteObject();
MemDC.DeleteDC();
}
/
// CMfc_snackeView printing
BOOL CMfc_snackeView::OnPreparePrinting(CPrintInfo* pInfo)
{
// default preparation
return DoPreparePrinting(pInfo);
}
void CMfc_snackeView::OnBeginPrinting(CDC* , CPrintInfo* )
{
// TODO: add extra initialization before printing
}
void CMfc_snackeView::OnEndPrinting(CDC* , CPrintInfo* )
{
// TODO: add cleanup after printing
}
/
// CMfc_snackeView diagnostics
#ifdef _DEBUG
void CMfc_snackeView::AssertValid() const
{
CView::AssertValid();
}
void CMfc_snackeView::Dump(CDumpContext& dc) const
{
CView::Dump(dc);
}
CMfc_snackeDoc* CMfc_snackeView::GetDocument() // non-debug version is inline
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMfc_snackeDoc)));
return (CMfc_snackeDoc*)m_pDocument;
}
#endif //_DEBUG
/
// CMfc_snackeView message handlers
void CMfc_snackeView::Initial_Game() //游戏地图的初始化
{
m_body.RemoveAll();
//initial direciton
m_NowDir = m_PreDir = 3;
//inital head of snake
CPoint head;
head.x = 100; head.y = 100;
m_body.Add(head);
//create rand food
CMfc_snackeView::Create_food();
}
void CMfc_snackeView::Create_food()
{
CPoint t;
int x, y, prove;
while(1)
{
x = rand()%L + 4;
y = rand()%H + 4;
if(x%2 == 0 && y%2 == 0) //其实坐标是x=(40,580), y=(40,380)
{ //每一个图标如蛇头的high和length是占20, 20
prove = 0;
x *= 10; y *= 10;
//食物不能是蛇的地方
for(int i = 0; i <= m_body.GetUpperBound(); i++)
{
t = m_body.GetAt(i);
if(t.x == x && t.y == y)
{
prove = 1;
break;
}
}
if(!prove)
break;
}
}
m_food = CPoint(x, y);
}
void CMfc_snackeView::GameStart() //开始游戏哦
{ //玩家时间和得分数的初始化
m_Count = m_UseTime = 0;
m_start++;
if(m_start > 1)
CMfc_snackeView::Initial_Game();
//每隔m_speed时间发送WM_TIMER消息
Timer = SetTimer(1, m_speed, NULL);
}
void CMfc_snackeView::OnTimer(UINT nIDEvent) //WM_TIMER消息
{
// TODO: Add your message handler code here and/or call default
m_UseTime++; //时间记录
//蛇移动
CMfc_snackeView::Move();
CView::OnTimer(nIDEvent);
}
void CMfc_snackeView::Move() //蛇移动函数
{
if(m_PreDir == m_NowDir || abs(m_PreDir-m_NowDir) == 2)
m_NowDir = m_PreDir;
else
m_PreDir = m_NowDir;
CPoint head;
head = m_body.GetAt(0);
//4个方向
switch(m_NowDir)
{
case 4: head.y -= 20;
break;
case 3: head.x += 20;
break;
case 2: head.y += 20;
break;
case 1: head.x -= 20;
break;
}
int over = 0;
CPoint t;
//蛇头是否吃到自己
for(int i = 0; i <= m_body.GetUpperBound(); i++)
{
t = m_body.GetAt(i);
if(head.x == t.x && head.y == t.y)
{
over = 1;
break;
}
}
if(!Check(head) || over)
{ //结束Timer,并弹出一个结束的对话框
KillTimer(Timer);
MessageBox("Game is over! You are fail!");
}
else
{
m_body.InsertAt(0, head);
//蛇头吃到食物
if(head.x == m_food.x && head.y == m_food.y)
{
m_Count++;
Create_food();
}
else
m_body.RemoveAt(m_body.GetUpperBound());
}
//使整个窗口客户区无效,意味着需要重绘,即调用OnDraw函数
//InvalidateRect(CRect(39, 39, 839, 489));
Invalidate();
}
//键盘按键判断
void CMfc_snackeView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: Add your message handler code here and/or call default
if(m_start)
{
switch(nChar)
{
case 38: //up
m_NowDir = 4;
break;
case 39: //right
m_NowDir = 3;
break;
case 40: //down
m_NowDir = 2;
break;
case 37: //left
m_NowDir = 1;
break;
default:
m_NowDir = m_PreDir;
break;
}
}
CView::OnKeyDown(nChar, nRepCnt, nFlags);
}
//判断是否出界
int CMfc_snackeView::Check(CPoint head)
{
if(head.x < 40 || head.x > 580
|| head.y < 40 || head.y > 380)
return 0;
else
return 1;
}
void CMfc_snackeView::OnEasy()
{
// TODO: Add your command handler code here
m_speed = 500;
CMfc_snackeView::GameStart();
}
void CMfc_snackeView::OnMedium()
{
// TODO: Add your command handler code here
m_speed = 200;
CMfc_snackeView::GameStart();
}
void CMfc_snackeView::OnHard()
{
// TODO: Add your command handler code here
m_speed = 50;
CMfc_snackeView::GameStart();
}
BOOL CMfc_snackeView::OnEraseBkgnd(CDC* pDC)
{
// TODO: Add your message handler code here and/or call default
//即擦除背景由本函数处理,不需要OnPain()来擦除背景
return true;
}