mfc实现的贪吃蛇游戏

#include "stdafx.h"

#include "Snake.h"

#include "SnakeDlg.h"

#include "afxdialogex.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#endif

#include "stdafx.h"

#include "Snake.h"

#include "SnakeDlg.h"

#include "afxdialogex.h"

#include <time.h>

#define UNIT 10

#define SPEED 140

// 方向宏定义 → 一看就懂!

#define DIR_RIGHT 1

#define DIR_LEFT 2

#define DIR_UP 3

#define DIR_DOWN 4

struct Food {

int x, y;

bool yes;

} food;

struct Snake {

int x[200];

int y[200];

int node;

int dir;

bool life;

} snake;

int score = 0;

bool m_dirLocked = false; // 方向锁

bool m_bPaused = false; // 是否暂停

bool m_bGameOver = false;// 是否游戏结束

class CAboutDlg : public CDialogEx

{

public:

CAboutDlg();

enum { IDD = IDD_ABOUTBOX };

protected:

virtual void DoDataExchange(CDataExchange* pDX);

protected:

DECLARE_MESSAGE_MAP()

};

CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)

{

}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)

{

CDialogEx::DoDataExchange(pDX);

}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)

END_MESSAGE_MAP()

BOOL CSnakeDlg::PreTranslateMessage(MSG* pMsg)

{

if (pMsg->message == WM_KEYDOWN)

{

OnKeyDown(pMsg->wParam, 0, 0);

return TRUE;

}

return CDialogEx::PreTranslateMessage(pMsg);

}

CSnakeDlg::CSnakeDlg(CWnd* pParent /*=NULL*/)

: CDialogEx(CSnakeDlg::IDD, pParent)

{

m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

srand((unsigned)time(NULL));

snake.node = 2;

snake.dir = 1;

snake.life = 1;

snake.x[0] = 100; snake.y[0] = 100;

snake.x[1] = 110; snake.y[1] = 100;

food.yes = 1;

score = 0;

snake.x[0] = 120;

snake.y[0] = 120;

snake.dir = 1; // 向右

food.yes = 1;

}

void CSnakeDlg::InitSnake()

{

snake.node = 2;

snake.x[0] = 100;

snake.y[0] = 100;

snake.x[1] = 90;

snake.y[1] = 100;

snake.dir = DIR_RIGHT;

snake.life = 1;

food.yes = 1;

m_bShowGameOver = false;

}

void CSnakeDlg::DoDataExchange(CDataExchange* pDX)

{

CDialogEx::DoDataExchange(pDX);

}

BEGIN_MESSAGE_MAP(CSnakeDlg, CDialogEx)

ON_WM_SYSCOMMAND()

ON_WM_PAINT()

ON_WM_QUERYDRAGICON()

ON_WM_TIMER()

ON_WM_KEYDOWN()

END_MESSAGE_MAP()

BOOL CSnakeDlg::OnInitDialog()

{

CDialogEx::OnInitDialog();

ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);

ASSERT(IDM_ABOUTBOX < 0xF000);

CMenu* pSysMenu = GetSystemMenu(FALSE);

if (pSysMenu != NULL)

{

BOOL bNameValid;

CString strAboutMenu;

bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);

ASSERT(bNameValid);

if (!strAboutMenu.IsEmpty())

{

pSysMenu->AppendMenu(MF_SEPARATOR);

pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);

}

}

SetIcon(m_hIcon, TRUE);

SetIcon(m_hIcon, FALSE);

Invalidate(TRUE);

SetTimer(1, 150, NULL);

return TRUE;

}

void CSnakeDlg::OnSysCommand(UINT nID, LPARAM lParam)

{

if ((nID & 0xFFF0) == IDM_ABOUTBOX)

{

CAboutDlg dlgAbout;

dlgAbout.DoModal();

}

else

{

CDialogEx::OnSysCommand(nID, lParam);

}

}

void CSnakeDlg::OnPaint()

{

if (IsIconic()){

CPaintDC dc(this);

SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

int cxIcon = GetSystemMetrics(SM_CXICON);

int cyIcon = GetSystemMetrics(SM_CYICON);

CRect rect;

GetClientRect(&rect);

int x = (rect.Width() - cxIcon + 1) / 2;

int y = (rect.Height() - cyIcon + 1) / 2;

dc.DrawIcon(x, y, m_hIcon);

}else{

CPaintDC dc(this);

dc.FillSolidRect(50, 40, 560, 420, RGB(255, 255, 255));

CPen pen(PS_SOLID, 1, RGB(200, 200, 200));

dc.SelectObject(&pen);

// 固定 10 像素一格,超级明显!

for (int x = 50; x <= 610; x += UNIT) // 这里写死 10,不要用 UNIT

{

dc.MoveTo(x, 40);

dc.LineTo(x, 460);

}

for (int y = 40; y <= 460; y += UNIT)

{

dc.MoveTo(50, y);

dc.LineTo(610, y);

}

// --------------- 画 Game Over 文字(3秒自动消失)---------------

if (m_bShowGameOver){

CFont font;

font.CreatePointFont(240, L"黑体", &dc);

// 保存旧字体

CFont* pOldFont = dc.SelectObject(&font);

COLORREF oldTextColor = dc.SetTextColor(RGB(255, 0, 0));

int oldBkMode = dc.SetBkMode(TRANSPARENT);

dc.TextOutW(200, 470, L"Game Over!");

dc.SetBkMode(oldBkMode);

dc.SetTextColor(oldTextColor);

dc.SelectObject(pOldFont);

}

if (food.yes) {

food.x = rand() % 400 + 60;

food.y = rand() % 350 + 60;

food.x -= food.x % UNIT;

food.y -= food.y % UNIT;

food.yes = 0;

}

dc.SelectObject(GetStockObject(DC_BRUSH));

dc.SetDCBrushColor(RGB(0, 255, 0));

dc.Rectangle(food.x, food.y, food.x + UNIT, food.y + UNIT);

#if 1

// ===================== 画蛇:白头白尾 + 蓝身体 + 大紫眼睛 =====================

dc.SelectObject(GetStockObject(DC_PEN));

dc.SetDCPenColor(RGB(0, 0, 0));

// 中间身体:蓝色方块

dc.SelectObject(GetStockObject(DC_BRUSH));

//dc.SetDCBrushColor(RGB(0, 80, 255));

//dc.SetDCBrushColor(RGB(100, 180, 255)); // 淡蓝色

dc.SetDCBrushColor(RGB(173, 216, 230)); // 浅天蓝 (light blue)

for (int i = 1; i < snake.node - 1; i++)

{

dc.Rectangle(snake.x[i], snake.y[i], snake.x[i] + UNIT, snake.y[i] + UNIT);

}

// ===================== 蛇头:白色大箭头 =====================

int hx = snake.x[0];

int hy = snake.y[0];

int cx = hx + UNIT / 2;

int cy = hy + UNIT / 2;

dc.SetDCBrushColor(RGB(255, 255, 255));

POINT head[3];

switch (snake.dir)

{

case DIR_RIGHT:

head[0] = { hx, hy };

head[1] = { hx, hy + UNIT };

head[2] = { hx + UNIT + 4, cy };

break;

case DIR_LEFT:

head[0] = { hx + UNIT, hy };

head[1] = { hx + UNIT, hy + UNIT };

head[2] = { hx - 4, cy };

break;

case DIR_UP:

head[0] = { hx, hy + UNIT };

head[1] = { hx + UNIT, hy + UNIT };

head[2] = { cx, hy - 4 };

break;

case DIR_DOWN:

default:

head[0] = { hx, hy };

head[1] = { hx + UNIT, hy };

head[2] = { cx, hy + UNIT + 4 };

break;

}

dc.Polygon(head, 3);

// ========== 紫色大眼睛 ==========

dc.SetDCBrushColor(RGB(255, 0, 255));

if (snake.dir == DIR_RIGHT)

{

dc.Ellipse(cx + 0, cy - 4, cx + 5, cy + 0);

dc.Ellipse(cx + 0, cy + 1, cx + 5, cy + 5);

}

else if (snake.dir == DIR_LEFT)

{

dc.Ellipse(cx - 5, cy - 4, cx - 0, cy + 0);

dc.Ellipse(cx - 5, cy + 1, cx - 0, cy + 5);

}

else if (snake.dir == DIR_UP)

{

dc.Ellipse(cx - 4, cy - 5, cx + 0, cy - 0);

dc.Ellipse(cx + 1, cy - 5, cx + 5, cy - 0);

}

else

{

dc.Ellipse(cx - 4, cy + 0, cx + 0, cy + 5);

dc.Ellipse(cx + 1, cy + 0, cx + 5, cy + 5);

}

// ===================== 蛇尾:白色箭头 =====================

if (snake.node >= 2)

{

int tail = snake.node - 1;

int tx = snake.x[tail];

int ty = snake.y[tail];

int tcx = tx + UNIT / 2;

int tcy = ty + UNIT / 2;

int dx = snake.x[tail] - snake.x[tail - 1];

int dy = snake.y[tail] - snake.y[tail - 1];

dc.SetDCBrushColor( RGB(255, 255, 255) ); //白色尾巴

POINT tailArrow[3];

if (dx > 0){

tailArrow[0] = { tx, ty };

tailArrow[1] = { tx, ty + UNIT };

tailArrow[2] = { tx + UNIT + 4, tcy };

}else if (dx < 0){

tailArrow[0] = { tx + UNIT, ty };

tailArrow[1] = { tx + UNIT, ty + UNIT };

tailArrow[2] = { tx - 4, tcy };

}else if (dy < 0){

tailArrow[0] = { tx, ty + UNIT };

tailArrow[1] = { tx + UNIT, ty + UNIT };

tailArrow[2] = { tcx, ty - 4 };

}else{

tailArrow[0] = { tx, ty };

tailArrow[1] = { tx + UNIT, ty };

tailArrow[2] = { tcx, ty + UNIT + 4 };

}

dc.Polygon(tailArrow, 3);

}

#endif

CString str;

str.Format(L"Score: %d", score);

dc.TextOutW(55, 20, str);

CDialogEx::OnPaint();

}

}

HCURSOR CSnakeDlg::OnQueryDragIcon()

{

return static_cast<HCURSOR>(m_hIcon);

}

int g_timer_ing = 0x00;

void CSnakeDlg::OnTimer(UINT_PTR nIDEvent)

{

m_dirLocked = false; //unlock

if (g_timer_ing == 0x01) {

KillTimer(1);

return;

}

//3秒自动隐藏 Game Over

if (nIDEvent == 2)

{

KillTimer(2);

m_bShowGameOver = false;

Invalidate();

}

if (!snake.life ) {

g_timer_ing = 0x01;

KillTimer(1);

//MessageBox(L"Game Over!");

Invalidate();

g_timer_ing = 0x00;

return;

}

if (m_bPaused || m_bGameOver){

return;

}

// 1. 先算出"下一格"蛇头在哪

int nextX = snake.x[0];

int nextY = snake.y[0];

switch (snake.dir) {

case 1: nextX += UNIT; break;

case 2: nextX -= UNIT; break;

case 3: nextY -= UNIT; break;

case 4: nextY += UNIT; break;

}

// 2. 先判断:撞墙直接死,【完全不移动任何一节】

if (nextX < 50 || nextX > 600 || nextY < 40 || nextY > 450)

{

snake.life = 0;

m_bGameOver = true;

m_bShowGameOver = true; // 显示游戏结束

SetTimer(2, 3000, NULL);

//KillTimer(1);

Invalidate();

return;

}

// 只有长度 >=3 才判断撞自己

if (snake.node >= 4){

for (int i = 1; i < snake.node; i++) {

if (nextX == snake.x[i] && nextY == snake.y[i]) {

snake.life = 0;

m_bGameOver = true;

m_bShowGameOver = true;

SetTimer(2, 3000, NULL);

Invalidate();

return;

}

}

}

// ======================================================

// 走到这里,说明绝对安全,才开始移动身体

// ======================================================

// 4. 身体跟着前一节走

for (int i = snake.node - 1; i > 0; i--) {

snake.x[i] = snake.x[i - 1];

snake.y[i] = snake.y[i - 1];

}

// 5. 蛇头走到新位置

snake.x[0] = nextX;

snake.y[0] = nextY;

// 6. 吃食物

if (abs(snake.x[0] - food.x) < UNIT && abs(snake.y[0] - food.y) < UNIT) {

// 先把最后一节身体复制一遍 ← 你缺的就是这句!

snake.x[snake.node] = snake.x[snake.node - 1];

snake.y[snake.node] = snake.y[snake.node - 1];

// 然后再变长

snake.node++;

score += 10;

food.yes = 1;

}

Invalidate(FALSE);

CDialogEx::OnTimer(nIDEvent);

}

void CSnakeDlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)

{

if (m_dirLocked){ // 锁着就不理你

return;

}

// 游戏结束时只允许 S 重新开始

if (m_bGameOver)

{

if (nChar == 'S' || nChar == 's')

{

// 重新初始化蛇、分数、食物等

InitSnake();

score = 0;

m_bGameOver = false;

m_bPaused = false;

SetTimer(1, SPEED, NULL);

Invalidate();

}

CDialogEx::OnKeyDown(nChar, nRepCnt, nFlags);

return;

}

// P 暂停

if (nChar == 'P' || nChar == 'p')

{

m_bPaused = !m_bPaused;

if (m_bPaused)

KillTimer(1);

else

SetTimer(1, SPEED, NULL);

Invalidate();

}

// S 继续/开始

if (nChar == 'S' || nChar == 's')

{

if (m_bPaused)

{

m_bPaused = false;

SetTimer(1, SPEED, NULL);

Invalidate();

}

}

// 暂停时不响应方向键

if (m_bPaused || m_bGameOver)

return;

switch (nChar) {

case VK_UP:

if (snake.dir != DIR_DOWN){

snake.dir = DIR_UP;

m_dirLocked = true;

}

break;

case VK_DOWN:

if (snake.dir != DIR_UP){

snake.dir = DIR_DOWN;

m_dirLocked = true;

}

break;

case VK_LEFT:

if (snake.dir != DIR_RIGHT){

snake.dir = DIR_LEFT;

m_dirLocked = true;

}

break;

case VK_RIGHT:

if (snake.dir != DIR_LEFT){

snake.dir = DIR_RIGHT;

m_dirLocked = true;

}

break;

case VK_ESCAPE:

PostQuitMessage(0);

break;

}

CDialogEx::OnKeyDown(nChar, nRepCnt, nFlags);

}

相关推荐
CDN3602 小时前
SDK 游戏盾接入闪退 / 初始化失败?依赖冲突与兼容修复
运维·游戏·网络安全
kyle~2 小时前
ROS2 --- WaitSet(等待集) 等待实体就绪,管理执行回调函数
大数据·c++·机器人·ros2
量子炒饭大师3 小时前
【C++进阶】Cyber骇客的赛博血统上传——【面向对象之 继承 】一文带你搞懂面向对象编程的三要素之————继承
c++·dubbo·继承·面向对象编程
小贺儿开发3 小时前
Unity3D 拼图互动游戏
游戏·unity·人机交互·2d·拼图·互动
Tanecious.3 小时前
蓝桥杯备赛:Day2-B3612 求区间和
c++·蓝桥杯
C+++Python3 小时前
Linux/C++多进程
linux·运维·c++
stolentime3 小时前
通信题:洛谷P15942 [JOI Final 2026] 赌场 / Casino题解
c++·算法·洛谷·joi·通信题
XZHOUMIN3 小时前
【生成pdf格式的报告】
c++·pdf·mfc
elseif1233 小时前
浅谈 C++ 学习
开发语言·c++·学习