计算机软件技术实习项目二 MFC贪吃蛇

一、功能描述
(1)通过“START”、“PAUSE”、“EXIT”三个控件,控制游戏的开始、暂停和终止。

(2)通过四个方向键控制蛇的运动方向,吃掉随机出现的豆子。

(3)吃到豆子蛇身会变长,同时得分增加;碰到壁或者自身则该轮游戏结束。

(4)游戏开始、暂停、结束时进行提示。

二、概要设计

1.系统框架

三、主要功能实现

1、主要函数

(1)食物类:

#pragma once
#include "Point.h"
#ifndef Food_H
#define Food_h

class Food
{
public:
    Food();
    ~Food() {}
    void draw(CDC* dc);
    Point init();
    friend class Snake;
private:
    Point x;
};
#endif // DEBUG
(2)蛇类:

#pragma once
#include "Point.h"
#ifndef Snake_H
#define Snake_H
#include "vector"
#include "Food.h"
using namespace std;
class Snake
{
public:
    friend class CTCSnakeView;
    Snake(Point head, int d = 2, int len = 1);
    ~Snake() {

    }
    void drawSnake(CDC *dc);  //画蛇
    bool eatFood(int x, int y); //吃食物
    bool move(Food &food); //游走
    void changeD(int);  //改变方向
    bool isHit();
    int getD() {
        return direct;
    }        // 获取方向
    Point getH() {
        return head;
    }        //获取头位置
private:
    Point head;  //蛇头位置
    int direct;  //前进方向
    vector<Point> body; // 蛇身位置
    int length; //蛇长
    bool isEat();  //判断是否吃到自己
    //蛇身宽度为10像素
};
#endif // !Snake_H
(3)点类:

#pragma once
#ifndef Point_H
#define Point_H


class Point
{
public:
    Point(int x = 0, int y = 0);
//    Point(Point&);
    int getPointx();
    int getPointy();
    bool changex(int a);
    bool changey(int b);
    bool cmp(Point &a);
private:
    int x, y;
};
#endif // !Point_H
2、具体功能

(1)随机食物生成:

#include "pch.h"
#include "Food.h"
Food::Food()
{
    srand(time(NULL));
    int a = rand() % 320 + 100;
    a -= a % 10;
    int b = rand() % 320 + 100;
    b -= b % 10;
    x.changex(a);
    x.changey(b);
}
void Food::draw(CDC* dc)
{
    CBrush brush;
    brush.CreateSolidBrush(RGB(0, 0, 255));
    dc->FillRect(CRect(x.getPointx(), x.getPointy(), x.getPointx() + 10, x.getPointy() + 10), &brush);
    brush.DeleteObject();
}
Point Food::init()
{
    srand(time(NULL));
    int a = rand() % 380 + 40;
    a -= a % 10;
    int b = rand() % 380 + 40;
    b -= b % 10;
    x.changex(a);
    x.changey(b);
    return x;
}

(2)点坐标的实现:

#include "pch.h"
#include "Point.h"
Point::Point(int x, int y)
{
    this->x = x;
    this->y = y;
}
int Point::getPointx() {
    return  x;
}
int Point::getPointy() {
    return y;
}
bool Point::changex(int a)
{
    x = a;
    return true;
}
bool Point::changey(int b)
{
    y = b;
    return true;
}
bool Point::cmp(Point& a) {
    if (a.x == x && a.y == y)
        return true;
    return false;
}
//Point::Point(Point& a) {
//    x = a.x;
//    y = a.y;
//}

(3)画蛇以及蛇的移动:

#include "pch.h"
#include "Snake.h"
Snake::Snake(Point head, int d, int len):head(60, 60), direct(d), length(len)
{
    body.push_back(Point(50, 60));
}
void Snake::drawSnake(CDC * dc)
{
    CBrush brush;
    brush.CreateSolidBrush(RGB(0, 255, 0));
    dc->FillRect(CRect(head.getPointx(), head.getPointy(), head.getPointx()+10, head.getPointy()+10), &brush);
    brush.DeleteObject();
    brush.CreateSolidBrush(RGB(255, 0, 0));
    for (int i = length - 1; i >= 0; i--) {
        dc->FillRect(CRect(body[i].getPointx(), body[i].getPointy(), body[i].getPointx() + 10, body[i].getPointy() + 10), &brush);
    }
    brush.DeleteObject();
}
bool Snake::isHit() {
    if (head.getPointx() <= 30 || head.getPointx() >= 420)
        return true;
    if (head.getPointy() <= 30 || head.getPointy() >= 420)
        return true;
    return false;
}
bool Snake::move(Food &food)
{
    int i = length;
    if (isEat())
    {
        return false;
    }
    if (isHit())
    {
        return false;
    }
    if (eatFood(food.x.getPointx(), food.x.getPointy())) {
        Point n = food.init();
        if (n.cmp(head)) 
            n = food.init();
        while (1) {
            i = length;
            for (i--; i >= 0; i--) {
                if (body[i].cmp((n))) {
                    n = food.init();
                    break;
                }
            }    
            if (i < 0)
                break;
        }
        Point t(body[length - 1].getPointx(), body[length - 1].getPointy());
        body.push_back(t);
        length++;
        return true;
    }
    i = length;
    switch (direct)
    {
        case 1: {
            for (i--; i >= 1; i--)
            {
                body[i].changex(body[i - 1].getPointx());
                body[i].changey(body[i - 1].getPointy());
            }
            body[0].changex(head.getPointx());
            body[0].changey(head.getPointy());
            head.changey(head.getPointy() - 10);
            break;
        }
        case 2: {
            for (i--; i >= 1; i--)
            {
                    body[i].changex(body[i - 1].getPointx());
                    body[i].changey(body[i - 1].getPointy());            
            }
            body[0].changex(head.getPointx());
            body[0].changey(head.getPointy());
            head.changex(head.getPointx() + 10);
            break;
        }
        case 3: {
            for (i--; i >= 1; i--)
            {
                    body[i].changex(body[i - 1].getPointx());
                    body[i].changey(body[i - 1].getPointy());
            }
            body[0].changex(head.getPointx());
            body[0].changey(head.getPointy());
            head.changey(head.getPointy() + 10);
            break;
        }
        case 4: {
            for (i--; i >= 1; i--)
            {
                body[i].changex(body[i - 1].getPointx());
                body[i].changey(body[i - 1].getPointy());
            }
            body[0].changex(head.getPointx());
            body[0].changey(head.getPointy());
            head.changex(head.getPointx() - 10);
            break;
        }
    }
    return true;
}
bool Snake::isEat()
{
    int x = head.getPointx();
    int y = head.getPointy();
    int i = length;
    if (i > 3) {
    for (i-- ; i >= 0; i--)
        {
            if (head.getPointx() == body[i].getPointx() && head.getPointy() == body[i].getPointy())
                return true;
        }
    }
    
    return false;
}
bool Snake::eatFood(int x, int y)
{
    if (head.getPointx() == x && head.getPointy() == y)
        return true;
    return false;
}
void Snake::changeD(int a)
{
    direct = a;
}

3、画图以及控件实现

#include "pch.h"
#include "framework.h"
// SHARED_HANDLERS 可以在实现预览、缩略图和搜索筛选器句柄的
// ATL 项目中进行定义,并允许与该项目共享文档代码。
#ifndef SHARED_HANDLERS
#include "TCSnake.h"
#endif

#include "TCSnakeDoc.h"
#include "TCSnakeView.h"
#include "Snake.h"
#include "Food.h"
#include "stdlib.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CTCSnakeView

IMPLEMENT_DYNCREATE(CTCSnakeView, CView)

BEGIN_MESSAGE_MAP(CTCSnakeView, CView)
    // 标准打印命令
    ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint)
    ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint)
    ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CTCSnakeView::OnFilePrintPreview)
    ON_WM_CONTEXTMENU()
    ON_WM_RBUTTONUP()
    ON_WM_KEYDOWN()
    ON_WM_TIMER()
    ON_COMMAND(ID_32775, &CTCSnakeView::Onstart)
    ON_COMMAND(ID_32776, &CTCSnakeView::Onstart2)
    ON_COMMAND(ID_32777, &CTCSnakeView::Onstart3)
    ON_COMMAND(ID_32778, &CTCSnakeView::Onrestart)
    ON_COMMAND(ID_32779, &CTCSnakeView::Onbdr)
END_MESSAGE_MAP()

// CTCSnakeView 构造/析构

CTCSnakeView::CTCSnakeView() noexcept:snake((60, 60))
{
    // TODO: 在此处添加构造代码

}
CTCSnakeView::~CTCSnakeView()
{

}

BOOL CTCSnakeView::PreCreateWindow(CREATESTRUCT& cs)
{
    // TODO: 在此处通过修改
    //  CREATESTRUCT cs 来修改窗口类或样式

    return CView::PreCreateWindow(cs);
}

// CTCSnakeView 绘图

void CTCSnakeView::OnDraw(CDC* pDC)
{
    CTCSnakeDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    if (!pDoc)
        return;
    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(30, 30, 430, 430));
    MemDC.TextOutW(500, 200, _T("游戏说明:"));
    MemDC.TextOutW(500, 220, _T("这个贪吃蛇是被绿的贪吃蛇,喜欢吃蓝莓派!"));
    MemDC.TextOutW(500, 240, _T("上下左右控制它的移动方向"));
    MemDC.TextOutW(500, 260, _T("重开说明:死了重开第一步点开始游戏-重新开始,第二步选择模式"));
    MemDC.TextOutW(500, 280, _T("游戏开始后可调难度:“a”简单、“s”一般、“d”困难、“f”不当人"));
    MemDC.TextOutW(500, 340, TEXT("你的分数是"));
    CString n;
    n.Format(_T("%d"), snake.length-1);
    MemDC.TextOutW(600, 340, n);
    snake.drawSnake(&MemDC);
    food.draw(&MemDC);
    pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &MemDC, 0, 0, SRCCOPY);
    //绘图完成后的清理
    

    MemBitmap.DeleteObject();
    MemDC.DeleteDC();
    // TODO: 在此处为本机数据添加绘制代码
}


// CTCSnakeView 打印


void CTCSnakeView::OnFilePrintPreview()
{
#ifndef SHARED_HANDLERS
    AFXPrintPreview(this);
#endif
}

BOOL CTCSnakeView::OnPreparePrinting(CPrintInfo* pInfo)
{
    // 默认准备
    return DoPreparePrinting(pInfo);
}

void CTCSnakeView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
    // TODO: 添加额外的打印前进行的初始化过程
}

void CTCSnakeView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
    // TODO: 添加打印后进行的清理过程
}

void CTCSnakeView::OnRButtonUp(UINT /* nFlags */, CPoint point)
{
    ClientToScreen(&point);
    OnContextMenu(this, point);
}

void CTCSnakeView::OnContextMenu(CWnd* /* pWnd */, CPoint point)
{
#ifndef SHARED_HANDLERS
    theApp.GetContextMenuManager()->ShowPopupMenu(IDR_POPUP_EDIT, point.x, point.y, this, TRUE);
#endif
}


// CTCSnakeView 诊断

#ifdef _DEBUG
void CTCSnakeView::AssertValid() const
{
    CView::AssertValid();
}

void CTCSnakeView::Dump(CDumpContext& dc) const
{
    CView::Dump(dc);
}

CTCSnakeDoc* CTCSnakeView::GetDocument() const // 非调试版本是内联的
{
    ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CTCSnakeDoc)));
    return (CTCSnakeDoc*)m_pDocument;
}
#endif //_DEBUG


// CTCSnakeView 消息处理程序

void CTCSnakeView::OnInitialUpdate()
{
    CView::OnInitialUpdate();
    // TODO: 在此添加专用代码和/或调用基类
    
}


void CTCSnakeView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    static Point x = snake.getH();
    if (!x.cmp(snake.getH())) {
    switch (nChar)
        {
        case VK_UP: if (snake.getD() != 3) {
            x = snake.getH(); 
            snake.changeD(1);
        }    
            break;
        case VK_RIGHT: if (snake.getD() != 4) {
            x = snake.getH();
            snake.changeD(2);
        }    
            break;
        case VK_DOWN: if (snake.getD() != 1) {
            x = snake.getH();
            snake.changeD(3);
        }    
            break;
        case VK_LEFT: if (snake.getD() != 2) {
            x = snake.getH();
            snake.changeD(4);
        }
            break;
        default: break;
        }
    if (nChar == 97 || nChar == 65)
        Onstart();
    if (nChar == 115 || nChar == 83)
        Onstart2();
    if (nChar == 100 || nChar == 68)
        Onstart3();
    if (nChar == 102 || nChar == 70)
        Onbdr();
    }
    
    CView::OnKeyDown(nChar, nRepCnt, nFlags);
}


void CTCSnakeView::OnTimer(UINT_PTR nIDEvent)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    if (!snake.move(food)) {
        KillTimer(1);
        MessageBox(TEXT("游戏结束"), TEXT("游戏结束"), 1);
    }
        
    Invalidate();
    CView::OnTimer(nIDEvent);
}


void CTCSnakeView::Onstart()
{
    // TODO: 在此添加命令处理程序代码
    SetTimer(1, 200, NULL);
}


void CTCSnakeView::Onstart2()
{
    // TODO: 在此添加命令处理程序代码
    SetTimer(1, 100, NULL);
}


void CTCSnakeView::Onstart3()
{
    // TODO: 在此添加命令处理程序代码
    SetTimer(1, 50, NULL);
}


void CTCSnakeView::Onrestart()
{
    // TODO: 在此添加命令处理程序代码
    snake.body.clear();
    snake.body.push_back(Point(50, 60));
    snake.head.changex(60);
    snake.head.changey(60);
    snake.direct = 2;
    snake.length = 1;
}


void CTCSnakeView::Onbdr()
{
    // TODO: 在此添加命令处理程序代码
    SetTimer(1, 20, NULL);
}
4、运行界面

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值