MFC 小游戏之贪吃蛇

弄了控制台的贪吃蛇等游戏后,学了一些MFC,即马上去弄MFC的贪吃蛇。写的时候由于学的MFC视频和实际操作还是有很大区别的,很多东西还是要自己去查MSDN或百度才知道的,在这里次MFC贪吃蛇中学到了很多知识!做出来后发现界面有闪烁的显现,后有前辈说可以采取双缓冲去避免闪烁,即百度查了不少这方面的资料,自己编写后还是闪烁,后问前辈是否写错,前辈说未写错,我就继续找度娘,找了很久没弄出来,最后就去csdn论坛去发了个帖子。呵,果然给力,一下解决了问题。即只要添加一个消息函数OnEraseBkgnd(CDC* pDC) 在加上之前的双缓冲即可避免不闪烁了!很是高兴啊!


代码下载网址: http://download.csdn.net/detail/hai8902882/3823052 


关于双缓冲 和 OnEraseBkgnd(CDC* pDC) 的资料请看我博客的文章。

 

我是用VC6建立一个.exe 的文本空间,即再在view类中添加文件,我将这部分的代码贴出

大家可以交流下,如有不好之处,请见谅!


// 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; 
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值