#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);
}