一、功能描述
(1)通过“START”、“PAUSE”、“EXIT”三个控件,控制游戏的开始、暂停和终止。
(2)通过四个方向键控制蛇的运动方向,吃掉随机出现的豆子。
(3)吃到豆子蛇身会变长,同时得分增加;碰到壁或者自身则该轮游戏结束。
(4)游戏开始、暂停、结束时进行提示。
二、概要设计
1.系统框架![](https://i-blog.csdnimg.cn/blog_migrate/c164af0ded83c4be5970948a8b7845e2.png)
三、主要功能实现
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、运行界面